안녕하세요 아즈라엘 입니다. 


Google Play In App Billing을 사용하여 

구매를 요청하고 아래와 같은 문구를 만난다면...


screenshop.png



Choosing a Purchase Type 을 반듯이 확인하세요


Defualt 설정이 managed per user account 으로 되어 있어서 선택을 하지 않을 경우 재구매가 안되는 

비소멸성 구매가 됩니다.



아울러 In App Billing 샘플 소스인 Dungeons 를 설치해서 돌려 보시면

조금 복잡하게 되어 있는데요..


Dungeons 프로젝트에서 Dungeons.java 만 제외하고 나머진 다 

본인의 프로젝트에 임포트시켜야 하고


Dungeons.java 에서 아래만 구현하면 됩니다. 


public class ActivityShop extends Activity{

private BillingService mBillingService;
private TestPurchaseObserver  purchaseObserver;
private boolean billingEnable = false;
        private Handler handler = new Handler();

private class TestPurchaseObserver extends PurchaseObserver {
public TestPurchaseObserver(Handler handler) {
super(PororoActivityShop.this, handler);
}

@Override
public void onBillingSupported(boolean supported, String type) {
}

@Override
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId, int quantity, long purchaseTime,
String developerPayload) {

}

@Override
public void onRestoreTransactionsResponse(RestoreTransactions request, ResponseCode responseCode) {

}

@Override
public void onRequestPurchaseResponse(RequestPurchase request, ResponseCode responseCode) {
// TODO 승인요청 확인
if (responseCode == ResponseCode.RESULT_OK) {
} else {
}
}
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

pororoPurchaseObserver = new TestPurchaseObserver(handler);
mBillingService = new BillingService();
mBillingService.setContext(this);

// Check if billing is supported.
ResponseHandler.register(pororoPurchaseObserver);
billingEnable = mBillingService.checkBillingSupported();

}

@Override
public void onBackPressed() {
// nothing..
}

@Override
protected void onResume() {
super.onResume();
}

@Override
protected void onPause() {
super.onPause();
}

@Override
protected void onStop() {
super.onStop();
ResponseHandler.unregister(purchaseObserver);
}

@Override
protected void onStart() {
super.onStart();
ResponseHandler.register(purchaseObserver);
}

@Override
protected void onDestroy() {
super.onDestroy();
mBillingService.unbind();
}


private void requestPurchase() {
mBillingService.requestPurchase("web에서 등록한 상품ID", Consts.ITEM_TYPE_INAPP, "Optional data");
}

}











Security.java 에서 


이렇게 키값을 소스레벨에서 바인딩 하시지 마시고..

PublicKey base64EncodedPublicKey = "ABCDEFG";



런타임에서 데이터를 가져 올 수 있도록 숨기세요..

PublicKey base64EncodedPublicKey = YourObjct.getData();


물론 Base64 incoding에서 사용되는 SALT 값도 런타임에서 바인딩되도록

묶어야 보안에 좋습니다. 



BillingService.java 에서는 


handleCommand 메서드를  NullPointerException 처리 해주셔야 합니다. 

그 이유는 서비스가 언바인딩 되지 않고 비정상 종료 되면 이 메서드가 호출이 되고

NullPointerException 에러가 발생되어 죽습니다. 

따라서 아래와 같이 해 두시면 비정상 종료가 되더라도 .. 

추가적인 문제가 발생되지 않더군요..



public void handleCommand(Intent intent, int startId) {

try {

String action = intent.getAction();

if (Consts.DEBUG) {

Log.i(TAG, "handleCommand() action: " + action);

}

if (Consts.ACTION_CONFIRM_NOTIFICATION.equals(action)) {

String[] notifyIds = intent.getStringArrayExtra(Consts.NOTIFICATION_ID);

confirmNotifications(startId, notifyIds);

} else if (Consts.ACTION_GET_PURCHASE_INFORMATION.equals(action)) {

String notifyId = intent.getStringExtra(Consts.NOTIFICATION_ID);

getPurchaseInformation(startId, new String[] { notifyId });

} else if (Consts.ACTION_PURCHASE_STATE_CHANGED.equals(action)) {

String signedData = intent.getStringExtra(Consts.INAPP_SIGNED_DATA);

String signature = intent.getStringExtra(Consts.INAPP_SIGNATURE);

purchaseStateChanged(startId, signedData, signature);

} else if (Consts.ACTION_RESPONSE_CODE.equals(action)) {

long requestId = intent.getLongExtra(Consts.INAPP_REQUEST_ID, -1);

int responseCodeIndex = intent.getIntExtra(Consts.INAPP_RESPONSE_CODE, ResponseCode.RESULT_ERROR.ordinal());

ResponseCode responseCode = ResponseCode.valueOf(responseCodeIndex);

checkResponseCode(requestId, responseCode);

}

} catch (NullPointerException e) {

this.unbind();

}


}