【问题标题】:Detect refunded managed in-app purchase android IAP 2.0.3检测退款的托管应用内购买android IAP 2.0.3
【发布时间】:2019-09-05 09:33:58
【问题描述】:

我无法确定如何使用com.android.billingclient:billing:2.0.3 检测 Android 中托管(不可消费)应用内产品何时退款。这个问题似乎很深,但也许我让它变得比应该的更复杂。

首先,我进行了一次测试购买,已确认并已退款:

查看我的应用程序的日志,我看到以下内容:

D/BillingManager: Got a verified purchase: Purchase. Json: {"orderId":"GPA.3362-7185-5389-78416","packageName":"com.glan.input","productId":"pro","purchaseTime":1567672759460,"purchaseState":0,"purchaseToken":"pbkpcaadklleoecegjfjdpbl.AO-J1OwsR6WVaVZCCYOU6JyYN1r0qJsrwitIPZfhc3jX4yketRUwNzKqwMgYx0TgZ2GebEGbXDL0RlMyogwtSKSPsaHCJ4RA4MPlIGay-aM1-QhmnqwjXjQ","acknowledged":true}
I/BillingManager: purchase pbkpcaadklleoecegjfjdpbl.AO-J1OwsR6WVaVZCCYOU6JyYN1r0qJsrwitIPZfhc3jX4yketRUwNzKqwMgYx0TgZ2GebEGbXDL0RlMyogwtSKSPsaHCJ4RA4MPlIGay-aM1-QhmnqwjXjQ is in 1 state

这里发生了一些有趣的事情:

  1. 我们可以看到订单 ID 在图像中的内容与检测到的购买之间匹配
  2. 第一条日志行使用Log.d(TAG, "Got a verified purchase: " + purchase); 打印购买,它正在打印代表purchase 的底层JSON。
  3. 注意"purchaseState":0
  4. 第二个日志行以Log.i(TAG, "purchase " + purchase.getPurchaseToken() + " is in " + purchase.getPurchaseState() + " state"); 发出。
  5. 注意这里purchase.getPurchaseState() 的结果是1

如果我在 Android Studio 中查看 getPurchaseState 的实现,我会看到以下内容:

  public @PurchaseState int getPurchaseState() {
    switch (mParsedJson.optInt("purchaseState", PurchaseState.PURCHASED)) {
      case 4:
        return PurchaseState.PENDING;
      default:
        return PurchaseState.PURCHASED;
    }
  }

在文件的前面,PurchaseState 接口被声明为:

  @Retention(SOURCE)
  public @interface PurchaseState {
    // Purchase with unknown state.
    int UNSPECIFIED_STATE = 0;
    // Purchase is completed.
    int PURCHASED = 1;
    // Purchase is waiting for payment completion.
    int PENDING = 2;
  }

似乎getPurchaseState 永远不会返回PurchaseState.UNSPECIFIED_STATE 并且只返回PENDING,JSON 的值为4。我已确认,当使用需要一段时间才能批准的付款方式执行购买时,会正确返回 PENDING 状态。

我发现像 In-App Billing v3 - Don't detect refund 这样的帖子表明 Play 服务正在缓存购买,但我不相信这是导致此问题的原因,因为如果我在我的应用程序运行之间修改我的代码以确认/消费购买,这些获得状态更改会立即反映在购买的 JSON 中。

我应该如何检测退款的被管理产品?

【问题讨论】:

  • 这个问题你解决了吗?好像还是一样..
  • 这是一个严重的错误,现在我的用户只是在退还他们的购买并且仍然获得购买状态!

标签: android in-app-purchase


【解决方案1】:

我的应用程序中有一次购买 (SkuType.INAPP)。我进行了测试购买,然后退款。

问题:

purchase.getOriginalJson()  // contains "purchaseState":0
purchase.getPurchaseState() // returns 1

com.android.billingclient.api.Purchase 内部:

public int getPurchaseState() {
    switch(this.zzc.optInt("purchaseState", 1)) {
    case 4:
        return 2;
    default:
        return 1;
    }
}
//...
public @interface PurchaseState {
    int UNSPECIFIED_STATE = 0;
    int PURCHASED = 1;
    int PENDING = 2;
}

从原始 json 检查 purchaseState 的黑客方法:

