1

Ever since in billing library 2 and 3, there is an extra acknowledgement step we need to perform, after purchasing is success.


When purchasing is success, the following call-back will be triggered.

public interface PurchasesUpdatedListener {
    void onPurchasesUpdated(BillingResult billingResult, java.util.List<com.android.billingclient.api.Purchase> list);
}

When acknowledgement is success, the following call-back will be triggered.

public interface AcknowledgePurchaseResponseListener {
    void onAcknowledgePurchaseResponse(BillingResult billingResult);
}

In success case, should we unlock in-app purchase item, when purchasing is success, or acknowledgement is sucess?

Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875

2 Answers2

2

InApp Products cannot be bought after a purchase was made. We need to consume it after a successful purchase, so that we can purchase again and it will become available for the next time we make purchase of the same product that was bought before.

You must acknowledge all the purchases that are non consumable i.e subscriptions must be acknowledged. Here is a reference code or you

            /**
             * If you do not acknowledge a purchase, the Google Play Store will provide a refund to the
             * users within a few days of the transaction. Therefore you have to implement
             * [BillingClient.acknowledgePurchaseAsync] inside your app.
             *
             * @param purchase list of Purchase Details returned from the queries.
             */
            private fun acknowledgeNonConsumablePurchasesAsync(purchase: Purchase) {
                val acknowledgePurchaseRunnable = Runnable {
                    val params = AcknowledgePurchaseParams.newBuilder()
                        .setPurchaseToken(purchase.purchaseToken)
                        .build()
                    myBillingClient.acknowledgePurchase(
                        params
                    ) { billingResult: BillingResult ->
                        if (billingResult.responseCode == BillingResponseCode.OK) {
                            LogUtils.d(TAG, "onAcknowledgePurchaseResponse: " + billingResult.responseCode)
                        } else {
                            LogUtils.d(TAG, ("onAcknowledgePurchaseResponse: " + billingResult.debugMessage))
                        }
                    }
                }
            }

For Consumables this should do

 /**
     * Consumes InApp Product Purchase after successful purchase of InApp Product Purchase. InApp
     * Products cannot be bought after a purchase was made. We need to consume it after a
     * successful purchase, so that we can purchase again and it will become available for the
     * next time we make purchase of the same product that was bought before.
     *
     * @param purchase the purchase result contains Purchase Details.
     */
val onConsumeListener =
            ConsumeResponseListener { billingResult: BillingResult, purchaseToken: String ->
                // If billing service was disconnected, we try to reconnect 1 time
                // (feel free to introduce your retry policy here).
                if (billingResult.responseCode == BillingResponseCode.OK) {
                    LogUtils.d(TAG, "onConsumeResponse, Purchase Token: $purchaseToken")
                } else {
                    LogUtils.d(TAG, "onConsumeResponse: " + billingResult.debugMessage)
                }
            }
        // Creating a runnable from the request to use it inside our connection retry policy below
        val consumeRequest = Runnable {

            // Consume the purchase async
            val consumeParams = ConsumeParams.newBuilder()
                .setPurchaseToken(purchase.purchaseToken)
                .build()
            myBillingClient!!.consumeAsync(consumeParams, onConsumeListener)
        }
AkkixDev
  • 450
  • 3
  • 12
1

Please review the section "Verify purchases before granting entitlements".

https://developer.android.com/google/play/billing/security#verify

You should unlock content only after verifying via your backend server that the purchase was legitimate.

You implement onPurchasesUpdated, to get notifications for purchases updates initiated both within or outside your app.

If you don't acknowledge a purchase, the purchase will be automatically refunded. You implement onAcknowledgePurchaseResponse, to receive a notification that the acknowledgement of the purchase operation is complete.

But to know if it is a legitimate purchase, you must verify you the purchase is legitimate before granting entitlements.

A special case of sensitive data and logic that should be handled in the backend is purchase verification. After a user has made a purchase, you should do the following:

  • Send the corresponding purchaseToken to your backend. This means that you should maintain a record of all purchaseToken values for all purchases.
  • Verify that the purchaseToken value for the current purchase does not match any previous purchaseToken values. purchaseToken is globally unique, so you can safely use this value as a primary key in your database.
  • Use the Purchases.products:get or Purchases.subscriptions:get endpoints in the Google Play Developer API to verify with Google that the purchase is legitimate.
  • If the purchase is legitimate and has not been used in the past, you can then safely grant entitlement to the in-app item or subscription.
  • For subscriptions, when linkedPurchaseToken is set in Purchases.subscriptions:get, you should also remove the linkedPurchaseToken from your database and revoke the entitlement that is granted to the linkedPurchaseToken to ensure that multiple users are not entitled for the same purchase.

hence you should unlock content only after all three are completed.

  • onPurchaseUpdated, for initially knowing that a purchase is complete.
  • onAcknowledgePurchaseResponse, so you know the acknowledgement is done, and the purchase will not be automatically refunded
  • you need to verify via your backend server that the purchase was legitimate.

When you have done all three, it is safe to unlock your purchase. If you do not do all three, there is a risk of unlocking content that for a purchase that has either been refunded or is illegitimate.

Rahul Iyer
  • 19,924
  • 21
  • 96
  • 190
  • Very good answer. But if you acknowledge the purchase already on your backend there is no need for `onAcknowledgePurchaseResponse` in the Android client. – Houman Apr 02 '21 at 13:58