Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Appland Subscription API

Download as pdf or txt
Download as pdf or txt
You are on page 1of 26

Henrik Lewander - CTO Appland AB

henrik.lewander@appland.se

Appland Subscription API

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

Security & Signing for server endpoints 16

FAQ 18

Appendix 1: Development help tool 19

Appendix 2: SMS send & receive in WebView 20

Appendix 3: Linking to your My Account page 22

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

Figure 1: Architecture overview

Figure 2: User Flow Signup

3
Appland Subscription API
Public

Figure 3: User Flow login from Club App

4
Appland Subscription API
Public

Mandatory parts

Web page for Subscribe and Login


The web page for Subscribe and Login is the one displayed in Step 2 in Figure 2 and Figure 3. Below are
detailed information about each step in the two flows.

Figure 2: ​User Flow Signup​.

1. User clicks on a banner/sms/email that links to your web page.


2. On the web page you authenticate and ​sign up​ the user by using Header Enrichment, OTP (PIN
Flow), email login or any other method you need.
a. When the user is signed up you redirect the user to
<domain-provided-by-appland>/api/session/resource/verify​ 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 welcome page will be displayed.
3. The welcome page with the instruction on how to install the apk is displayed. The user will
automatically be signed in here.

5
Appland Subscription API
Public

Figure 3: User Flow login from Club App

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.

Result Token before base64 encoding:


{
"user":"12221874384",
"timestamp": 1488534113,
"key": "example-company-appland",
"signature": "Iv6c_Z1RsmiG5M_MkA-v1oIBWRcV5_fulgsaiBT_MjE"
}

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

Calculating the signature


The JSON result token shall be signed to be able to verify the integrity of the data. Use the same method as
in Appland Subscription API. Use HMAC with SHA256.
1. Get current timestamp in Unix Timestamp in seconds as {timestamp}
2. Build the payload data from the result:
{user} + "\r\n" + {timestamp}
3. Calculate HMAC with SHA256 and base64 encode with specification RFC4648 chapter "​Base 64
Encoding with URL and Filename Safe Alphabet​"
4. Build the final JSON result by creating JSON object with "user", "key", "timestamp" from step 1 and
"signature" from step 3.

See next page for example code.

7
Appland Subscription API
Public

Example: Calculating signature in PHP


$user = '12221874384';
$timestamp = 1488534113;
$data = $user . "\r\n" . $timestamp;
$secret = 'tyCLoQykxPssf2vlFlSpiLToDh6h8EF2';
$hmac = hash_hmac('sha256', $data, $secret, TRUE);
$encoded = base64_encode($hmac);
$signature = rtrim(strtr($encoded, '+/', '-_'), '=');
echo $signature // Outputs: fhUP5N2dAmoQ_ngdcl495is9xA-klRvp5SXbu_eKv70

Example: Calculating signature in C#


using System;
using System.Security.Cryptography;
using System.Text;

public class Program


{
public static void Main()
{
string user = "12221874384";
int timestamp = 1488534113;
string message = user +"\r\n" +timestamp;
string secret = "tyCLoQykxPssf2vlFlSpiLToDh6h8EF2";

byte[] keyByte = new ASCIIEncoding().GetBytes(secret);


byte[] messageBytes = new ASCIIEncoding().GetBytes(message);
byte[] hash = new HMACSHA256(keyByte).ComputeHash(messageBytes);
String.Concat(Array.ConvertAll(hash, x => x.ToString("X2")));
string signature = Convert.ToBase64String(hash)
.TrimEnd('=').Replace('+', '-').Replace('/', '_');
Console.WriteLine(signature); // Outputs: fhUP5N2dAmoQ_ngdcl495is9xA-klRvp5SXbu_eKv70
}
}

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.

Example active subscriber:


{"user":"461234567890", "nextRenewal": 1486373326, "isEligible": true,
"numberOfProfiles": 4, "numberOfConcurrentSessions": 4}

● 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

Security & Signing for server endpoints


All requests to the API need to be signed to verify the integrity of the requests. Use HMAC with SHA256.
1. Get current timestamp in Unix Timestamp in seconds as {timestamp}
2. Build the payload data from the request:
○ For request: Subscribe, Status and Cancel use following pattern:
{HTTP-POST method} + "\r\n" + {subscription} + "\r\n" + {user} + "\r\n" +
{timestamp}
○ For request: Event use the following pattern:
{HTTP-POST method} + "\r\n" + {subscription} + "\r\n" + {startTimestamp}
+"\r\n" + {offset} + "\r\n" + {limit} + "\r\n" + {timestamp}
3. Calculate HMAC with SHA256 and base64 encode with specification RFC4648 chapter "​Base 64
Encoding with URL and Filename Safe Alphabet​"
4. Add the following query parameters to the request:
○ key = ServiceID, described in prerequisites.
○ time = {timestamp} Timestamp from step 1.
○ signature = Base64 encoded signature from step 3.

