The In-app Billing Version 2 API has been deprecated in
favor of the Version 3 API. If your app is using In-app Billing, please
make sure that it is using the Version 3 API. If your app is
still using the Version 2 API, you must migrate to the Version 3 API
as soon as possible.
We plan to turn off the In-app Billing Version 2 service on January
27, 2015, after which time users will no longer be able to
purchase in-app items and subscriptions through the Version 2 API.
We strongly encourage and recommend you migrate your apps to use Version 3
API by November 2014, to provide ample time for users to update their apps to
the new version.
For more information, please see the Help Center
article. For common questions about transitioning your implementation to
In-app Billing Version 3, please see Migration
Considerations.
In this document
This document is focused on highlighting implementation details that are specific to subscriptions with the Version 2 API. To understand how subscriptions work, see In-app Billing Subscriptions.
Sample Application
To help you get started with your In-app Billing implementation and subscriptions, an updated Version of the In-app Billing sample app is available. You can download the sample app from the Android SDK repository using the Android SDK Manager. For details, see Downloading the Sample Application.
Application Model
With subscriptions, your app uses the standard In-app Billing application model, sending billing requests to the Play Store application over interprocess communication (IPC) and receiving purchase responses from the Play Store app in the form of asynchronous broadcast intents. Your application does not manage any network connections between itself and the Google Play server or use any special APIs from the Android platform.
Your app also uses the standard In-app Billing components — a billing Service for sending requests, a BroadcastReceiver for receiving the responses, and a security component for verifying that the response was sent by Google Play. Also recommended are a response Handler for processing notifications, errors, and status messages, and an observer for sending callbacks to your application as needed. All of these components and their interactions are described in full in the In-app Billing Overview and related documents.
To initiate different types of billing communication with Google Play, your app will use the standard set of in-app billing requests and receive the same responses. Inside the requests and responses are two new fields described below.
Purchase Token
Central to the end-to-end architecture for subscriptions is the purchase token, a string value that uniquely identifies (and associates) a user ID and a subscription ID. Google Play generates the purchase token when the user completes the purchase of a subscription product (and payment is approved by Google Wallet) and then sends it to the purchasing app on the device through the In-app Billing API.
At the conclusion of a PURCHASE_REQUEST
message flow, your app
can retrieve the purchase token and other transaction details by initiating a
GET_PURCHASE_INFORMATION
request. The Bundle returned by the call
contains an JSON array of order objects. In the order corresponding to the
subscription purchase, the token is available in the purchaseToken
field.
An example of a JSON order object that includes a subscription purchase token is shown below.
{ "nonce" : 1836535032137741465, "orders" : [{ "notificationId" : "android.test.purchased", "orderId" : "12999556515565155651.5565135565155651" "packageName" : "com.example.dungeons", "productId" : "android.test.purchased", "developerPayload" : "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ", "purchaseTime" : 1290114783411, "purchaseState" : 0, "purchaseToken" : "rojeslcdyyiapnqcynkjyyjh" }] }
After receiving a purchase token, your apps can store the token locally or pass it to your backend servers, which can then use it to query the billing status or cancel the subscription remotely. If your app will store the token locally, please read the Security and Design document for best practices for maintaining the security of your data.
Checking the In-app Billing API Version
Subscriptions support is available only in versions of Google Play that support the In-app Billing v2 API (Google Play 3.5 and higher). For your app, an essential first step at launch is to check whether the Version of Google Play installed on the device supports the In-app Billing v2 API and subscriptions.
To do this, create a CHECK_BILLING_SUPPORTED request Bundle that includes the required key-value pairs, together with
- The
API_VERSION
key, assigning a value of 2. - The
BILLING_REQUEST_ITEM_TYPE
key, assigning a value of “subs”
Send the request using sendBillingRequest(Bundle)
and receive
the response Bundle. You can extract the response from the
BILLING_RESPONSE_RESPONSE_CODE
key of the response. RESULT_OK
indicates that subscriptions are supported.
The sample app declares constants for the accepted
BILLING_REQUEST_ITEM_TYPE
values (from Consts.java):
// These are the types supported in the IAB v2 public static final String ITEM_TYPE_INAPP = "inapp"; public static final String ITEM_TYPE_SUBSCRIPTION = "subs";
It sets up a convenience method for building the request bundle (from BillingService.java):
protected Bundle makeRequestBundle(String method) {
Bundle request = new Bundle();
request.putString(Consts.BILLING_REQUEST_METHOD, method);
request.putInt(Consts.BILLING_REQUEST_API_VERSION
, 2);
request.putString(Consts.BILLING_REQUEST_PACKAGE_NAME, getPackageName());
return request;
}
Here’s an example of how to test support for In-App Billing v2 and subscriptions (from BillingService.java):
/** * Wrapper class that checks if in-app billing is supported. */ class CheckBillingSupported extends BillingRequest { public String mProductType = null; public CheckBillingSupported() { // This object is never created as a side effect of starting this // service so we pass -1 as the startId to indicate that we should // not stop this service after executing this request. super(-1); } public CheckBillingSupported(String type) { super(-1); mProductType = type; } @Override protected long run() throws RemoteException { Bundle request = makeRequestBundle("CHECK_BILLING_SUPPORTED"); if (mProductType != null) { request.putString(Consts.BILLING_REQUEST_ITEM_TYPE
, mProductType); } Bundle response = mService.sendBillingRequest(request); int responseCode = response.getInt(Consts.BILLING_RESPONSE_RESPONSE_CODE
); if (Consts.DEBUG) { Log.i(TAG, "CheckBillingSupported response code: " + ResponseCode.valueOf(responseCode)); } boolean billingSupported = (responseCode == ResponseCode.RESULT_OK.ordinal()); ResponseHandler.checkBillingSupportedResponse(billingSupported, mProductType); return Consts.BILLING_RESPONSE_INVALID_REQUEST_ID; } }
Requesting a Subscription Purchase
Once you’ve checked the API Version as described above and determined that subscriptions are supported, you can present subscription products to the user for purchase. When the user has selected a subscription product and initiated a purchase, your app handles the purchase just as it would for other in-app products — by sending a REQUEST_PURCHASE request. You can then launch Google Play to display the checkout user interface and handle the financial transaction..
The REQUEST_PURCHASE includes a Bundle containing the item details, as described in the In-app Billing Overview. For a subscription, the Bundle must also specify:
- The
ITEM_ID
key, with a value that specifies a valid, published subscription product. - The
ITEM_TYPE
key, with a value of “subs” (ITEM_TYPE_SUBSCRIPTION
in the sample app). If the request does not specify the subscription'sITEM_TYPE
, Google Play attempts to handle the request as a standard in-app purchase (one-time purchase).
Google Play synchronously returns a response bundle that includes
RESPONSE_CODE
, PURCHASE_INTENT
, and
REQUEST_ID
. Your app uses the PURCHASE_INTENT
to
launch the checkout UI and the message flow proceeds exactly as described in Messaging sequence.
Here’s how the sample app initiates a purchase for a subscription, where
mProductType
is ITEM_TYPE_SUBSCRIPTION
(from
BillingService.java).
/**
* Wrapper class that requests a purchase.
*/
class RequestPurchase extends BillingRequest {
public final String mProductId;
public final String mDeveloperPayload;
public final String mProductType;
. . .
@Override
protected long run() throws RemoteException {
Bundle request = makeRequestBundle("REQUEST_PURCHASE");
request.putString(Consts.BILLING_REQUEST_ITEM_ID, mProductId);
request.putString(Consts.BILLING_REQUEST_ITEM_TYPE
, mProductType);
// Note that the developer payload is optional.
if (mDeveloperPayload != null) {
request.putString(Consts.BILLING_REQUEST_DEVELOPER_PAYLOAD, mDeveloperPayload);
}
Bundle response = mService.sendBillingRequest(request);
PendingIntent pendingIntent
= response.getParcelable(Consts.BILLING_RESPONSE_PURCHASE_INTENT);
if (pendingIntent == null) {
Log.e(TAG, "Error with requestPurchase");
return Consts.BILLING_RESPONSE_INVALID_REQUEST_ID;
}
Intent intent = new Intent();
ResponseHandler.buyPageIntentResponse(pendingIntent, intent);
return response.getLong(Consts.BILLING_RESPONSE_REQUEST_ID,
Consts.BILLING_RESPONSE_INVALID_REQUEST_ID);
}
@Override
protected void responseCodeReceived(ResponseCode responseCode) {
ResponseHandler.responseCodeReceived(BillingService.this, this, responseCode);
}
}
Restoring Transactions
Subscriptions always use the managed by user account purchase type, so that you can restore a record of subscription transactions on the device when needed. When a user installs your app onto a new device, or when the user uninstalls/reinstalls the app on the original device, your app should restore the subscriptions that the user has purchased.
The process for restoring subscriptions transactions is the same as described
in Messaging sequence. Your app sends a
RESTORE_TRANSACTIONS
request to Google Play. Google Play sends two
broadcast intents as asynchronous responses — a RESPONSE_CODE
intent and a PURCHASE_STATE_CHANGED
intent.
The PURCHASE_STATE_CHANGED
intent contains a notification ID
that your app can use to retrieve the purchase details, including the purchase
token, by sending a standard GET_PURCHASE_INFORMATION
request. The
Bundle
returned in the call includes an JSON array of order objects
corresponding to subscription (and in-app product) purchases that you can
restore locally.
Your app can store the restored purchase state and other transaction details in the way that best meets your needs. Your app can use it later to check the subscription validity, although please read the Security and Design document for best practices for maintaining the security of your data.
Checking Subscription Validity
Subscriptions are time-bound purchases that require successful billing recurrences over time to remain valid. Your app should check the validity of purchased subscriptions at launch or prior to granting access to subscriber content.
With In-app Billing, you validate a subscription by keeping track of its purchase state and then checking the state whenever needed. Google Play provides two ways to let you know when the purchase state of a subscription changes:
- In-app Billing Notifications. Google Play pushes a notification to your app to indicate a change in the purchase state of a subscription. Your app can store the most recent purchase state for a given purchase token and then check that state at run time, as needed.
- Google Play Android Developer API. You can use this HTTP-based
API to poll Google Play for the current purchase state of a subscription. You
can store the purchased state for each
purchaseToken
on your backend servers. For more information, see Google Play Android Developer API, below.
For most use-cases, especially those where backend servers are already keeping track of subscribed users, implementing a combination of both methods is the recommended approach. A typical implementation might work like this:
- When the user successfully purchases a new subscription, your app notifies a backend server, which stores the purchase token, user name, and other information in a secure location.
- Since your app cannot know the expiration date, your server can poll Google Play to get the expiration and store it with the purchase token and other data.
- Because your server now knows the expiration date, it does not need to poll Google Play again until after the expiration date, at which time it can confirm that the subscription was not cancelled.
- On the client side, your app can continue to update the server whenever the purchase state changes, storing the state locally.
If you are using both notifications and the Google Play Android Developer API to validate subscriptions, we recommend the following:
- If your app wants to check validity but you can’t reach your server (or you don’t have a server), use the latest purchase state received by notification.
- If you have a server and it’s reachable, always give preference to the purchase state obtained from your server over the state received in notifications.
If necessary, you can also use a RESTORE_TRANSACTIONS
request to retrieve a record of all managed and in-app products purchased by the user, which you can then store locally. However, using RESTORE_TRANSACTIONS
on a regular basis is not recommended because of performance impacts.
Regardless of the approach you choose, your app should check subscriptions and validity at launch, such as prior to accessing subscriber content, game levels, and so on.
State | purchaseState Value | Comments |
---|---|---|
Purchased successfully | 0 | Sent at original purchase only (not at recurring billing cycles). | Cancelled | 1 | Sent at original purchase only if the purchase has failed for some reason. | Refunded | 2 | The purchase was refunded. | Subscription expired | 3 | Sent at the end of a billing cycle to indicate that the subscription expired without renewal because of non-payment or user-cancellation. Your app does not need to grant continued access to the subscription content. |
Letting the User Cancel or View Subscriptions
In-app Billing does not currently provide an API to let users directly view or cancel subscriptions from within the purchasing app. Instead, users can launch the Play Store app on their devices and go to the My Apps screen to manage subscriptions. In My Apps, users can see a list of their subscriptions organized by application. Tapping one of the subscriptions loads the app's product page, from which users can see active subscriptions and billing status and cancel subscriptions as needed.
To make it easier for users to find and manage their subscriptions from inside your app, we recommend that you offer a "View My Subscriptions" or "Manage Subscriptions" option in your UI that directly loads your app's product page in the Play Store app.
To do this, create an intent with the ACTION_VIEW
action and include the market://
URI (rather than the http://
URI) of your app's details page. Here’s an example:
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse("market://details?id=com.example.app")); startActivity(intent);
For more information, see Linking to Your Products.
Recurring Billing, Cancellation, and Changes In Purchase State
Google Play notifies your app when the user completes the purchase of a subscription, but the purchase state does not change over time, provided that recurring billing takes place successfully. Google Play does not notify your app of a purchase state change until the subscription expires because of non-payment or user cancellation.
Over the life of a subscription, your app does not need to initiate any recurring billing events — those are all handled by Google Play and they are transparent to your application if billing is successful.
When the user cancels a subscription during an active billing cycle, Google Play does not notify your app immediately of the change in purchase state. Instead, it waits until the end of the active billing cycle and then notifies your app that the purchase state has changed to "Expired".
Similarly, if payment for the next billing cycle fails, Google Play waits until the end of the active billing cycle and then notifies your app at that time that the purchase state has changed to "Expired".
Your app can handle user cancellation and non-payment in the same way, since both cause a change to the same "Expired" purchase state. Once the purchase state has become "Expired", your app does not need to grant further access to the subscription content.
Modifying Your App for Subscriptions
For subscriptions, you make the same types of modifications to your app as are described in Modifying your Application Code.
Note that, in your UI that lets users view and select subscriptions for purchase, you should add logic to check for purchased subscriptions and validate them. Your UI should not present subscriptions if the user has already purchased them.