📜 ⬆️ ⬇️

In-app Billing Subscriptions from server side

In-app Billing Subscriptions (subscriptions) allow you to automate the withdrawal of funds from the user's account for applications developed for Android. This tool is a great assistant in the task of increasing the monetization of applications. In general, the scheme of work with subscriptions is as follows:

  1. User buys a subscription to some of the app's bun
  2. In case of a successful purchase, the application receives the data of the user's order, in particular the transaction identifier and the subscription sale token, and sends them to the server
  3. The server verifies the signature of the order on Google Play, controls the uniqueness of the transaction, determines the completion time of the subscription, and charges the required benefits.
  4. Upon completion of the subscription, the server can determine the renewal and, if successful, continue to accrue benefits


The article presents the steps to provide server support for the monetization tool for In-App Billing version 2.

')
To obtain subscription data from the server, Google suggests using the Google Play Android Developer API . The API provides only two methods:



If you try to get data on some subscription myapp.month.test of the com.myapp application (by the way, all the matches of the tokens, products, identifiers and keys presented below are random. To repeat the examples, please use your account data), the result will be as follows:

$ wget 'https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription' --2013-02-11 18:24:01-- https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription Resolving www.googleapis.com (www.googleapis.com)... 173.194.71.95, 2a00:1450:4010:c04::5f Connecting to www.googleapis.com (www.googleapis.com)|173.194.71.95|:443... connected. HTTP request sent, awaiting response... 401 Unauthorized Authorization failed. 


To successfully invoke subscription methods, an API access token is required. The latter can be obtained by performing authorization via the OAuth2 protocol using secret data from the console . Identify during authorization in various ways. It is important that in order to get access tokens to the methods of interaction with subscriptions, you need to introduce yourself as a web application using the data in the Client ID for web applications section:

image

Authorization by the web application is carried out in the following sequence
  1. Open link in browser accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher
    &response_type=code&access_type=offline
    &redirect_uri=http://example.com/oauth2callback
    &client_id=someclientid.apps.googleusercontent.com
    Here, among other things, pay attention to the access type offline , the benefits of which will become apparent later.
  2. Login
  3. Allow your account to access the requested resources on a screen similar to the one presented.

    image
  4. Remember code from backward link example.com/oauth2callback?code=4/AuthoRIZ4ti0nC0De
  5. Send authorization request

     $ wget https://accounts.google.com/o/oauth2/token --post-data 'code=4/AuthoRIZ4ti0nC0De&client_id=someclientid.apps.googleusercontent.com&client_secret=c1iEnT5eCReT&redirect_uri=http://example.com/oauth2callback&grant_type=authorization_code' -O - --2013-02-11 18:31:20-- https://accounts.google.com/o/oauth2/token Resolving accounts.google.com (accounts.google.com)... 173.194.71.84, 2a00:1450:4010:c04::54 Connecting to accounts.google.com (accounts.google.com)|173.194.71.84|:443... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [application/json] Saving to: `STDOUT' [<=> ] 0 --.-K/s { "access_token" : "ya29.ACCeSs-T0keN", "token_type" : "Bearer", "expires_in" : 3600, "refresh_token" : "1/rEfRE5hT0KeN" [ <=> ] 127 --.-K/s in 0s 2013-02-11 18:31:20 (16.6 MB/s) - written to stdout [127] 


  6. The access_token field represents an API access token. It provides the ability to receive subscription data for 3600 seconds, as indicated in expires_in . After this time, the token, unfortunately, will cease to act
  7. If suddenly for some reason (well, you never know, you have forgotten) you will need the access token again, you can get the authorization code again and repeat the request.

     $ wget https://accounts.google.com/o/oauth2/token --post-data 'code=4/0thER-AuthoRIZ4ti0nC0De&client_id=someclientid.apps.googleusercontent.com&client_secret=c1iEnT5eCReT&redirect_uri=http://example.com/oauth2callback&grant_type=authorization_code' -O - --2013-02-11 18:31:20-- https://accounts.google.com/o/oauth2/token Resolving accounts.google.com (accounts.google.com)... 173.194.71.84, 2a00:1450:4010:c04::54 Connecting to accounts.google.com (accounts.google.com)|173.194.71.84|:443... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [application/json] Saving to: `STDOUT' [<=> ] 0 --.-K/s { "access_token" : "ya29.neW-ACCeSs-T0keN", "token_type" : "Bearer", "expires_in" : 3573, [ <=> ] 127 --.-K/s in 0s 2013-02-11 18:31:20 (16.6 MB/s) - written to stdout [127] 




