No results found

Try a different or more specific query
Developer Console

Implement the Google Play Billing Interface

This page details how your app can implement in-app purchasing using the Appstore SDK Compatible With Google Play Billing API. If you've already integrated your app with Google Play Billing Library, you'll make minimal or no code changes in most steps.

For a detailed API reference, see API reference for Appstore SDK Compatible With Google Play Billing API v4.0.0.

Initialize a BillingClient

You don't need to make code changes here.

After following the steps in Appstore SDK Compatible With Google Play Billing, initialize a BillingClient instance. The BillingClient enables communicates between the Appstore SDK and your app. The BillingClient provides asynchronous convenience methods for many common billing operations.

Like Google Play Billing, it's strongly recommended that you instantiate only one BillingClient instance at a time. However, with the Appstore SDK, instantiating multiple BillingClient instances at a time doesn't result in multiple PurchasesUpdatedListener callbacks for a single purchase event. Instead, each new instantiation of BillingClient updates the PurchasesUpdatedListener to the new listener provided, even for other BillingClient instances.

Create a BillingClient using the newBuilder() method. To receive updates on purchases, add a listener by calling setListener(). Pass a PurchasesUpdatedListener object to the setListener() method.

The enablePendingPurchases() method is only available as a no-op method . It doesn't enable pending purchases.

The following code shows how to initialize a BillingClient.

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        // To be implemented 
    }
};

private BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    .enablePendingPurchases()
    .build();

Connect to the Amazon Appstore

You don't need to make code changes here.

Unlike the requirement to establish a connection in Google Play, the Amazon Appstore doesn't have any concept of maintaining a connection. Since connection maintenance is not necessary in the Amazon Appstore, you don't need to monitor whether the connection is broken or not. Connection-related APIs are supported as no-op methods that assume the connection is always ready.

On calling startConnection(), the BillingClientStateListener always receives a callback with BillingResponseCode.OK. The onBillingServiceDisconnected() method is provided as a no-op method, which is never invoked by the Appstore SDK.

The following example demonstrates how to connect to the Amazon Appstore.

billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(BillingResult billingResult) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK) {
            // In the Appstore SDK, the BillingClientStateListener always receives 
            // a callback with BillingResponseCode.OK.
            
            // Query products and purchases here.
        }
    }
    
    @Override
    public void onBillingServiceDisconnected() {
        // This is a no-op method, which is never invoked by the Appstore SDK.
    }
});

Show products available to buy

You don't need to make code changes here.

Before showing products to your users, make sure to query for product details to get localized product information. You can either call queryProductDetailsAsync() or querySkuDetailsAsync() to query for in-app product details.

The only error response codes the Appstore SDK returns are BillingResponseCode.DEVELOPER_ERROR and BillingResponseCode.ERROR. Other error response codes supported by Google Play Billing are available, but are never returned.

QueryProductDetailsAsync API

You can query for product details with the queryProductDetailsAsync() method. This method takes an instance of QueryProductDetailsParams. The QueryProductDetailsParams object specifies a list of product ID strings you created in the Amazon Developer Console, along with a ProductType. For consumables and entitlements, the ProductType is ProductType.INAPP.

To handle the result of the asynchronous operation, the queryProductDetailsAsync() method also requires a listener. This listener is your implementation of the ProductDetailsResponseListener interface, where you override onProductDetailsResponse(). The onProductDetailsResponse() method notifies the listener when the product details query finishes, as shown in the following example.

QueryProductDetailsParams queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.INAPP)
                    .build()))
        .build();

billingClient.queryProductDetailsAsync(
    queryProductDetailsParams,
    new ProductDetailsResponseListener() {
        public void onProductDetailsResponse(BillingResult billingResult,
                List<ProductDetails> productDetailsList) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Process the returned skuDetailsList.
            } else if (billingResult.getResponseCode() == BillingResponseCode.ERROR) {
                // Handle the error response.
            } else if (billingResult.getResponseCode() == BillingResponseCode.DEVELOPER_ERROR) {
                // Handle the developer error response.
            } else {
                // Other error codes are available, but are never returned by the 
                // Appstore SDK.
            }
        }
    }
);

Unlike Google Play Billing, ProductDetails.getTitle() does not include the app name.

QuerySkuDetailsAsync API

You can query for SKU details with the querySkuDetailsAsync() method. This method takes an instance of SkuDetailsParams, which specifies a list of SKU strings created in the Amazon Developer Console, along with a SkuType. For consumables and entitlements, the SkuType is SkuType.INAPP.

