summaryrefslogtreecommitdiffstats
path: root/docs/html/google/play/billing/v2/billing_subscriptions.jd
blob: 3bcf212d8a4b32097892373c73bf2fe9867ce6bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
page.title=Implementing Subscriptions  <span style="font-size:16px;">(IAB Version 2)</span>
excludeFromSuggestions=true
@jd:body

<div style="background-color:#fffdeb;width:100%;margin-bottom:1em;padding:.5em;">In-app Billing Version 2 is superseded. Please <a href="{@docRoot}google/play/billing/billing_overview.html#migration">migrate to Version 3</a> at your earliest convenience.</div>
    <div id="qv-wrapper" style="margin-top:0;">
<div id="qv">
  <h2>In this document</h2>
  <ol>
        <li><a href="#sample">Sample Application</a></li>
        <li><a href="#model">Application Model</a></li>
        <li><a href="#token">Purchase Token</a></li>
        <li><a href="#version">Checking the In-app Billing API Version</a></li>
        <li><a href="purchase">Purchasing a Subscription</a></li>
        <li><a href="#restore">Restoring Transactions</a></li>
        <li><a href="#validity">Checking Subscription Validity</a></li>
        <li><a href="#viewstatus">Letting Users Cancel or View Status</a></li>
        <li><a href="#purchase-state-changes">Recurring Billing and Changes in Purchase State</a></li>
        <li><a href="modifying">Modifying Your App for Subscriptions</a></li>
   </ol>
</div>
</div>

<p>This document is focused on highlighting implementation details that are 
specific to subscriptions with the Version 2 API. To understand how  
subscriptions work, see <a href="{@docRoot}google/play/billing/billing_subscriptions.html">In-app Billing Subscriptions</a>.</p>


<h2 id="sample">Sample Application</h2>

<p>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 <a
href="{@docRoot}google/play/billing/v2/billing_integrate.html#billing-download">
Downloading the Sample Application</a>.</p>

<h2 id="model">Application Model</h2>

<p>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.</p>

<p>Your app also uses the standard In-app Billing components &mdash; 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 <a
href="{@docRoot}google/play/billing/v2/api.html">In-app Billing
Overview</a> and related documents.</p>

<p>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.
</p>

<h2 id="token">Purchase Token</h2>

<p>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 Checkout) and then sends it to the purchasing app on the device through the
In-app Billing API. </p>

<p>At the conclusion of a <code>PURCHASE_REQUEST</code> message flow, your app
can retrieve the purchase token and other transaction details by initiating a
<code>GET_PURCHASE_INFORMATION</code> 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 <code>purchaseToken</code>
field. </p>

<p>An example of a JSON order object that includes a subscription purchase token
is shown below. </p>

<pre class="no-pretty-print" style="color:black">{ "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" }]
}
</pre>

<p>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 <a
href="{@docRoot}google/play/billing/billing_best_practices.html">Security and
Design</a> document for best practices for maintaining the security of your
data.</p>

<h2 id="version">Checking the In-app Billing API Version</h2>

<p>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.</p>

<p>To do this, create a CHECK_BILLING_SUPPORTED request Bundle that includes the
required key-value pairs, together with</p>

<ul>
  <li>The <code>API_VERSION</code> key, assigning a value of 2.</li>
  <li>The <code>BILLING_REQUEST_ITEM_TYPE</code> key, assigning a value of “subs”</li>
</ul>

<p>Send the request using <code>sendBillingRequest(Bundle)</code> and receive
the response Bundle. You can extract the response from the
<code>BILLING_RESPONSE_RESPONSE_CODE</code> key of the response. RESULT_OK
indicates that subscriptions are supported.</p>

<p>The sample app declares constants for the accepted
<code>BILLING_REQUEST_ITEM_TYPE</code> values (from Consts.java):</p>

<pre class="pretty-print">   // 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";
</pre>

<p>It sets up a convenience method for building the request bundle (from BillingService.java):</p>