As a result, Google provides a new access token, but without an update token. All tokens obtained in this way are valid to fulfill requests, but they will not be updated. To as rarely as possible perform the procedure described above, and at least somehow automate the process of checking subscriptions, I recommend taking the following actions:

  1. Save access to any storage
  2. Save the access token update token in the most secure location. By the way, he is available to us in access type offline . If the time of validity of the backward reference code is limited, there is no data update token limit. In my practice by itself, it has not yet been completed
  3. When the access token becomes outdated, which can be recognized by the already familiar response with the characteristic HTTP 401 code for calls to subscription methods, you must send an update request for the access token to

     $ wget 'https://accounts.google.com/o/oauth2/token' --post-data 'refresh_token=1/rEfRE5hT0KeN&client_id=someclientid.apps.googleusercontent.com&client_secret=c1iEnT5eCReT&grant_type=refresh_token' -O - --2013-02-11 19:33:13-- https://accounts.google.com/o/oauth2/token Resolving accounts.google.com (accounts.google.com)... 173.194.71.84, 2a00:1450:4010:c04::54 Connecting to accounts.google.com (accounts.google.com)|173.194.71.84|:443... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [application/json] Saving to: `STDOUT' [<=> ] 0 --.-K/s { "access_token" : "ya29.rEFre5hED-ACCeSs-T0keN", "token_type" : "Bearer", "expires_in" : 3600 [ <=> ] 127 --.-K/s in 0s 2013-01-11 19:33:14 (18.7 MB/s) - written to stdout [127] 'refresh_token = $ wget 'https://accounts.google.com/o/oauth2/token' --post-data 'refresh_token=1/rEfRE5hT0KeN&client_id=someclientid.apps.googleusercontent.com&client_secret=c1iEnT5eCReT&grant_type=refresh_token' -O - --2013-02-11 19:33:13-- https://accounts.google.com/o/oauth2/token Resolving accounts.google.com (accounts.google.com)... 173.194.71.84, 2a00:1450:4010:c04::54 Connecting to accounts.google.com (accounts.google.com)|173.194.71.84|:443... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [application/json] Saving to: `STDOUT' [<=> ] 0 --.-K/s { "access_token" : "ya29.rEFre5hED-ACCeSs-T0keN", "token_type" : "Bearer", "expires_in" : 3600 [ <=> ] 127 --.-K/s in 0s 2013-01-11 19:33:14 (18.7 MB/s) - written to stdout [127] = c1iEnT5eCReT & grant_type = refresh_token' -O - $ wget 'https://accounts.google.com/o/oauth2/token' --post-data 'refresh_token=1/rEfRE5hT0KeN&client_id=someclientid.apps.googleusercontent.com&client_secret=c1iEnT5eCReT&grant_type=refresh_token' -O - --2013-02-11 19:33:13-- https://accounts.google.com/o/oauth2/token Resolving accounts.google.com (accounts.google.com)... 173.194.71.84, 2a00:1450:4010:c04::54 Connecting to accounts.google.com (accounts.google.com)|173.194.71.84|:443... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [application/json] Saving to: `STDOUT' [<=> ] 0 --.-K/s { "access_token" : "ya29.rEFre5hED-ACCeSs-T0keN", "token_type" : "Bearer", "expires_in" : 3600 [ <=> ] 127 --.-K/s in 0s 2013-01-11 19:33:14 (18.7 MB/s) - written to stdout [127] 


  4. Update access token in the same repository