purchase.getOriginalJson().contains(String.format("\"purchaseState\":%s", Purchase.PurchaseState.PURCHASED))

很遗憾,这个问题依然存在!

更多详情here.

【讨论】:

  • 好吧,这行得通,但是当我在退款后再次尝试购买 INAPP 时,它会抛出我 Unable to buy item, Error response code: 7 该代码是用于已拥有的项目。
  • @MakinTosH 这是真的。这是一个今天仍然存在的已知错误。看看这里有多少 cmets:issuetracker.google.com/issues/73982566
【解决方案2】:

您可以检查是否仍然购买退出以下

 binding.btnRestore.setOnClickListener(v->{


            Purchase.PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
            for ( Purchase purchase : purchasesResult.getPurchasesList()){
                handlePurchase(purchase);

            }
});

Google Play 会返回由登录到设备的用户帐户进行的购买。如果请求成功,Play 计费库将查询结果存储在购买对象列表中。

注意:此列表中仅显示有效订阅。只要应用内产品在此列表中,用户就应该有权访问它。有关详细信息,请参阅添加特定于订阅的功能的处理 SUBSCRPTION_ON_HOLD 部分。 要检索列表,请在 PurchasesResult 上调用 getPurchasesList()。然后,您可以在 Purchase 对象上调用各种方法来查看有关项目的相关信息,例如其购买状态或时间。要查看可用的产品详细信息类型,请参阅 Purchase 类中的方法列表。

您应该在代码中至少调用两次 queryPurchases():

每次启动您的应用时调用 queryPurchases(),这样您就可以恢复用户在应用上次停止后所做的任何购买。 在您的 onResume() 方法中调用 queryPurchases(),因为用户可以在您的应用处于后台时进行购买(例如,在 Google Play 商店应用中兑换促销代码)。 在启动和恢复时调用 queryPurchases() 可确保您的应用程序发现用户在应用程序未运行时可能进行的所有购买和兑换。此外,如果用户在应用运行时进行了购买,而您的应用因任何原因错过了购买,您的应用仍然会在下次活动恢复并调用 queryPurchases() 时发现购买。

查询最近的购买 queryPurchases() 方法使用 Google Play 商店应用程序的缓存,而无需发起网络请求。如果您需要查看每个产品 ID 的用户最近一次购买,您可以使用 queryPurchaseHistoryAsync(),传递购买类型和 PurchaseHistoryResponseListener 来处理查询结果。

queryPurchaseHistoryAsync() 返回一个 PurchaseHistory 对象,其中包含用户为每个产品 ID 进行的最近购买的信息,即使该购买已过期、取消或已消费。尽可能使用 queryPurchase(),因为它使用本地缓存,而不是 queryPurchaseHistoryAsync()。如果使用 queryPurchaseHistoryAsync(),您还可以将其与 Refresh 按钮结合使用,允许用户更新他们的购买列表。

以下代码演示了如何覆盖 onPurchaseHistoryResponse() 方法:

  private void handlePurchase(Purchase purchase) {
        if(purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {

            if (purchase.getSku().equals(skuPro)) {
                EntityPRO entityPRO = RoomDB.getDatabase(context).proDAO().getLastItem();
                entityPRO.isBought = true;
                RoomDB.getDatabase(context).proDAO().updateSpecificSLI(entityPRO);
                Toast.makeText(context, context.getString(R.string.pro_succesfully_bought), Toast.LENGTH_LONG).show();

            }

            if (!purchase.isAcknowledged()) {
                AcknowledgePurchaseParams acknowledgePurchaseParams =
                        AcknowledgePurchaseParams.newBuilder()
                                .setPurchaseToken(purchase.getPurchaseToken())
                                .build();
                mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
            }

        }

    }

【讨论】:

  • queryPurchases() 的注释。我想“只有活跃的订阅才会出现在这个列表中。”暂时不正确。我在原始 json 中看到取消订阅,带有“purchaseState”:0。看起来像这里所说的 UNSPECIFIED_STATE developer.android.com/reference/com/android/billingclient/api/…。此订阅不再存在于 Google Play 应用上的订阅中,并且我收到了已取消的电子邮件。我也尝试过同步数据并等待,但没有任何改变。
猜你喜欢
  • 1970-01-01
  • 2020-05-31
  • 2013-03-10
  • 2012-12-29
  • 2012-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多