Appland Subscription API
Appland Subscription API
Appland Subscription API
henrik.lewander@appland.se
Table of Content
Appland Subscription API 1
Table of Content 1
Implementation overview 2
System Overview 3
Mandatory parts 5
Web page for Subscribe and Login 5
GET: Status 9
GET: Event 11
Optional parts 13
DELETE: Cancel 13
POST: Subscribe 15
FAQ 18
Revision history 24
Appland Subscription API
Public
Implementation overview
To have a fully functional integration with Appland, you will need to implement the mandatory parts of
Appland Subscription API. These will enable end-user to subscribe to the club and play games.
Mandatory parts
● Web page for Login and Subscribe
The web page will be used both from an Android WebView in the Club App and from a browser so
make sure the design is responsive.
● GET Status
Endpoint called from Appland’s backend to check if a user is eligible for the Club. You will also
include the timestamp for the next renewal so we can cache the result.
● GET Event
Endpoint called from Appland’s backend to bulk fetching status for multiple users. We will use this to
update the cached status in our system and for analyzing user behavior such as churn.
● End-user accounts
Appland will need to be able to test the complete user journey as part of the verification and
regression during initial integration but also after the club has gone live. Test accounts must be
provided for both staging and production.
Optional parts
● DELETE Cancel subscription
Endpoint called from Appland’s backend to unsubscribe a user. The Club App already includes a
user interface for canceling subscription. You may provide an web url where we will send users to
unsubscribe. In this case you will handle the user interface.
● POST Start subscription
Makes it possible for Appland to verify your Subscription Engine with an automatic test.
Prerequisites
● Shared secrets and Subscription ID will be provided by Appland when integration is started.
● Data passed between your system and Appland’s will be signed to prevent unauthorized access.
● The API is REST based and data is passed as JSON. Don’t forget to use “Content-Type:
application/json”.
● A User ID that can be shared between your system and Appland’s needs to be defined. This could
be MSISDN, email or a hash if the real user id can’t be shared.
● Timestamp is always in UTC.
2
Appland Subscription API
Public
System Overview
3
Appland Subscription API
Public
4
Appland Subscription API
Public
Mandatory parts
5
Appland Subscription API
Public
1. User open the club app and clicks install on a game. The Android Club App will open a webview
(internal web browser) to your web page:
http://www.yourendpoint.com/your-custom-uri?subscription=APPS_CLUB_1&cal
lback=http://applandendpoint.example.com/api/session/event/verify-subscr
iption-widget
subscription will be the ID to identify the subscription club.
callback will be determined by Appland's platform for each request.
2. On the web page you authenticate and login the user by using Header Enrichment, OTP (PIN Flow),
email login or any other method you need.
a. When the user is logged in you redirect the user to the callback url and pass a token. How to
generate the token is described in the section "Generate Token".
b. Appland will acquire the user ID from the token and verify the subscription with a call to
“GET: Status” to your backend. If the user is eligible, the download will start.
3. The game starts to download.
6
Appland Subscription API
Public
Generate Token
To generate a token you shall encode a JSON string containing user, timestamp, key and signature. The
generated token shall be passed in a query parameter called “token” either to the URI you receive from
Appland or to the one specified in the query parameter “callback”. The encoding shall be Base 64 according
to specification RFC4648 chapter Base 64 Encoding with URL and Filename Safe Alphabet.
Please note that the callback URI can already have one or more query parameters that needs to be kept.
The complete URL with the generated result token when using the “callback”:
http://applandendpoint.example.com/api/verify?ott=1ClWAOkfFQG61sK2vymoHsr3L9l
TXI5h&token=eyJ1c2VyIjoiMTIyMjE4NzQzODQiLCJ0aW1lc3RhbXAiOjE0ODg1MzQxMTMsImtle
SI6ImV4YW1wbGUtY29tcGFueS1hcHBsYW5kIiwic2lnbmF0dXJlIjoiZmhVUDVOMmRBbW9RX25nZG
NsNDk1aXM5eEEta2xSdnA1U1hidV9lS3Y3MCJ9
7
Appland Subscription API
Public
8
Appland Subscription API
Public
GET: Status
Get the current status of the subscription for a user and if the user is still active or has cancelled the
subscription.
Method: GET
Endpoint: https://www.yourdomain.com/api/subscription/<subscription-id>/<user>
Parameter:
● subscription: String, Subscription ID to identify the subscription if more than one subscription can
exist.
● user: String, user ID to identify the user, described in prerequisites.
Result shall be given with HTTP status code and JSON encoded response body.
● 200 OK:
Content-Type: application/json
User is active and eligible for subscription service, respond with the following body:
○ user: String, User ID of the user in question.
○ nextRenewal: Integer, Unix Timestamp in seconds that describes when the next scheduled
renewal shall occur. If next renewal is not known nextRenewal shall be NULL. This
parameter is important to be able to cache the result in the Appland platform. If this is not
specified, Appland platform will call partner platform a lot to query for user statuses.
○ isEligible: Boolean, define as true if the user is allowed in any form to use/access content in
the Subscription. If false, the user will be prevented from using the service and forced to
renew or resubscribe before using the service.
○ numberOfProfiles (optional) Integer, defines the number of profiles (e.g. family members)
that can be created in this subscription. Default: 1.
1 or missing: Personal account.
2 or more: Family account.
○ numberOfConcurrentSessions (optional) I nteger, defines the number of concurrent
sessions (game plays) per complete user account. Default: 1.
● 401 Unauthorized:
Invalid signature.
● 404 Not Found:
Can be any one of:
○ User does not exist and has never been subscribed.
○ User has canceled the subscription (DELETE) and the current active period has expired.
9
Appland Subscription API
Public
○ Subscription provider has decided to remove the user e.g. due to payment issue.
No further action to renew the subscription shall be taken until user manually resubscribes.
● Other error:
All other errors shall be handled as temporary issues and might be retried later on.
Example request
To generate the signature in the request the following example secret was used:
“tyCLoQykxPssf2vlFlSpiLToDh6h8EF2”. Instructions for adding query parameters including signature can
be found under Security & Signing for server endpoints.
Request
https://www.yourdomain.com/api/subscription/TEST_GAMES_CLUB/461234567890?key=example-compan
y-appland&time=1488534113&signature=DqdNNYXs6fjThZzVPK0iGgxyxIY8rkro3OIRSflfxeQ
Response
{"user":"461234567890", "nextRenewal": 1486373326, "isEligible": true, "numberOfUsers": 4,
"numberOfConcurrentUsers": 1}
10
Appland Subscription API
Public
GET: Event
Used to bulk list all recent changes of subscriptions, including subscribe and cancel for all users. The
endpoint shall return a list of events ordered with oldest first and newest last. All events are final, i.e. no
event should be changed or deleted. This call is used by Appland to update subscription status for many
users at once to increase the performance of the platform and lower the load on your platform. It is
additionally used to get analytics.
Method: GET
Endpoint: https://www.yourdomain.com/api/events/<subscription>/<startTimestamp>/<offset>/<limit>
Parameter:
● subscription: String, Subscription ID to identify the subscription if more than one subscription type
can exist.
● startTimestamp: Integer, Unix Timestamp in seconds. Specifies the oldest element to fetch
(including this timestamp).
● offset: Integer, offset number of records from the startTimestamp. Normally 0.
● limit: Integer, maximum number of elements to fetch in this request.
Result shall be given with HTTP status code and JSON encoded response body.
● 200 OK:
Content-Type: application/json
Response body shall be JSON encoded array with zero or more elements in the following format:
○ isEligible: Boolean, if user is eligible to use the service at that moment. The last value will
override any previously set value independent on the type of event.
○ event: String, type of event. More events might be added as needed.
■ SUBSCRIBE: User has started a new subscription.
■ BILLED_SUCCESS: User has been billed successfully. Currency and amount shall
be included, see below.
■ BILLED_FAILURE (optional): User has been tried to be billed but failed. Currency
and amount shall be included, see below.
■ CANCEL (optional): User has manually cancelled the subscription.
■ SUBSCRIPTION_END (optional): Subscription has ended. A subscription can end
after too many billed failures or when subscription period ends after the user has
cancelled the subscription manually.
○ timestamp: Integer, Unix Timestamp in seconds when the event occurred.
○ user: String, User ID of the user in question.
○ currency: String, Currency name of the transaction according to ISO 4217. Set to null if
event is not BILLED_SUCCESS or BILLED_FAILURE.
○ amount: Float, The actual amount the user is billed including tax in above currency. Decimal
point format (e g 3.14). Set to null if event is not BILLED_SUCCESS or BILLED_FAILURE.
○ nextRenewal: Integer, Unix Timestamp in seconds that describes when the next scheduled
renewal shall occur. If next renewal is not known nextRenewal shall be NULL. This
11
Appland Subscription API
Public
parameter is important to be able to cache the result in the Appland platform. If this is not
specified, Appland platform may call partner platform a lot to query for user statuses.
○ numberOfProfiles (optional) Integer, defines the number of profiles (e.g. family members)
that can be created in this subscription. Default: 1.
1 or missing: Personal account.
2 or more: Family account.
○ numberOfConcurrentSessions (optional) I nteger, defines the number of concurrent
sessions (game plays) per complete user account. Default: 1.
Example:
{"events":[
{"isEligible":true,"event":"SUBSCRIBE","user":"012345789","currency":NULL","amount":
NULL,"timestamp":1480323607,"nextRenewal": 1486373326, "numberOfProfiles": 4,
"numberOfConcurrentSessions": 4},
{"isEligible":true,"event":"BILLED_SUCCESS","user":"012345789","currency":"USD","amount"
:3.14,"timestamp":1480323607,"nextRenewal": 1486373326, "numberOfProfiles": 4,
"numberOfConcurrentSessions": 4},
{...}]}
● 401 Unauthorized:
Invalid signature.
● Other error:
All other errors shall be handled as temporary issues and might be retried later on.
Example request
To generate signature in the request the follow secret was used: “tyCLoQykxPssf2vlFlSpiLToDh6h8EF2”.
Instructions for adding query parameters including signature can be found under Security & Signing for
server endpoints.
Request
https://www.yourdomain.com/api/events/TEST_GAMES_CLUB/0/0/250?key=example-company-appland&ti
me=1488534113&signature=tY6TDKuf15DFeYDziMl8sa2t0bgaJa0V-JjBlhypXFs
Response
{"events":[{"isEligible":true, "event":
"BILLED_SUCCESS","user":"012345789","currency":"USD","amount":3.14,",":1480323607},
{"isEligible":true, "event":
"BILLED_SUCCESS","user":"112345789","currency":"USD","amount":3.14,"timestamp":1480323707},{"isEli
gible":true, "event":
"BILLED_SUCCESS","user":"212345789","currency":"USD","amount":3.14,"timestamp":1480323807}]}
12
Appland Subscription API
Public
Optional parts
DELETE: Cancel
Cancel an ongoing subscription for the user.
Method: DELETE
Endpoint: https://www.yourdomain.com/api/subscription/<subscription>/<user>
Parameter:
● subscription: String, Subscription ID to identify the subscription if more than one subscription can
exist.
● user: String, User ID to identify the user, described in prerequisites.
Result shall be given with HTTP status code and JSON encoded response body.
● 200 OK:
User has been successfully unsubscribed.
● 202 Accepted:
User was already unsubscribed.
● 401 Unauthorized:
Invalid signature.
● 404 Not found:
Can be any one of:
○ User does not exist and has never been subscribed.
○ User has canceled the subscription (DELETE).
○ Subscription provider has decided to remove the user e.g. due to payment issue.
No further action to renew the subscription shall be taken until user manually resubscribe.
● 422 Unprocessable Entity:
User can't be unsubscribed, JSON body with "message" describe in more detail why the user can't
be unsubscribed.
Example:
{"message":"User can't cancel before period has ended."}
● Other error:
All other errors shall be handled as temporary issues and might be retried later on.
13
Appland Subscription API
Public
Example request
To generate the signature in the request the following example secret was used:
“tyCLoQykxPssf2vlFlSpiLToDh6h8EF2”. Instructions for adding query parameters including signature can
be found under Security & Signing for server endpoints.
Request
https://www.yourdomain.com/api/subscription/TEST_GAMES_CLUB/461234567890?key=example-compan
y-appland&time=1488534113&signature=rPeTDeVgysxcrY-uZpQnK-ejFDJ1sgRAqhg5pO4jUug
Response
Status code: 200
14
Appland Subscription API
Public
POST: Subscribe
Start a new Subscription for a user in the Subscription Service
Method: POST
Endpoint: https://www.yourdomain.com/api/subscription/<subscription>/<user>
Parameter:
● subscription: String, Subscription ID to identify the subscription club. Useful if partner have more
than one club, e g kids club & games club. Example: "PARTNER_KIDS_CLUB_1".
● user: String, User ID to identify the user, described in prerequisites.
Result shall be given with HTTP status code and JSON encoded response body.
● 201 Created:
User has been subscribed successfully.
● 401 Unauthorized:
Invalid signature.
● 402 Payment Required:
User can not be subscribed, JSON body with "message" describe in more detail way user can't be a
subscribed.
Example:
{"message":"No money on pre paid card."}
● 409 Conflict:
User is already subscribed to the requested Subscription ID.
● Other error:
All other errors shall be handled as temporary issues and might be retried later on.
Example request
To generate the signature in the request the following example secret was used:
“tyCLoQykxPssf2vlFlSpiLToDh6h8EF2”. Instructions for adding query parameters including signature can
be found under Security & Signing for server endpoints.
Request
https://www.yourdomain.com/api/subscription/TEST_GAMES_CLUB/461234567890?key=example-compan
y-appland&time=1488534113&signature=nFuYfZ1EdjnnYLayyd9Oc4yVXz4YB7Zzs2R138kmseE
Response
Status code: 201
15
Appland Subscription API
Public
16
Appland Subscription API
Public
17
Appland Subscription API
Public
FAQ
● The game is not downloaded and our web page for signing up is appearing again when we
click on install.
18
Appland Subscription API
Public
19
Appland Subscription API
Public
The URI scheme without signing is as follow (line breaks may occur):
sms:[number]?body=[body]&success=[success-uri]&failure=[failure-uri]&sender_0=[sender-
id]&sender_1=[sender-id]
● Optional [body]
Body of the SMS text that shall be sent.
● Optional [success-uri]
HTTP URI the Android Client will navigate to in the webview if SMS was successfully sent.
● Optional [failure-uri]
HTTP URI the Android Client will navigate to in the webview if it was an error when sending SMS
(example: user denies Android Client to send SMS).
● Optional [sender-id]
A list of sender ids that will be searched for when waiting for a response SMS. Each item in the list
will be sent as different items: sender_0, sender_1 etc. If set, the Club App will wait for an SMS that
matches [sender-id] to be received. The body of the message will be added to the [success-uri]
as a query param with key [sms-body]. If no SMS was received that matches [sender-id] within
30 seconds, the Club App will redirect to [failure-uri].
This will send an SMS to phone number 123456789 with the text "SUBSCRIBE GAMES_CLUB".
20
Appland Subscription API
Public
This will send an SMS to phone number 123456789 with the text "SUBSCRIBE GAMES_CLUB" and on:
● success then navigate the webview to https://example.com/success
● failure then navigate the webview to https://example.com/failure
This will send an SMS to phone number 123456789 with the text "SUBSCRIBE GAMES_CLUB" and will
wait for an incoming SMS message from any of these senders: “38742”, “Billing Provider” or “3535”.
● If an SMS was received matching any of the senders, then navigate the webview to
https://example.com/success?sms-body=Please%20reply%20with%20OK%20to%20start%20s
ubscription
● If the SMS was not sent and/or no SMS was received matching the senders then navigate the
webview to https://example.com/failure
Can be used to automatically pick up the code in PIN flow (OTP) case. When the link is triggered the Club
App will start listening for incoming SMS matching these senders: “38742”, “Billing Provider” or “3535”.
● If an SMS was received matching any of the senders, then navigate the webview to
https://example.com/success?sms-body=Your%20code%20is%20x3mF9
● If the SMS was not sent or no SMS was received matching the senders then navigate the webview
to https://example.com/failure
21
Appland Subscription API
Public
To avoid the need for the user to login each time “My Account” is opened, Appland will pass a token
containing the user id. If a token is not available your page can use your own session cookie. Your “My
Account” page will need to support these cases:
1. New user where no token or cookie are available.
2. Already signed in user where token is known by Appland.
3. Already signed in user where your cookie is present but not token.
The format of the token is identical to the token that you pass in the subscribe flow, which is a Base 64
encoded json object. To get the user id you just decode the Base 64 token. Important is that you make sure
to verify that the signature is correct. Example of the decoded token:
{
"user":"12221874384",
"timestamp": 1488534113,
"key": "example-company-appland",
"signature": "fhUP5N2dAmoQ_ngdcl495is9xA-klRvp5SXbu_eKv70"
22
Appland Subscription API
Public
// Validate signature
$signature = $object->signature;
$secret = 'tyCLoQykxPssf2vlFlSpiLToDh6h8EF2';
$timestamp = $object->timestamp;
$data = $user . "\r\n" . $timestamp;
$hmac = hash_hmac('sha256', $data, $secret, TRUE);
$encoded = base64_encode($hmac);
$calculated_signature = rtrim(strtr($encoded, '+/', '-_'), '=');
23
Appland Subscription API
Public
Revision history
Revision Date Author Comment
PD 170206 Jesper Sjovall Adding HTTP status code 202 for Unsubscribe
and clarify the status code 404 for
Unsubscribe.
PE1 170303 Simon Arvidsson Added code example for calculating signature
PH1 170427 Jesper Sjövall Added PIN flow in web view for user
identification
24
Appland Subscription API
Public
PL1 170614 Henrik Lewander Added currency & amount to GET Event.
QP1 171019 Simon Arvidsson Added code examples in C#. Fixed issue in
code examples for PHP.
25
Appland Subscription API
Public
26