【问题标题】:android: Inapp billing: Error response: 7:Item Already Ownedandroid:Inapp计费:错误响应:7:项目已拥有
【发布时间】:2013-10-12 07:35:14
【问题描述】:

我正在学习为我的应用实施应用内结算,例如,人们可以在按下捐赠按钮时捐赠 $。

允许用户多次捐赠,即购买为消耗品。

以下代码来自 TrivalDrive 示例和网络上的一些教程:

代码:

IabHelper mHelper;
static final String ITEM_SKU = "android.test.purchased"; 

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_in_app_billing);

    buy10Button = (Button) findViewById(R.id.buy10Button); 
    buy15Button = (Button) findViewById(R.id.buy15Button); 
    buy20Button = (Button) findViewById(R.id.buy20Button);      

    String base64EncodedPublicKey = "keykeykey";

    mHelper = new IabHelper(this, base64EncodedPublicKey);


    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() 
    {
          public void onIabSetupFinished(IabResult result) 
          {
            if (!result.isSuccess()) 
            {
               Log.d(TAG, "In-app Billing setup failed: " + result);
               return;
            } 
            if (mHelper == null) 
            {
                return;
            }          
            Log.d(TAG, "In-app Billing is set up OK");
          }
    });     
}

public void buy10Click(View view) 
{
    mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001,  mPurchaseFinishedListener, "");
}

public void buy15Click(View view) 
{

}

public void buy20Click(View view) 
{

}   

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
{
    if (mHelper == null) return;  
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) 
    {     
        super.onActivityResult(requestCode, resultCode, data);
    }
}

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() 
{
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) 
    {
        if (mHelper == null) return;
        if (result.isFailure()) 
        {
           // Handle error
               return;
        }      
        else if ((purchase.getSku().equals(ITEM_SKU)))   
        {
           consumeItem();
        }              
    }
};

public void consumeItem() 
{
    mHelper.queryInventoryAsync(mReceivedInventoryListener);
}

IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() 
{
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) 
    {
        if (mHelper == null) return;
        if (result.isFailure()) 
        {
            // Handle failure
        } 
        else 
        {
            mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU), mConsumeFinishedListener);
        }
    }
};

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() 
{
    public void onConsumeFinished(Purchase purchase, IabResult result) 
    {
        if (mHelper == null) return;
        if (result.isSuccess()) 
        {
            Toast.makeText(InAppBillingActivity.this, "Thank you for your donation!!", Toast.LENGTH_LONG).show();   
        } 
        else 
        {
            // handle error
        }
    }
};

问题:

但我不断收到E/IabHelper(13392): In-app billing error: Unable to buy item, Error response: 7:Item Already Owned 错误,而且 Google Play 的付款对话框没有弹出。

我研究了很多类似的情况,有的建议等几分钟,然后购买会自动重置,但我等了快一个小时,但还是很糟糕。

我还发现有人建议更改 IabResult public boolean isSuccess() { return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK; } 以将 BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED 也返回为 isSuccess = true,但我不知道如何修改此类...

如何解决问题?谢谢!!

【问题讨论】:

  • 请在下面查看我的答案。
  • 确保在启动时获取库存以检查是否拥有任何物品,如果有则消耗它。
  • 一个有效的答案是enter link description here

标签: android in-app-purchase in-app-billing


【解决方案1】:

您购买了“android.test.purchased”但没有使用它。但是,如果忘记立即食用,再食用也不是一件容易的事。我们可以等14天。虚假购买将被自动清除。但这是不可接受的。

我花了很多时间寻找解决方案:

添加此行以获取调试信息。

_iabHelper.enableDebugLogging(true, "TAG");

运行应用程序。在 LogCat 中,你会看到一个 json 字符串,如

{"packageName":"com.example","orderId":"transactionId.android.test.purchased","productId":"android.test.purchased","developerPayload":"123","purchaseTime":0,"purchaseState":0,"purchaseToken":"inapp:com.example:android.test.purchased"}

手动使用(将 THAT_JSON_STRING 替换为您的 json 字符串)

    Purchase purchase;
    try {
        purchase = new Purchase("inapp", THAT_JSON_STRING, "");
        _iabHelper.consumeAsync(purchase, new OnConsumeFinishedListener() {

            @Override
            public void onConsumeFinished(Purchase purchase, IabResult result) {
                Log.d("TAG", "Result: " + result);
            }
        });
    } catch (JSONException e) {
        e.printStackTrace();
    }

_iabHelper 是 mHelper。

【讨论】:

  • 非常感谢您的回答!我会试一试的!
  • 我启用了调试,但在日志中看不到 JSON 字符串。你能告诉我从哪里或如何获取值的想法,以便我可以创建自己的字符串吗?
  • 要获取 JSON,您必须查询库存。
  • 真的可以工作,但它可以在从 play store 生成的 live sku 上工作?
  • 我在查询库存时遇到问题,它没有返回任何购买详细信息,但是当它尝试购买该商品时,它显示该商品已购买。我重新启动后问题得到了解决设备它开始在查询库存中返回购买的物品。似乎对某些人有帮助:)。
【解决方案2】:

在此处查看我的以下代码:

我不明白您的代码为什么在购买完成监听器中使用查询库存。当您获得与您请求的 sku 相同的 sku 时,应调用 ConsumeAsync() 方法。

