Google In-App Billing V3 Unable to buy item, Error response: 7:Item Already Owned

Starting async operation: launchPurchaseFlow
Constructing buy intent for testinappitem1, item type: inapp
In-app billing error: Unable to buy item, Error response: 7:Item Already Owned
Purchase finished: IabResult: Unable to buy item (response: 7:Item Already Owned), purchase: null
InAppBuyItem_U testinappitem1

 구글 인앱빌링 V3로 비관리 아이템 같은 것을 중복 구매시 위와 같은 오류가 발생합니다. 만약 onIabPurchaseFinished 부분에서 purchase != null 인지 체크하지 않고 샘플과 같게 개발하셨다면 NullPointerException 에러가 발생하게되니 주의하시기 바랍니다.

    // Callback for when a purchase is finished
    IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            ...

            Log.d(TAG, "Purchase successful.");

            if (purchase.getSku().equals(SKU_GAS)) {
                // bought 1/4 tank of gas. So consume it.
                Log.d(TAG, "Purchase is gas. Starting gas consumption.");
                mHelper.consumeAsync(purchase, mConsumeFinishedListener);
            }
            ...
        }
    };

 해결방법은 소모성 아이템을 구매하고 바로 소비처리를 해야합니다. 위 소스는 trivialdrivesample의 구매 완료 처리 부분인데 바로 comsumeAsync를 통해서 소비를 하고 있죠.


public class Inventory {
    Map<String,SkuDetails> mSkuMap = new HashMap<String,SkuDetails>();
    Map<String,Purchase> mPurchaseMap = new HashMap<String,Purchase>();

 그리고 Inventory 부분을 봐도 소모성 아이템이라도 오직 한개만 가지고 있을 수 있도록 자료구조가 처리되고 있더군요. 인앱빌링 V2만 해도 이런 이슈는 없었는데 구글에서 소모성 아이템을 구매 후 바로 소비하도록 강제하는 것 같습니다.


// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
        Log.d(LOG_TAG, "Purchase finished: " + result + ", purchase: " + purchase);
       
        if (purchase != null && !verifyDeveloperPayload(purchase)) {
            Log.d(LOG_TAG, "Error purchasing. Authenticity verification failed.");
        }
       
        JSONObject iabJsonObj = new JSONObject();
       
        try {
        iabJsonObj.put("Result", result.getResponse());
        if(purchase != null) {
        iabJsonObj.put("OrderId", purchase.getOrderId());
iabJsonObj.put("ItemType", purchase.getItemType());
iabJsonObj.put("OriginalJson", purchase.getOriginalJson());
iabJsonObj.put("Signature", purchase.getSignature());
        }
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
                   
        UnityPlayer.UnitySendMessage("GoogleInAppManager", "InAppBuyItemResult_J", iabJsonObj.toString());                                                                                                      
    }
};

 처음에는 이같은 사실을 모르고 그냥 V2 개발할 때 처럼 위 소스와 같이 유니티3D 측에 정보를 넘겨서 가지고 있다가 나중에 마음데로 소비를 처리할 수 있게 했었는데 이 작업이 소용없게 되었네요.

이 블로그의 인기 게시물

CMake Windows에 설치하기

'xxx.exe' 프로그램을 시작할 수 없습니다. 지정된 파일을 찾을 수 없습니다.

Unity3D 안드로이드 Keystore 생성하기