检测已退款的托管应用内购买 android IAP 2.0.3

问题描述 投票:0回答:3

我无法弄清楚如何使用

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
状态。

我发现像应用内计费 v3 - 不检测退款这样的帖子表明 Play 服务正在缓存购买,但我不相信这会导致此问题,因为如果我在应用程序运行之间修改代码以确认/消费购买,这些状态更改会立即反映在购买的 JSON 中。

我该如何检测已退款的托管产品?

android in-app-purchase
3个回答
8
投票

我的应用程序中有一项购买(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的Hacky方法:

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

不幸的是,这个问题仍然存在!

更多详情这里


2
投票

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

 binding.btnRestore.setOnClickListener(v->{


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

            }
});

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

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

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

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

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

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

以下代码演示了如何重写 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);
            }

        }

    }

0
投票

以下是我检测退款购买的方法。 注意:这不是官方解决方案,但它有效。

从json响应中获取购买状态:

val jsonObject = JSONObject(purchase.originalJson)
val purchaseStateFromJson = jsonObject.get("purchaseState") as Int

从购买对象获取购买状态:

val purchaseState = purchase.purchaseState

检查订单是否退款:

if (purchaseState == PurchaseState.PURCHASED
             && purchaseStateFromJson == PurchaseState.UNSPECIFIED_STATE) {
    // this is a refunded order
    // consume the purchase, so that it can be purchased again
    val consumeParams = ConsumeParams.newBuilder()
                                     .setPurchaseToken(purchase.purchaseToken).build()
    billingClient.consumeAsync(consumeParams) { billingResult, _ ->
        if (billingResult.responseCode == BillingResponseCode.OK) {
            Log.d(TAG, "Product consumed")
        }
    }
}

为了使用户能够再次进行购买,我们需要消费该购买。

© www.soinside.com 2019 - 2024. All rights reserved.