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:
- Listens for the new purchases by overriding
onPurchaseUpdated()
method. - If purchase list is not null, verifies each of the purchases by passing the purchase token to your server.
- 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:
- Create a URL string with the appropriate developer and transaction information.
- Connect to the Amazon RVS server and pass the transaction URL to the server.
- Retrieve the response from the Amazon RVS server.
- 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;
}
Related topics
- RVS for Consumables and Entitlements
- RVS for Subscriptions
- RVS Examples for Appstore Billing Compatibility
Last updated: Oct 14, 2024