// Callback for when a purchase is finished
    IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            Log.d(TAG, "Purchase finished: " + result + ", purchase: "
                    + purchase);
            if (result.isFailure()) {
                complain("Error purchasing: " + result);
                return;
            }
            if (!verifyDeveloperPayload(purchase)) {
                complain("Error purchasing. Authenticity verification failed.");
                return;
            }

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

            if (purchase.getSku().equals(SKU_GAS)) {

                 // remove query inventory method from here and put consumeAsync() directly
                mHelper.consumeAsync(purchase, mConsumeFinishedListener);

            }

        }
    };

startSetup方法

//你忘记在startSetup方法中调用查询库存方法了。

 mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
                public void onIabSetupFinished(IabResult result) {
                    Log.d(TAG, "Setup finished.");

                    if (!result.isSuccess()) {
                        // Oh noes, there was a problem.
                        complain("Problem setting up in-app billing: " + result);
                        return;
                    }

                    // Hooray, IAB is fully set up. Now, let's get an inventory of
                    // stuff we own.
                    Log.d(TAG, "Setup successful. Querying inventory.");
                    mHelper.queryInventoryAsync(mGotInventoryListener);
                }
            });

QueryInventoryFinishedListener

并检查条件购买是否与您要求的相同 不等于 null 并且开发人员有效负载在您的查询中也相同 库存完成监听器。

if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)){
    //code
}
// Listener that's called when we finish querying the items and
        // subscriptions we own
        IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
            public void onQueryInventoryFinished(IabResult result,
                    Inventory inventory) {
                Log.d(TAG, "Query inventory finished.");
                if (result.isFailure()) {
                    complain("Failed to query inventory: " + result);
                    return;
                }

                Log.d(TAG, "Query inventory was successful.");

                /*
                 * Check for items we own. Notice that for each purchase, we check
                 * the developer payload to see if it's correct! See
                 * verifyDeveloperPayload().
                 */

                // // Check for gas delivery -- if we own gas, we should fill up the
                // tank immediately
                Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
                if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {
                    Log.d(TAG, "We have gas. Consuming it.");
                    mHelper.consumeAsync(inventory.getPurchase(SKU_GAS),
                            mConsumeFinishedListener);
                    return;
                }
            }
        };

解释为什么会发生:

每当您购买消耗品时,Google Play 商店都不会在 Google Play 控制台中管理其产品购买详情和其他内容。这就是为什么我们必须调用 consumeAsync() 方法。当我们购买物品时,Google play store 会记录物品已购买一次,并允许您进行第二次购买。

希望它能解决你的问题。

【讨论】:

  • 你是对的!我已经从头到尾重新排列了代码,它现在可以通过引用 Google TrivalDrive 示例来工作,现在可以正常工作了!现在将进一步修改,以便尝试实施更多可供选择的产品...谢谢!
  • verifyDeveloperPayload(gasPurchase) 这是女巫类方法吗?我找不到这个。
  • @ZalaJanaksinh 这是从谷歌文档下载的 MainActivity.java 类的示例代码。
  • 添加“mHelper.queryInventoryAsync(mGotInventoryListener);”后它在启动时崩溃
  • @zszen 在 stackoverflow 上创建新问题,并请包含您的崩溃日志以结束该问题,我认为这可能是由于其他一些原因。您可以在此处提供问题链接。
【解决方案3】:

您可以使用 Google Play“财务报告”->“访问您的商家帐户了解更多详情”->“订单”查看和取消任何订单以“消费”。 然后你需要重启你的设备。 =)

【讨论】:

  • 这个应该更高
  • 重启设备对我不起作用,但清除 Google Play 应用上的缓存确实有效 :)
【解决方案4】:

我只需重新启动设备就可以“消费购买”。

【讨论】:

    【解决方案5】:

    我正在使用: implementation 'com.android.billingclient:billing:2.0.0' 并且在购买过程中遇到了同样的错误。

    • 重点是:在启动购买之前,我们应该消耗待处理的购买。
    • 请看下面的代码sn-p:

          List<String> skuList = new ArrayList();
          skuList.add(THE_IAP_ID);
          BillingClient.Builder billingClientBuilder = BillingClient.newBuilder(context).setListener(new PurchasesUpdatedListener() {
              @Override
              public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
                   // here we have to check the result ...
      
              });
      
              billingClientBuilder.enablePendingPurchases();
              billingClient = billingClientBuilder.build();
              billingClient.startConnection(new BillingClientStateListener() {
              @Override
              public void onBillingSetupFinished(BillingResult billingResult) {
                  if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
                      // The BillingClient is ready - query purchases.
      
                      Purchase.PurchasesResult pr = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
                      List<Purchase> pList = pr.getPurchasesList();
                      for (Purchase iitem : pList) {
                          ConsumeParams consumeParams = ConsumeParams.newBuilder()
                                  .setPurchaseToken(iitem.getPurchaseToken())
                                  .build();
                          billingClient.consumeAsync(consumeParams, consumeResponseListener);
                      }
      
                      // process the purchase
                  } else {
                      // cancelled or s.e. 
                      ...
                  }
              }
      

    最好的问候,玩得开心:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-07
      • 2013-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-16
      相关资源
      最近更新 更多