Adrian Ivasku
Adrian Ivasku

Reputation: 1118

Google Play Billing Library 3.0+ - Restore purchase

With Google Play Billing Library v3.0+ we have a new purchase flow and everything is perfectly explained here: Google Play Billing

On older versions of the library we would restore something like this:

bp = new BillingProcessor(this, MERCHANT_ID, new BillingProcessor.IBillingHandler() {
        @Override
        public void onProductPurchased(@NonNull String productId, @Nullable TransactionDetails details) {
            String orderId = details.purchaseInfo.purchaseData.productId;
  // we then compare the orderID with the SKU and see if the user purchased the item,
  // however in the new version of the library there is nothing about restore

However, there is nothing in the documentation about restoring purchases ?

Eg, we have a use case you have a valid subscription and one IAP product that you purchased. You delete the app and reinstall it. How do you restore the subscription and that IAP product ?

Upvotes: 1

Views: 3905

Answers (3)

Shiv Shambhu
Shiv Shambhu

Reputation: 16

Use Google InApp Billing Library Available on Github.

A simple implementation of the Android In-App Billing API.

dependencies {
    implementation 'com.github.moisoni97:google-inapp-billing:1.0.5'
}

◆ Create an instance of BillingConnector class. Constructor will take 2 parameters:

● Context
● License key from *Play Console*
billingConnector = new BillingConnector(this, "license_key")
                .setConsumableIds(consumableIds)
                .setNonConsumableIds(nonConsumableIds)
                .setSubscriptionIds(subscriptionIds)
                .autoAcknowledge()
                .autoConsume()
                .enableLogging()
                .connect();

◆ Implement the listener to handle event results and errors:

billingConnector.setBillingEventListener(new BillingEventListener() {
            @Override
            public void onProductsFetched(@NonNull List<SkuInfo> skuDetails) {
                /*Provides a list with fetched products*/
            }

            @Override
            public void onPurchasedProductsFetched(@NonNull List<PurchaseInfo> purchases) {
                /*Provides a list with fetched purchased products*/
            }

            @Override
            public void onProductsPurchased(@NonNull List<PurchaseInfo> purchases) {
                /*Callback after a product is purchased*/
            }

            @Override
            public void onPurchaseAcknowledged(@NonNull PurchaseInfo purchase) {
                /*Callback after a purchase is acknowledged*/
                
                /*
                 * Grant user entitlement for NON-CONSUMABLE products and SUBSCRIPTIONS here
                 *
                 * Even though onProductsPurchased is triggered when a purchase is successfully made
                 * there might be a problem along the way with the payment and the purchase won't be acknowledged
                 *
                 * Google will refund users purchases that aren't acknowledged in 3 days
                 *
                 * To ensure that all valid purchases are acknowledged the library will automatically
                 * check and acknowledge all unacknowledged products at the startup
                 * */
            }

            @Override
            public void onPurchaseConsumed(@NonNull PurchaseInfo purchase) {
                /*Callback after a purchase is consumed*/
                
                /*
                 * CONSUMABLE products entitlement can be granted either here or in onProductsPurchased
                 * */
            }

            @Override
            public void onBillingError(@NonNull BillingConnector billingConnector, @NonNull BillingResponse response) {
                /*Callback after an error occurs*/
                
                switch (response.getErrorType()) {
                    case CLIENT_NOT_READY:
                        //TODO - client is not ready yet
                        break;
                    case CLIENT_DISCONNECTED:
                        //TODO - client has disconnected
                        break;
                    case SKU_NOT_EXIST:
                        //TODO - sku does not exist
                        break;
                    case CONSUME_ERROR:
                        //TODO - error during consumption
                        break;
                    case ACKNOWLEDGE_ERROR:
                        //TODO - error during acknowledgment
                        break;
                    case ACKNOWLEDGE_WARNING:
                        /*
                         * This will be triggered when a purchase can not be acknowledged because the state is PENDING
                         * A purchase can be acknowledged only when the state is PURCHASED
                         *
                         * PENDING transactions usually occur when users choose cash as their form of payment
                         *
                         * Here users can be informed that it may take a while until the purchase complete
                         * and to come back later to receive their purchase
                         * */
                        //TODO - warning during acknowledgment
                        break;
                    case FETCH_PURCHASED_PRODUCTS_ERROR:
                        //TODO - error occurred while querying purchased products
                        break;
                    case BILLING_ERROR:
                        //TODO - error occurred during initialization / querying sku details
                        break;
                    case USER_CANCELED:
                        //TODO - user pressed back or canceled a dialog
                        break;
                    case SERVICE_UNAVAILABLE:
                        //TODO - network connection is down
                        break;
                    case BILLING_UNAVAILABLE:
                        //TODO - billing API version is not supported for the type requested
                        break;
                    case ITEM_UNAVAILABLE:
                        //TODO - requested product is not available for purchase
                        break;
                    case DEVELOPER_ERROR:
                        //TODO - invalid arguments provided to the API
                        break;
                    case ERROR:
                        //TODO - fatal error during the API action
                        break;
                    case ITEM_ALREADY_OWNED:
                        //TODO - failure to purchase since item is already owned
                        break;
                    case ITEM_NOT_OWNED:
                        //TODO - failure to consume since item is not owned
                        break;
                }
            }
        });

Initiate a purchase

● Purchase a non-consumable/consumable product:

billingConnector.purchase(this, "sku_id");

● Purchase a subscription:

billingConnector.subscribe(this, "sku_id");

● Cancel a subscription:

billingConnector.unsubscribe(this, "sku_id");

Source Code :- https://github.com/Mahadev-code/Android-inApp-Billing

Upvotes: 0

Adrian Ivasku
Adrian Ivasku

Reputation: 1118

Basically queryPurchaseHistoryAsync does the job, just be careful to pass the SKU TYPE (inapp or subs).

My implementation:

fun restorePurchaseInApp() {
    bp.queryPurchaseHistoryAsync("inapp", this)
}

fun restorePurchaseInSubs() {
    bp.queryPurchaseHistoryAsync("subs", this)
}

// bp is BillingClient
// the class should implement PurchaseHistoryResponseListener

override fun onPurchaseHistoryResponse(
    p0: BillingResult,
    p1: MutableList<PurchaseHistoryRecord>?
) {
    if (p1 != null) {
        Log.d("TMS", "onPurchaseHistoryResponse: " + p1.size)
    }

    if (p1 != null) {
        for (item in p1) {
            Log.d("TMS", "onPurchaseHistoryResponse sku: " + item.sku)
            Log.d("TMS", "onPurchaseHistoryResponse signature: " + item.signature)
            Log.d("TMS", "onPurchaseHistoryResponse purchaseToken: " + item.purchaseToken)
            Log.d("TMS", "onPurchaseHistoryResponse purchaseTime: " + item.purchaseTime)
        }
    }
}

There you get the items that are purchased and that`s it :). I hope this will help because I lost a lot of time figuring out something so simple and the docs implementation have no mention about this.

Upvotes: 1

Hanrui
Hanrui

Reputation: 256

BillingProcessor and onProductPurchased seemed not to be part of Play Billing Library (nor AIDL), it's more like a wrap class implemented by anjlab(https://github.com/anjlab/android-inapp-billing-v3) To fullfill your needs, I think queryPurchases and queryPurchaseHistoryAsync can help.

Upvotes: 1

Related Questions