Having secured the receipt of fresh tokens if necessary, you can safely check-cancel subscriptions purchased by users. Example of receiving subscription data:

 $ wget 'https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN' -O - --2013-02-11 19:50:21-- https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN Resolving www.googleapis.com (www.googleapis.com)... 173.194.71.95, 2a00:1450:4010:c04::5f Connecting to www.googleapis.com (www.googleapis.com)|173.194.71.95|:443... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [application/json] Saving to: `STDOUT' [<=> ] 0 --.-K/s { "kind": "androidpublisher#subscriptionPurchase", "initiationTimestampMsec": "1357909784285", "validUntilTimestampMsec": "1360588184285", "autoRenewing": true } [ <=> ] 167 --.-K/s in 0s 2013-02-11 19:50:21 (30.7 MB/s) - written to stdout [167] 


Description of fields can be found in the documentation for the request

Note the discrepancy between the values ​​of initiationTimestampMsec and validUntilTimestampMsec:



I guess Google reserves an additional 6 hours to debit a client’s account. In case of problems when writing off, periodically tries to repeat. In this particular case, according to the publication, the money was written off from the client ~ in 2013-02-11 17:10. By the way, Facebook subscriptions are trying to write off funds up to 4 days after the end of the payment period. But iTunes is starting to write off the money the day before the end of the payment period so that by the time the subscription is completed, it will be known for sure whether it has been extended or not.

If there is something wrong with the request data, Google responds with 400 HTTP code. In some cases, an incorrect request results in HTTP 404.

 $ wget 'https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/not.myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN' -O - --2013-02-11 19:58:24-- https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/not.myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN Resolving www.googleapis.com (www.googleapis.com)... 173.194.71.95, 2a00:1450:4010:c04::5f Connecting to www.googleapis.com (www.googleapis.com)|173.194.71.95|:443... connected. HTTP request sent, awaiting response... 400 Bad Request 2013-02-11 19:58:25 ERROR 400: Bad Request. 


Sample cancellation:

 $ wget 'https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription/cancel?access_token=ya29.rEFre5hED-ACCeSs-T0keN' --post-data '' -O - --2013-02-11 20:01:16-- https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription/cancel?access_token=ya29.rEFre5hED-ACCeSs-T0keN Resolving www.googleapis.com (www.googleapis.com)... 173.194.71.95, 2a00:1450:4010:c04::5f Connecting to www.googleapis.com (www.googleapis.com)|173.194.71.95|:443... connected. HTTP request sent, awaiting response... 204 No Content 


Note the HTTP response code 204, and the empty response body indicates a successful cancellation of the subscription. If you repeat the request for receiving data by subscription, you can verify this.

 $ wget 'https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN' -O - --2013-02-11 20:08:47-- https://www.googleapis.com/androidpublisher/v1/applications/com.myapp/subscriptions/myapp.month.test/purchases/subsubsubscripscription?access_token=ya29.rEFre5hED-ACCeSs-T0keN Resolving www.googleapis.com (www.googleapis.com)... 173.194.71.95, 2a00:1450:4010:c04::5f Connecting to www.googleapis.com (www.googleapis.com)|173.194.71.95|:443... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [application/json] Saving to: `STDOUT' [<=> ] 0 --.-K/s { "kind": "androidpublisher#subscriptionPurchase", "initiationTimestampMsec": "1357909784285", "validUntilTimestampMsec": "1360588184285", "autoRenewing": false } [ <=> ] 167 --.-K/s in 0s 2013-02-11 20:08:49 (23.5 MB/s) - written to stdout [167] 


Please note that the end date of the subscription has not changed, but the automatic rollover was turned off.
To integrate subscriptions into an application, I suggest the following server support system architecture:

image

Description:



If someone is going to implement subscriptions, I suggest two libraries:

Source: https://habr.com/ru/post/160911/


All Articles