<pre class="pretty-print">       protected Bundle makeRequestBundle(String method) {
           Bundle request = new Bundle();
           request.putString(Consts.BILLING_REQUEST_METHOD, method);
           request.putInt(Consts.BILLING_REQUEST_<code>API_VERSION</code>, 2);
           request.putString(Consts.BILLING_REQUEST_PACKAGE_NAME, getPackageName());
           return request;
       }
</pre>

<p>Here’s an example of how to test support for In-App Billing v2 and subscriptions
(from BillingService.java):</p>

<pre class="pretty-print">   /**
    * 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;
       }

       &#64;Override
       protected long run() throws RemoteException {
           Bundle request = makeRequestBundle("CHECK_BILLING_SUPPORTED");
           if (mProductType != null) {
               request.putString(Consts.<code>BILLING_REQUEST_ITEM_TYPE</code>, mProductType);
           }
           Bundle response = mService.sendBillingRequest(request);
           int responseCode = response.getInt(Consts.<code>BILLING_RESPONSE_RESPONSE_CODE</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;
       }
   }
</pre>

<h2 id="purchase">Requesting a Subscription Purchase</h2>

<p>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 &mdash; by sending a REQUEST_PURCHASE request. You can then launch
Google Play to display the checkout user interface and handle the financial
transaction..  

<p>The REQUEST_PURCHASE includes a Bundle containing the item details, as
described in the <a
href="{@docRoot}google/play/billing/v2/api.html">In-app Billing
Overview</a>. For a subscription, the Bundle must also specify:</p>

<ul>
  <li>The <code>ITEM_ID</code> key, with a value that specifies a valid, published
  subscription product.</li>
  <li>The <code>ITEM_TYPE</code> key, with a value of “subs”
  (<code>ITEM_TYPE_SUBSCRIPTION</code> in the sample app). If the request does not
  specify the subscription's <code>ITEM_TYPE</code>, Google Play attempts to
  handle the request as a standard in-app purchase (one-time purchase).</li>
</ul>

<p>Google Play synchronously returns a response bundle that includes
<code>RESPONSE_CODE</code>, <code>PURCHASE_INTENT</code>, and
<code>REQUEST_ID</code>. Your app uses the <code>PURCHASE_INTENT</code> to
launch the checkout UI and the message flow proceeds exactly as described in <a
href="{@docRoot}google/play/billing/v2/api.html#billing-message-
sequence">Messaging sequence</a>.</p>

<p>Here’s how the sample app initiates a purchase for a subscription, where
<code>mProductType</code> is <code>ITEM_TYPE_SUBSCRIPTION</code> (from
BillingService.java).</p>

<pre class="pretty-print">   /**
    * Wrapper class that requests a purchase.
    */
   class RequestPurchase extends BillingRequest {
       public final String mProductId;
       public final String mDeveloperPayload;
       public final String mProductType;

. . .

       &#64;Override
       protected long run() throws RemoteException {
           Bundle request = makeRequestBundle("REQUEST_PURCHASE");
           request.putString(Consts.BILLING_REQUEST_ITEM_ID, mProductId);
           request.putString(Consts.<code>BILLING_REQUEST_ITEM_TYPE</code>, 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);
       }

       &#64;Override
       protected void responseCodeReceived(ResponseCode responseCode) {
           ResponseHandler.responseCodeReceived(BillingService.this, this, responseCode);
       }
   }
</pre>

<h2 id="restoring">Restoring Transactions</h2>

<p>Subscriptions always use  the <em>managed by user account</em> 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.</p>

<p>The process for restoring subscriptions transactions is the same as described
in <a
href="{@docRoot}google/play/billing/v2/api.html#billing-message-
sequence">Messaging sequence</a>. Your app sends a
<code>RESTORE_TRANSACTIONS</code> request to Google Play. Google Play sends two
broadcast intents as asynchronous responses &mdash; a <code>RESPONSE_CODE</code>
intent and a <code>PURCHASE_STATE_CHANGED</code> intent.</p>