To handle the result of the asynchronous operation, querySkuDetailsAsync() also requires a listener. This listener is your implementation of the SkuDetailsResponseListener interface, where you override onSkuDetailsResponse(). The onSkuDetailsResponse() method notifies the listener when the SKU details query finishes, as shown in the following example.

SkuDetailsParams skuDetailsParams =
    SkuDetailsParams.newBuilder()
            .setType(BillingClient.SkuType.INAPP)
            .setSkusList(skusList)
            .build();
            
billingClient.querySkuDetailsAsync(
    skuDetailsParams,
    new SkuDetailsResponseListener() {
        public void onSkuDetailsResponse(BillingResult billingResult,
               List<SkuDetails> skuDetailsList) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Process the returned skuDetailsList.
            } else if (billingResult.getResponseCode() == BillingResponseCode.ERROR) {
                // Handle the error response.
            } else if (billingResult.getResponseCode() == BillingResponseCode.DEVELOPER_ERROR) {
                // Handle the developer error response.
            } else {
                // Other error codes are available, but are never returned by the 
                // Appstore SDK.
            }
        }
    }
);

Launch the purchase flow

You might need to make minimal code changes here.

For your app to start the purchase flow, call launchBillingFlow() from your app's main thread. Unlike Google Play Billing, the Appstore SDK allows at most one product in a single purchase.

The launchBillingFlow() method takes a BillingFlowParams object, which contains a ProductDetails object. You can get ProductDetails by calling queryProductDetailsAsync(). Create a BillingFlowParams object by using the BillingFlowParams.Builder class. The following example shows how to launch the billing flow.

// An activity reference from which the billing flow is launched
Activity activity = ...;

ImmutableList productDetailsParamsList =
    ImmutableList.of(
        ProductDetailsParams.newBuilder()
             // Call queryProductDetailsAsync to get productDetails
            .setProductDetails(productDetails)
            .build()
    );

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)
    .build();

// Launch the billing flow
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);

Alternatively, the BillingFlowParams object can be initialized with a SkuDetails object obtained from calling querySkuDetailsAsync() as shown.

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
     .setSkuDetails(skuDetails)
     .build();

When you initialized your BillingClient, you used setLister() to add your implementation of PurchasesUpdatedListener as a listener. This listener overrides the onPurchasesUpdated() method, which delivers the result of your purchase. Your implementation of onPurchasesUpdated() must handle the possible response codes, as shown in the following example.

@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK) {
        for (Purchase purchase : purchases) {
            handlePurchase(purchase);
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.ERROR) {
        // Handle an error in the purchase flow.
    } else if (billingResult.getResponseCode() == BillingResponseCode.DEVELOPER_ERROR) {
        // Handle a developer error in the purchase flow.
    } else {
        // Other error codes are available, but are never returned by the Appstore SDK.
    }
}

The only error response codes the Appstore SDK returns are BillingResponseCode.DEVELOPER_ERROR and BillingResponseCode.ERROR. Other error response codes supported by Google Play Billing are available, but are never returned.

When a purchase is successful, a purchase token is generated. A purchase token uniquely identifies a purchase and represents the user and the product ID associated with the purchase.

Unsupported fields

The following fields that are available in Google Play Billing are not supported in the Appstore SDK. Remove references to these fields from your code.

Request fields:

  • Account identifiers (Obfuscated Account ID and Obfuscated Profile ID)
  • VR Purchase Flow

Response fields:

  • Account identifiers (Obfuscated Account ID and Obfuscated Profile ID)
  • Order ID
  • Signature
  • Package name
  • Acknowledged

Process purchases

You might need to make minimal code changes here.

After a user completes a purchase, your app needs to process that purchase. Your app is usually notified of purchases through your PurchasesUpdatedListener. However, there are cases where your app uses queryPurchasesAsync() to fetch purchases, as described in Fetch Purchases.

On completing a purchase, your app should give the content to the user. For entitlements, acknowledge delivery of the content using acknowledgePurchase(). For consumables, call consumeAsync() to acknowledge delivery and mark the item as consumed.

Review these differences between the Amazon Appstore's Google Play Billing API interface and the Google Play Billing Library:

  • Calling acknowledgePurchase() on a consumable doesn't just acknowledge the item, it also consumes it. Calling consumeAsync() on an entitlement only acknowledges the item, without consuming it. This happens because Appstore SDK considers consumables and entitlements as separate entities internally, unlike Google Play Billing.
  • Appstore SDK allows purchasing a consumable item again, even if it had already been purchased and not yet consumed (which can occur if consumeAsync() wasn't called on the previous purchase).
  • On non-acknowledgement of purchases, users do not automatically receive refunds. This is different from Google Play Billing, where purchases are revoked on non-acknowledgement for three days as detailed in the Android developer documentation.

The following example shows how to consume a product using the associated purchase token:

void handlePurchase(Purchase purchase) {
    // Purchase retrieved from queryPurchasesAsync or your PurchasesUpdatedListener.
    if (purchase.getPurchaseState() != PurchaseState.PURCHASED) {
        return;
    }

    // Deliver item to user.
    
    // Consume item.
    ConsumeParams consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build();

    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Handle the success of the consume operation.
            } else if (billingResult.getResponseCode() == BillingResponseCode.ERROR) {
                // Handle the error response.
            } else {
                // Other error codes are available, but are never returned by the Appstore SDK.
            }
        }
    };

    billingClient.consumeAsync(consumeParams, listener);
}