For validating a signature on incoming request:


1. Perform step 1 to 4 with the exception to use same timestamp taken from the request.
2. Verify that the calculated signature is equal to the signature included in the request, otherwise the
request shall fail with 401.
3. Verify timestamp does not differ more than 5 minute from current time, otherwise the request shall
fail with 401.

Example: Calculating signature in PHP


$httpMethod = 'POST';
$subscription = 'TEST_GAMES_CLUB';
$user = '12221874384';
$timestamp = 1488534113;
$data = $httpMethod . "\r\n" . $subscription . "\r\n" . $user . "\r\n" . $timestamp;
$secret = 'tyCLoQykxPssf2vlFlSpiLToDh6h8EF2';
$hmac = hash_hmac('sha256', $data, $secret, TRUE);
$encoded = base64_encode($hmac);
$signature = rtrim(strtr($encoded, '+/', '-_'), '=');
echo $signature; // Outputs: hw0V5TL3u6Fdy5CDcE0g3nYKS-nVGWYuTGtVH51asVk

16
Appland Subscription API
Public

Example: Calculating signature in C#


using System;
using System.Security.Cryptography;
using System.Text;

public class Program


{
public static void Main()
{
string method = "POST";
string secret = "tyCLoQykxPssf2vlFlSpiLToDh6h8EF2";
string subscription = "TEST_GAMES_CLUB";
string user = "12221874384";
int timestamp = 1488534113;
string message = method +"\r\n" +subscription +"\r\n" +user +"\r\n" +timestamp;
byte[] keyByte = new ASCIIEncoding().GetBytes(secret);
byte[] messageBytes = new ASCIIEncoding().GetBytes(message);
byte[] hash = new HMACSHA256(keyByte).ComputeHash(messageBytes);
String.Concat(Array.ConvertAll(hash, x => x.ToString("X2")));
string signature = Convert.ToBase64String(hash)
.TrimEnd('=').Replace('+', '-').Replace('/', '_');
Console.WriteLine(signature); // Outputs: hw0V5TL3u6Fdy5CDcE0g3nYKS-nVGWYuTGtVH51asVk
}
}

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.

There could be one of two errors:


○ Appland can not verify that the user is eligible. Make sure that you have implemented GET:
Status correctly in the developer tool and that the user is eligible.
○ You have not appended the token correctly to the callback url. There is already a parameter
on the url so you can not append it like “...?token=ksaAS..”, you have to do
“..&token=ksaAS..” (note the &-sign)

18
Appland Subscription API
Public

Appendix 1: Development help tool


To verify the signature implementation go to this url:
https://static2.appland.se/security-integration-for-subscription-api/index.html

19
Appland Subscription API
Public

Appendix 2: SMS send & receive in WebView


During the subscribe flow in the Club App it is possible to trigger a silent SMS to be sent from the device.
This is useful if the subscription needs to be started by an MO text message.

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]

NOTE: All parameters shall be urlencoded UTF-8.


Parameters
● Optional​ ​[number]
Is the short/long phone number the SMS shall be sent to and shall only contain digits and "+".
Not setting [number] is applicable for PIN flow where the Club App will listen for an incoming SMS
with the PIN. In this case ​[sender-id]​ is mandatory.

● 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​].

Example without callback URI


<a href="sms:123456789?body=SUBSCRIBE%20GAMES_CLUB">Send SMS</a>

This will send an SMS to phone number ​123456789​ with the text "​SUBSCRIBE GAMES_CLUB​".

Example with callback URI


<a href="sms:123456789?body=SUBSCRIBE%20GAMES_CLUB&success=https%3A%2F%2Fexample.com%2
Fsuccess&failure=https%3A%2F%2Fexample.com%2Ffailure">Send SMS</a>

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

Example with sender


<a​ ​href="sms:123456789?body=SUBSCRIBE%20GAMES_CLUB&success=https%3A%2F%2Fexample.com%2F
success&failure=https%3A%2F%2Fexample.com%2Ffailure&sender_0=38742&sender_1=Billing%20
Provider&sender_2=3535">Send SMS</a>

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

Example without number but with sender (PIN flow)