<p>The <code>PURCHASE_STATE_CHANGED</code> intent contains a notification ID
that your app can use to retrieve the purchase details, including the purchase
token, by sending a standard <code>GET_PURCHASE_INFORMATION</code> request. The
<code>Bundle</code> returned in the call includes an JSON array of order objects
corresponding to subscription (and in-app product) purchases that you can
restore locally.</p>

<p>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 <a
href="{@docRoot}google/play/billing/billing_best_practices.html">Security and
Design</a> document for best practices for maintaining the security of your
data.</p>

<h2 id="validity">Checking Subscription Validity</h2>

<p>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.</p>

<p>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:</p>

<ul>
  <li><em>In-app Billing Notifications</em>. 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.</li>
  <li><em>Google Play Android Developer API</em>. 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 <code>purchaseToken</code> on your
  backend servers. For more information, see <a href="#play-dev-api">Google Play
  Android Developer API</a>, below.</li>
</ul>

<p>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:</p>

<ul>
  <li>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.</li>
  <li>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.</li>
  <li>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.</li>
  <li>On the client side, your app can continue to update the server whenever the
  purchase state changes, storing the state locally.</li>
</ul>

<p>If you are using both notifications and the Google Play Android Developer API to validate subscriptions, we recommend the following:</p>

<ul>
  <li>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.</li>
  <li>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.</li>
</ul>

<p>If necessary, you can also use a <code>RESTORE_TRANSACTIONS</code> request to retrieve a record of all managed and in-app products purchased by the user, which you can then store locally. However, using <code>RESTORE_TRANSACTIONS</code> on a regular basis is not recommended because of performance impacts.</p>

<p>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.</p>

<p class="table-caption"><strong>Table 1.</strong> Summary of purchaseState
values for subscription purchases, as received with a
<code>PURCHASE_STATE_CHANGED</code> intent.</p>

<table>
<tr>
<th>State</th><th>purchaseState Value</th><th>Comments</th>
</tr>
<tr>
<td>Purchased successfully</td><td><code>0</code></td><td>Sent at original purchase only (not at recurring billing cycles).</td></tr>
<td>Cancelled</td><td><code>1</code></td><td>Sent at original purchase only if the purchase has failed for some reason. </td></tr>
<td>Refunded</td><td><code>2</code></td><td>The purchase was refunded.</code></td></tr>
<td>Subscription expired</td><td><code>3</code></td><td>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. 
</td></tr>
</table>


<h2 id="viewstatus">Letting the User Cancel or View Subscriptions</h2>

<p>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.</p>

<p>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.</p>

<p>To do this, create an intent with the <a 
href="{@docRoot}reference/android/content/Intent.html#ACTION_VIEW">ACTION_VIEW</a>
action and include the <code>market://</code> URI (rather than the <code>http://</code>
URI) of your app's details page. Here’s an example:</p>

<pre style="pretty-print">Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id=com.example.app"));
startActivity(intent);</pre>

<p>For more information, see 
  <a href="{@docRoot}distribute/googleplay/promote/linking.html">Linking to Your Products</a>.</p>

<h2 id="purchase-state-changes">Recurring Billing, Cancellation, and Changes In Purchase State</h2>

<p>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 <em>until the subscription expires because of
non-payment or user cancellation</em>. </p>

<p>Over the life of a subscription, your app does not need to initiate any
recurring billing events &mdash; those are all handled by Google Play and they
are transparent to your application if billing is successful.</p>

<p>When the user cancels a subscription during an active billing cycle, Google
Play <em>does not</em> 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". </p>

<p>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".</p>

<p>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.</p>

<h2 id="modifying">Modifying Your App for Subscriptions</h2>

<p>For subscriptions, you make the same types of modifications to your app as
are described in <a
href="{@docRoot}google/play/billing/v2/billing_integrate.html#billing-implement">
Modifying your Application Code</a>.</p>

<p>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.</p>