Similarly, the following example shows how to acknowledge a purchase using the associated purchase token:

void handlePurchase(Purchase purchase) {
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED) {
        AcknowledgePurchaseParams acknowledgePurchaseParams =
            AcknowledgePurchaseParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build();
        
        AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ...
        
        billingClient.acknowledgePurchase(
            acknowledgePurchaseParams, 
            acknowledgePurchaseResponseListener);
    }
}

The only error response code returned by the Appstore SDK when consuming an item is BillingResponseCode.ERROR. Other error response codes supported by Google Play Billing are available, but are never returned.

Purchase verification (optional)

Server-side purchase verification is available for the Amazon Appstore, but it differs from Google Play Billing. This is an optional step.

After a successful purchase, you can verify that the purchase receipt has come from authorized source. Amazon IAP provides the Receipt Verification Service (RVS) which requires you to make a call to the RVS API from your backend server to verify the correctness of a receipt.

Fetch purchases

You might need to make minimal code changes here.

Although your app is notified of purchases when listening through PurchasesUpdatedListener, certain scenarios might cause your app to be unaware of a purchase a user has made. Scenarios where your app could be unaware of purchases are:

  • Network issues: A user makes a successful purchase, but their device has a network connection failure before being notified of the purchase through PurchasesUpdatedListener.
  • Multiple devices: A user buys an item on a device, switches to another device, and expects to see the item they purchased.

You can handle these scenarios by calling queryPurchasesAsync() in your onResume() method. This ensures all purchases are successfully processed, as described in Process purchases.

The queryPurchasesAsync() method returns only non-consumed in-app purchases for entitlements and non-consumed consumables. The following example shows how to fetch a user's in-app purchases:

billingClient.queryPurchasesAsync(
    QueryPurchasesParams.newBuilder()
      .setProductType(ProductType.INAPP)
      .build(),
    new PurchasesResponseListener() {
      public void onQueryPurchasesResponse(BillingResult billingResult, List purchases) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK) {
            // Process returned purchase list (display the in-app items the user owns).
        } else if (billingResult.getResponseCode() == BillingResponseCode.ERROR) {
            // Handle the error response.
        } else if (billingResult.getResponseCode() == BillingResponseCode.DEVELOPER_ERROR) {
            // Handle the developer error response.
        } else {
            // Other error codes are available, but are never returned by the Appstore SDK.
        }
      }
    }
);

Alternatively, queryPurchasesAsync() can be called with a string specifying the SkuType instead of a QueryPurchasesParams object as shown.

billingClient.queryPurchasesAsync(
     SkuType.INAPP,
     purchasesResponseListener
 );

The only error response codes the Appstore SDK returns are BillingResponseCode.DEVELOPER_ERROR and BillingResponseCode.ERROR. Other error response codes supported by Google Play Billing are available, but are never returned.

Unlike Google Play Billing, consumables that aren't consumed (if consumeAsync() wasn't called) are only returned on the device from which they were purchased, and not other devices. If the version of the app changes on the same device (for example, when the app is upgraded), consumables that have not yet been consumed might not be returned immediately, but they are eventually returned.

Unsupported fields

The following fields that are available in Google Play Billing are not supported in Appstore SDK in the response. Remove references to these fields from your code.

  • Account identifiers (Obfuscated Account ID and Obfuscated Profile ID)
  • Order ID
  • Signature
  • Package name
  • Acknowledged

Unsupported features

The Appstore SDK Compatible With Google Play Billing API doesn't support the following features and APIs.

Test your app

To test your app and verify the integration with the Appstore SDK Compatible With Google Play Billing API, follow these guidelines.

  • Use the Live App Testing service to test your app in a live production environment with a select group of users.
  • Create and submit the in-app purchase items for your Appstore SDK integrated app before you start Live App Testing.
  • Keep the SKUs for the in-app items on the Amazon Developer Console the same as the product IDs on Google Play Console for the app. Otherwise, you will have to update the product IDs in your app as well.

Last updated: Nov 17, 2022