<a href="sms:?success=https%3A%2F%2Fexample.com%2Fsuccess&failure=https%3A%2F%2Fexampl
e.com%2Ffailure&sender_0=38742&sender_1=Billing%20Provider&sender_2=3535">Detect PIN
code</a>

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

Appendix 3: Linking to your My Account page


You may implement a web page where users can view all information about their account but it is not
mandatory. The url for the “My Account” web page is configured in Appland backend and opened inside the
Club App in a WebView.

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

Example url when token is known


https://www.yourdomain.com/my-account?subscription=EXAMPLE_GAMES_CLUB&token=eyJ1c2VyIj
oiMTIyMjE4NzQzODQiLCJ0aW1lc3RhbXAiOjE0ODg1MzQxMTMsImtleSI6ImV4YW1wbGUtY29tcGFueS1hcHBs
YW5kIiwic2lnbmF0dXJlIjoiZmhVUDVOMmRBbW9RX25nZGNsNDk1aXM5eEEta2xSdnA1U1hidV9lS3Y3MCJ9

Example url without token


https://www.yourdomain.com/my-account?subscription=EXAMPLE_GAMES_CLUB&token=

Example: Parsing token and validating signature in PHP


$token =
"eyJ1c2VyIjoiMTIyMjE4NzQzODQiLCJ0aW1lc3RhbXAiOjE0ODg1MzQxMTMsImtleSI6ImV4YW1wbGUtY29tcGFueS1hcH
BsYW5kIiwic2lnbmF0dXJlIjoiZmhVUDVOMmRBbW9RX25nZGNsNDk1aXM5eEEta2xSdnA1U1hidV9lS3Y3MCJ9";
$decoded_token = base64_decode($token);
$object = json_decode($decoded_token);
$user = $object->user;

// 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, '+/', '-_'), '=');

if($signature !== $calculated_signature) {


echo 'Invalid signature';
} else {
echo 'Signature OK';
}

23
Appland Subscription API
Public

Revision history
Revision Date Author Comment

PA1 161125 Jesper Sjövall Created

A 161129 Henrik Lewander Updated and approved

PB1 170110 Jesper Sjövall Adding extra resource.

B 170117 Henrik Lewander Updated and approved

PC1 170206 Jesper Sjövall Added isEligible to ​GET: Status

C 170206 Henrik Lewander Approved

PD 170206 Jesper Sjovall Adding HTTP status code 202 for Unsubscribe
and clarify the status code 404 for
Unsubscribe.

D 170207 Henrik Lewander Updated and approved

PE1 170303 Simon Arvidsson Added code example for calculating signature

E 170303 Henrik Lewander Approved

PF1 170328 Simon Arvidsson Changed type of timestamp from string to


integer in Event data.

PF2 170404 Henrik Lewander Added offset to GET Event.

F 170404 Henrik Lewander Approved

PG1 170424 Simon Arvidsson Added Architecture Overview Diagram.


Included email and header enrichment
appendixes.

G 170425 Henrik Lewander Cleanup and approved

PH1 170427 Jesper Sjövall Added PIN flow in web view for user
identification

H 170428 Henrik Lewander Approved

24
Appland Subscription API
Public

I 170504 Henrik Lewander Updated architecture diagram and approved

PJ1 170516 Jesper Sjövall In Appendix 4, renamed "appland-token" to


"token" and specified callback URI.

J 170516 Henrik Lewander Approved

PK1 170607 Tobias Ekblom In Appendix 4, clarified documentation for step


7 and 8.

K 170607 Henrik Lewander Approved

PL1 170614 Henrik Lewander Added currency & amount to GET Event.

idL 170614 Henrik Lewander Approved

PM1 170816 Simon Arvidsson Reworked to be easier to follow

M 170818 Henrik Lewander Cleanup and approved

PN1 170828 Simon Arvidsson Added appendix for linking to My Account

PN2 170904 Simon Arvidsson Added protocol for SMS receiving

N 170904 Henrik Lewander Approved

PO1 170907 Simon Arvidsson Clarified all data types

O 170907 Henrik Lewander Approved

PP1 171012 Simon Arvidsson Added nextRenewal time to GET Event

P 171012 Henrik Lewander Approved

QP1 171019 Simon Arvidsson Added code examples in C#. Fixed issue in
code examples for PHP.

Q 180201 Henrik Lewander Cleanup and approved

RP1 180419 Tobias Ekblom Added new pictures.

P 180419 Henrik Lewander Approved

QP1 180807 Jesper Sjövall Change endpoint for API.

25
Appland Subscription API
Public

PS1 181016 Csaba Veres Added numberOfProfiles and


numberOfConcurrentSessions to GET
subscription status API response.

26

You might also like