Developer Console

RVS Production Setup for Appstore Billing Compatibility

After you publish your app, you can set up your app server to communicate with the RVS production server. Amazon recommends calling RVS from a secure server because it hosts the shared secret. Don't call RVS from your app. Set up your app server using your preferred language and technology. The server must communicate with RVS using a secure protocol such as HTTPS. Your server sends validation requests to RVS and then processes the responses it receives.

Client-side setup

On the client side, the onPurchaseUpdated() method of the Appstore Billing Compatibility SDK gets invoked for new purchases. The following Android sample code performs the these tasks:

  1. Listens for the new purchases by overriding onPurchaseUpdated() method.
  2. If purchase list is not null, verifies each of the purchases by passing the purchase token to your server.
  3. If purchase is valid, grants the items, or else logs the validation error.
private void processPurchaseList(List<Purchase> purchases, List<String> skusToUpdate) {

    if (null != purchases) {
        for (final Purchase purchase : purchases) {
            // Verify the receipt using receipt verification service
            final String purchaseToken = purchase.getPurchaseToken();
            // Send purchase token to your secure backend for verification
            if(verifyInAppItemReceipt(purchaseToken, productType, packageName)) {
                // Process the purchase, grant the items
            } else {
                Log.d("Purchase is not valid")
            }
        }
    }
}

@Override
public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> list) {
    
    switch (billingResult.getResponseCode()) {
    
        case BillingClient.BillingResponseCode.OK:
            if(list != null) {
                processPurchaseList(list, null);
                return;
            }
            else {
                Log.d(TAG, "Null Purchase List Returned from OK response!");
            }
            break;
        case BillingClient.BillingResponseCode.USER_CANCELED:
        case BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED:
        case BillingClient.BillingResponseCode.DEVELOPER_ERROR:
            // Handle these cases
            break;
        default:
            // Handle default case
            break;
    }
}

Server-side setup

The following sample code is for a generic Java server. This code calls the purchases.products.get and purchases.subscriptionsv2.get APIs to perform the following tasks:

  1. Create a URL string with the appropriate developer and transaction information.
  2. Connect to the Amazon RVS server and pass the transaction URL to the server.
  3. Retrieve the response from the Amazon RVS server.
  4. Pass the response to the app.
// Verify entitlement or consumable receipt 
public static void verifyInAppItemReceipt(final String developerSecret, final String packageName, final String token) {

    // Call purchases.products.get API to validate inApp item purchase 
    String url = "https://appstore-sdk.amazon.com/version/1.0/" + 
    "developer/" + developerSecret + "/applications/" + packageName + "/purchases/subscriptionsv2/tokens/" + token

    JSONObject responseJson = verifyReceipt(url);

    JSONObject canceledStateContext = responseJson.getJSONObject("CanceledStateContext");
    String purchaseToken = responseJson.getString("purchaseToken");
    long purchaseTimeMillis = responseJson.getString("purchaseTimeMillis");
    long cancelDate = responseJson.optLong("cancelDate");
    boolean testTransaction = responseJson.optBoolean("testTransaction");
}

// Verify Subscription receipt 
public static void verifySubscriptionReceipt(final String developerSecret, final String packageName, final String productId, final String token) {
    
    // Call purchases.subscriptionsv2.get API to validate subscription item purchase.
    String url = "https://appstore-sdk.amazon.com/version/1.0/" + 
    "developer/" + developerSecret + "/applications/" + packageName + "/purchases/products/" + productId + "/tokens/" + token

    JSONObject responseJson = verifyReceipt(url);

    String purchaseToken = responseJson.getString("purchaseToken");
    String productType = responseJson.getString("productType");
    String productId = responseJson.getString("productId");
    long purchaseTimeMillis = responseJson.getString("purchaseTimeMillis");
    long cancelDate = responseJson.optLong("cancelDate");
    boolean testTransaction = responseJson.optBoolean("testTransaction");
}

private static void verifyReceipt(final String url) {
    System.out.println("Start Receipt Validation");

    System.out.println("Amazon Receipt Validation URL: " + url);


    JSONObject responseJson = null;
    try {
        System.out.println("Open HTTP connection to Amazon RVS");

        URL obj = new URL(url);
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();

        int responseCode = con.getResponseCode();

        System.out.println("Amazon RVS Response Code: " + responseCode);

        switch (responseCode)
        {
            case 400:
                System.out.println("Amazon RVS Error: Invalid purchase token or productId");
                // Process Response Data locally
                // Respond to app
                break;

            case 401:
                System.out.println("Amazon RVS Error: Invalid developerSecret");
                // Process Response Data locally
                // Respond to app
                break;

            case 404:
                System.out.println("Amazon RVS Error: Invalid packageName");
                // Process Response Data locally
                // Respond to app
                break;

            case 500:
                System.out.println("Amazon RVS Error: Internal Server Error");
                // Process Response Data locally
                // Respond to app
                break;

            case 200:

                //Retrieve Amazon RVS Response
                BufferedReader in = new BufferedReader( new InputStreamReader(con.getInputStream()));

                String inputLine;
                StringBuffer response = new StringBuffer();

                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();

                //Log Amazon RVS Response
                System.out.println("Amazon RVS Response: " + response.toString()());

                //Create JSONObject for RVS Response
                JSONObject responseJson = new JSONObject(response.toString());

                //Parse RVS Response
                responseJson = new JSONObject(response.toString());
                break;

            default:
                System.out.println("Amazon RVS Error: Undefined Response Code From Amazon RVS");
                // Process Response Data locally
                // Respond to app
                break;
        }

    } catch (MalformedURLException e) {

        // As a best practice, replace the following logic with logic for logging.
        System.out.println("Amazon RVS MalformedURLException");
        e.printStackTrace();
        // Process Response Data locally
        // Respond to app
    } catch (IOException e) {

        // As a best practice, replace the following logic with logic for logging.
        System.out.println("Amazon RVS IOException");
        e.printStackTrace();
        // Process Response Data locally
        // Respond to app
    }

    return responseJson;
}

Last updated: May 22, 2024