The Token Request Flow
For businesses, the most fundamental part of Token is creating a TokenRequest
. This is how you will request payments and data.
What are Smart Tokens?
Smart tokens are at the core of TokenOS.
The diagram below shows the components of a smart token:
There are two kinds of smart tokens:
Transfer tokens authorize payments, or the transfer of assets or funds from the payer to the payee, and function as programmable money.
Example:
A business (the payee) requests a member (the payer) to authorize a smart token to pay for an online purchase: “Allow Business XYZ to initiate a payment from my account at Iron Bank to pay €224 for order 79262212.“
Access tokens authorize the access of member data. The type of access depends on the conditions of the access token, and can include “who,” “what,” and “how” that data can be accessed.
Example:
A member (the grantor) authorizes a service (the grantee) to access and aggregate their account information.
Tokens are created by the Token app. To begin this process you must create a TokenRequest
which will be sent to the app. The created token will mostly consist of details specified on the TokenRequest
.
1. Creating a Token Request
A TokenRequest
consists of the following fields:
to |
Recipient of the token, specifiable by member ID or alias |
description |
Description of the payment or access. While it is optional to populate the description field, it MUST match the description field when you redeem the token. For example, if you leave description empty in the request it must be empty when you redeem. If you populate the description in the request with the string “book”, you must populate the description in the redemption with the same string, “book”. |
redirectUrl |
Defines the callback URL where your server will initiate redemption of the token |
refId |
Reference ID for the token |
callbackState |
Developer-specified string that allows state to be persisted between the the request and callback phases of the flow. |
actingAs |
Optional: Entity that the to member is acting as a proxy for |
destinationCountry |
Optional: |
userRefId |
Optional: Used to track a member claimed by a TPP |
token_expiration |
Optional: Used to set an expiration date and time for a request. May not be available for all banks. Format using the UTC timestamp in milliseconds. |
customizationId |
Optional: Used for customizing the UI of the web-app |
bankId |
Optional: Specifies you would like to bypass the Token bank selection UI. See below for more details |
from |
Optional: Specify if you would like to bypass the Token email input UI. See below for more details |
sourceAccountId |
Optional: The account ID of the source bank account |
receiptRequested |
Optional: True if a receipt should be sent to the from member’s default receipt location |
A RequestId
will be returned on submitting the TokenRequest
. This ID references the stored TokenRequest
and is used to verify that the conditions of the request are unchanged. It must be included in the URL when redirecting the user to Token to obtain authorization.
NOTE:The member creating the TokenRequest
must match the member that redeems the token later in the flow.
Additionally, a TokenRequest
expires within 20 minutes. If you do not receive a response within this time, you will have to create another request.
Requesting Transfer Tokens
To make a request for a transfer token, use the static TokenRequest.transferTokenRequestBuilder(amount, currency)
method, where amount
is the total lifetime amount of the token, and currency
is the 3 letter (ISO4217) currency code (e.g. “EUR”).
When the amount is passed, Token will scale it to 4 decimal places, using HALF_EVEN rounding.
You can set the following fields in the transfer token request builder:
transfer_destinations |
Destination account(s) for the payment. This will typically contain an IBAN for SEPA transfer, or Sort Code and Account Number for Faster Payments. Details about the TransferDestination object can be found here |
Though optional, Token recommends setting a reference ID (not to be confused with the requestId
) in the tokenBuilder
, which is included in the request to transfer the funds. This allows businesses the ability to easily reconcile transactions against payments they receive.
IMPORTANT: Due to restrictions of particular payment rails, this value cannot exceed 18 characters in length.
Future-Dated Payments (Part of the TransferTokenRequest
)
Another optional, but important field in the TransferRequest
is executionDate
which is used for requesting a future-dated single payment. Set this in the transfer token request builder with the dates in the format indicated in the table below:
execution_date |
Sets the date for executing a single future payment. ISO 8601: YYYY-MM-DD or YYYYMMDD. |
Example of creating a Token Request for a Transfer Token
const tokenRequest = Token.createTransferTokenRequest(10.00, 'EUR')
.setToMemberId(payee.memberId())
.setFromAlias('EMAIL', 'payerEmail@gmail.com')
.setDescription('Book Purchase')
.setRedirectUrl('https://token.io/callback')
.setBankId('iron');
TokenRequest request = TokenRequest.transferTokenRequestBuilder(100., "EUR")
.setRefId(UUID.randomUUID().toString().replace("-", ""))
.setToMemberId(payee.memberId())
.setDescription("Book purchase") // optional description
.setRedirectUrl("https://token.io/callback") // callback URL
.setFromAlias(Alias.newBuilder()
.setValue("payer-alias@token.io") // user alias
.setType(Alias.Type.EMAIL)
.build())
.setBankId("iron") // bank ID
.setCsrfToken(generateNonce()) // nonce for CSRF check
.build();
// Store token request
return payee.storeTokenRequestBlocking(request);
TokenRequest request = TokenRequest.TransferTokenRequestBuilder(100, "EUR")
.SetRefId(Util.Nonce())
.SetToMemberId(payee.MemberId())
.SetDescription("Book purchase") // optional description
.SetRedirectUrl("https://token.io/callback") // callback URL
.SetFromAlias(new Alias
{
Value = "payer-alias@token.io",
Type = Alias.Types.Type.Email
})
.SetBankId("iron") // bank ID
.SetCsrfToken(Util.Nonce()) // nonce for CSRF check
.Build();
// Store token request
return payee.StoreTokenRequestBlocking(request);
Requesting Standing Order Tokens
The standing order token is used for recurring payments-that is, payments that happen at regular intervals. Standing orders can be executed in the following frequencies: daily (DAIL), weekly (WEEK), twice-weekly (TOWK), monthly, (MNTH), twice-monthly (TOMN), quarterly (QUTR), semiannually (SEMI), or yearly (YEAR).
You must specify the startDate
and in most cases, the endDate
of each standing order. For example, if you create a standing order from, 2019-01-01
to 2019-02-28
with the frequency TOMN
, it is an order for 4 payments, 2 for the month of January and 2 for February. If you do not specify an end date, the payment will happen indefinitely.
To make a request for a standing order token, use the static TokenRequest.standingOrderBuilder()
method and set the parameters. Those listed in the table below as required must be included in the request. The others are optional.
member |
Required. Payer of the token |
amount |
Required. Amount per charge of the standing order token |
currency |
Required. Currency for each charge in the standing order |
frequency |
Required. ISO 20022 code for the frequency of the standing order: DAIL, WEEK, TOWK, MNTH, TOMN, QUTR, SEMI, YEAR |
startDate |
Required. Start date of the standing order: ISO 8601 YYYY-MM-DD or YYYYMMDD. |
endDate |
Required. End date of the standing order: ISO 8601 YYYY-MM-DD or YYYYMMDD. If not specified, the standing order will occur indefinitely. |
transferDestination |
Required. Destination account for the payment. This will typically contain an IBAN for SEPA transfer, or Sort Code and Account Number for Faster Payments. Details about the TransferDestination object can be found here. |
ProviderTransferMetadata |
Optional. Adds metadata for a specific provider. |
destinationCountry |
Optional. Sets the destination country in order to narrow down the country selection in the web-app UI. |
The TPP is responsible for initiating the standing order, but it is the responsibility of the bank to execute each payment. The bank will not initiate a standing order until the token has been redeemed.
const tokenRequest = Token.createStandingOrderTokenRequest(
10, 'EUR', 'MNTH', '2020-02-15', '2021-02-15',
[{
faster_payments: {
sort_code: 123456,
account_number: 12345678,
},
}])
.setDescription('Monthly music subscription')
.setRedirectUrl('https://token.io/callback')
.setFromAlias('EMAIL', 'grantorEmail@gmail.com')
.setToMemberId(grantee.memberId())
.setBankId('iron');
Customization
The Token Web App can be white-labelled through the use of our SDK; specifically, the logo and colour scheme. To receive permission to white-label, follow the instructions below:
Set your business name. Call
setProfile
on your business member and setdisplay_name_first
to your business name.Send Token your member ID requesting permission to customize.
Once permission has been granted, call
createCustomization
. This will return acustomizationId
.Include this customization ID in your
TokenRequest
.
Parameters
- Logo
Setting your logo picture as a
BlobPayload
Blob.Payload logo = Payload.newBuilder()
.setName("arbitrary name")
.setAccessMode(AccessMode.PUBLIC)
// type of image
.setType("image/png")
// your logo in ByteString format
.setData(bs)
.setOwnerId(merchantMember.memberId())
// your merchant member’s ID
.build();
- Colours
A map from string to string where the keys denote CSS variables and the values denote the colours in hex format #AARRGGBB (AA being the alpha channel).
Here’s the list of color variables Token uses and their default values:
Variables | Default Value |
---|---|
color-dark |
#FF1A262C |
color-secondary |
#FF50546C |
color-body-text |
#FF525F7F |
color-buttons |
#FF757F99 |
color-outlines |
#FFB8BFCA |
color-dividers |
#FFE9EAF0 |
color-background |
#FFFCFCFC |
color-light |
#FFFFFFFF |
color-primary |
#FF05A5F0 |
color-delete-error |
#FFEB5757 |
color-disabled-input |
#FFC4C4C4 |
color-success-green |
#FF16AB2B |
color-transparent |
transparent |
The values you specify in your map will override the default values for the given key.
NOTE: The most important one is colour-primary
, as this is responsible for our overall blue theme. It is likely you will only need to customize this value.
The following parameters have been DEPRECATED:
- (deprecated)
consentText
IMPORTANT: If you are using your own NCA license, request to hide the Token consent screen completely and handle consent before the user is directed to Token. The merchant will need to contact Token directly to use this feature.
(deprecated)
name
(deprecated)
appName
Relevant API:
Requesting Access Tokens
To make a request for an access token, use the static TokenRequest.accessTokenRequestBuilder(resources)
method, where resources
is a list of ResourceType
s.
ACCOUNTS |
Request access to basic information about a user’s account, such as accountType , and accountId |
BALANCES |
Request access to a user’s account balances, or if singular, the balance of one account. |
TRANSACTIONS |
Request access to the transaction history of a user’s account(s). |
TRANSFER_DESTINATIONS |
Request access to the payment rails which can be used to transfer money to a user’s account. e.g. the BIC and account number for a SWIFT account. |
getAccount()
can be called on the user’s account with accountId
to access other ResourceType
s.
Example of creating a Token Request for an Access Token
const tokenRequest = Token.createAccessTokenRequest(['ACCOUNTS', 'BALANCES'])
.setDescription('Account and balance access')
.setRedirectUrl('https://token.io/callback')
.setFromAlias('EMAIL', 'grantorEmail@gmail.com')
.setToMemberId(grantee.memberId())
.setBankId('iron');
TokenRequest request = TokenRequest.accessTokenRequestBuilder(ACCOUNTS, BALANCES)
.setRefId(UUID.randomUUID().toString().replace("-", ""))
.setToMemberId(grantee.memberId())
.setRedirectUrl("https://token.io/callback") // callback URL
.setFromAlias(Alias.newBuilder()
.setValue("grantor-alias@token.io") // user alias
.setType(Alias.Type.EMAIL)
.build())
.setBankId("iron") // bank ID
.setCsrfToken(generateNonce()) // nonce for CSRF check
.build();
return grantee.storeTokenRequestBlocking(request);
TokenRequest request = TokenRequest.AccessTokenRequestBuilder(ResourceType.Accounts, ResourceType.Balances)
.SetRefId(Util.Nonce())
.SetToMemberId(grantee.MemberId())
.SetRedirectUrl("https://token.io/callback") // callback URL
.SetFromAlias(new Alias
{
Value = "grantor-alias@token.io",
Type = Alias.Types.Type.Email
})
.SetBankId("iron") // bank ID
.SetCsrfToken(Util.Nonce()) // nonce for CSRF check
.Build();
return grantee.StoreTokenRequestBlocking(request);
Confirmation of Available Funds
CAF is used to check that an account has enough funds available when the user wants to make a payment.
CBPIIs (Card Based Payment Instrument Issuer)
If you are a CBPII, create an AccessTokenRequest
using the createFundsConfirmationRequest()
function. You must have the bankId
and a BankAccount
object to do this. Continue with the rest of the request flow. The bank should not prompt the user to select their account, but give consent for the account that has already been provided.
At the end of the flow, call confirmFunds()
function, providing the accountId
, amount
, and currency
. The response of this function is a boolean indication if the user has enough funds in the specified account. The account number will be found in the returned token token.payload.access.resources[0].funds_confirmation.account_id
.
NOTE: This is NOT the same as the account number provided in the original Token Request.
PISPs
Payment Service Providers should create a standard TransferTokenRequest
and use the setConfirmFunds()
function to indicate that confirming available funds should be performed before the transfer.
IMPORTANT: Only certain banks support CAF to be used this way. If a particular bank does not support CAF, this field will be ignored.
Cross Border Payments
When making a cross border payment, set the TransferDestinations
in the storeTokenRequest(tokenRequest)
to any of the international payment types, such as SEPA
or SWIFT
.
Set TransferDestinations in the
TransferTokenRequest
TransferDestination sepa = TransferDestination
.newBuilder()
.setSepa(TransferDestination.Sepa
.newBuilder()
.setBic(generateNonce())
.setIban(generateNonce())
.build())
.build();
TokenRequest tokenRequest = TokenRequest.transferTokenRequestBuilder(250, "EUR")
.setToMemberId(payee.memberId())
.setDescription("Book purchase")
.addDestination(sepa)
.setRedirectUrl("https://tpp-sample.com/callback")
.build()
String requestId = payee.storeTokenRequestBlocking(tokenRequest);
NOTE: If there are multiple TransferDestinations set, the domestic payment system will be preferred over international payment system.
2. Redirect to TokenOS to Obtain Authorization
After creating a TokenRequest
, construct a URL that directs to the Token web app.
tokenRequestUrl |
URL to TokenOS that guides your user through the necessary steps to create a token |
requestId |
ID that references a previously created TokenRequest stored in TokenOS |
After constructing the tokenRequestUrl
, direct your front-end to visit that URL. There are two different ways you can do this:
Server-side Redirect
Send an HTTP POST
to your back-end to initiate the token request creation in Step 1, and redirect using an HTTP 302
.
Binding the Token Button
You can also create a Token Button that either redirects to, or creates a pop-up of, the Token web app in the user’s browser.
The redirect option is default for both the mobile and web browsers, though for web browsers, there is an option of using a pop-up to bring a better UX to your users.
To get both redirect and pop-up, use the token.createRequest
function, which will automatically choose the best option depending on the user’s device.
Pop-up/Redirect Function
token.createRequest(
function(redirect) { // non-async function for redirect
// do some non-async actions here and execute
// redirect callback with url immediately after
redirect(url);
},
function(done, errorCallback) { // async function for popup
// do any async actions here and
// execute callback with url when done
done(url);
},
);
Creating a Redirect-Only Button
function createRedirectButton() {
var token = new window.Token({
env: 'sandbox',
});
// get button placeholder element
var element = document.getElementById(elementId);
// create the button
var button = token.createTokenButton(element, {
label: "Redirect Token Quick Checkout",
});
// create TokenController to handle messages
var tokenController = token.createController();
// bind the Token Button to the Token Controller when ready
tokenController.bindButtonClick(
button, // Token Button
token.createRequest(
function(redirect) {
redirect("/transfer", {
amount: 4.99,
currency: "EUR",
description: "Book",
});
}
),
function(done) { // redirect token request function
// go to transfer
done("/transfer"
+ "?amount=4.99¤cy=EUR"
+ "&description=Book");
},
function(error) { // bindComplete callback
if (error) throw error;
// enable button after binding
button.enable();
},
);
}
Creating a Button for both Pop-up and Redirect
function createPopupButton() {
var token = new window.Token({
env: 'sandbox',
});
// get button placeholder element
var element = document.getElementById(elementId);
// create the button
var button = token.createTokenButton(element, {
label: "Popup Token Quick Checkout",
});
// create TokenController to handle messages
tokenController = token.createController({
onSuccess: function(data) { // Success Callback
// build success URL
var successURL = '/redeem-popup'
+ '?data='
+ window.encodeURIComponent(
JSON.stringify(data));
// navigate to success URL
window.location.assign(successURL);
},
onError: function(error) { // Failure Callback
throw error;
},
});
// bind the Token Button to the Token Controller when ready
tokenController.bindButtonClick(
button, // Token Button
token.createRequest(
function(redirect) { // redirect
redirect('/getTokenRequestUrl', {
amount: 4.99,
currency: "EUR",
description: "Book",
});
},
function(done, errorCb) { // optional async request for popup
fetch('/transfer', {
method: 'POST',
headers: {'Content-Type':
'application/json; charset=utf-8'},
body: '{"amount":4.99,"currency":'
+ '"EUR","description":"Book"}',
})
.then(function(res) {
// extract body text from response
return res.text();
})
.then(function(tokenRequestUrl) {
// Execute callback
// with the Token Request URL
done(tokenRequestUrl);
})
.catch(function(e) {
errorCb('Error fetching Token Request URL');
});
},
),
function(error) { // bindComplete callback
if (error) throw error;
// enable button after binding
button.enable();
},
{ // options
desktop: 'POPUP',
}
);
}
For more information, please see the sample code below.
Example Token Request URL
https://web-app.token.io/request-token/rq:42w7yzgwJtN9fQVx78McJzEKiyU9:5zKtXEAq?lang=en
Receiving the Callback
After the user is redirected to the request URL, they will be prompted to agree to the Token terms and conditions. After accepting, they will be taken to their bank where they will authenticate themselves and authorize the payment or data access transaction. Once the token has been created, TokenOS will redirect to the callback URL specified in the TokenRequest
.
There are three variations: See Payment Flows for detailed information.
a. Receiving the Token ID
You will receive a token ID in the callback, which you can then redeem.
Example Token Request Callback URL (token ID)
https://merchant-site.com/callback?signature=signature&state=state&tokenid=tokenid
See SDK Methods for information about the above parameters.
NOTE: If you are integrating with a mobile app, you can initiate the token request creation in a WebView in Create a Token Request so the redirect is also in the form of an HTTP 302
.
When your server-side code receives an HTTP GET
request at the callback URL, it should use the Token SDK parseTokenRequestCallbackUrl
method to extract the tokenId
, and verify the signature and CSRF token.
In the case of the pop-up method, the request to the server could be an HTTP POST and contain JSON formatted data. It should use the Token SDK parseTokenRequestCallbackParams
method to extract the tokenId
, and verify the signature and CSRF token.
Example of Parsing a Token Request Callback URL
return await Token.parseTokenRequestCallbackUrl(callbackUrl, csrfToken);
tokenClient.parseTokenRequestCallbackUrlBlocking(callbackUrl, csrfToken);
.FlatMap(mem => tokenClient.ParseTokenRequestCallbackUrl(callbackUrl, csrfToken.Value)
// get the token and check its validity
.FlatMap(callback => mem.GetToken(callback.TokenId))
Example of Parsing the Token Request Callback JSON Data
var result = await Token.parseTokenRequestCallbackParams(JSON.parse(data), req.session.csrfToken);
tokenClient.parseTokenRequestCallbackParamsBlocking(data, csrfToken);
.FlatMap(mem => tokenClient.ParseTokenRequestCallbackParams(queryParams, csrfToken.Value)
// get the token and check its validity
.FlatMap(callback => mem.GetToken(callback.TokenId))
b. Receiving the Transfer ID
Some banks don’t support the separation of the authorization and the initiation of a transfer. They initiate the transfer immediately after the user authorizes it, in which case you would receive a transfer ID directly in the callback.
Example Token Request Callback URL (transfer ID)
https://merchant-site.com/callback?signature=signature&state=state&tokenId=tokenId&transferId=transferid&transferStatus=transferstatus
3. Redeeming Tokens
Note: If you receive a transfer ID in the callback, the transfer token has already been redeemed and no further action is required. See Payment Flows for more details.
Tokens must be redeemed to create a payment or access account information.
Redeeming Transfer Tokens
Use the server-side redeemToken
method to create a Transfer
. This initiates a payment (via one of Token’s supported rails: e.g. SEPA) from the user’s bank.
tokenId |
ID that refers to a Token stored in TokenOS |
refID |
Optional. Client-supplied reference ID used for deduplicating requests. If not provided, Token will generate one |
transferToken |
A token retrieved from TokenOS using a tokenId |
transfer |
Represents a payment. The status of the transaction can be retrieved using this object |
Redeeming a Transfer Token
const transferToken = await payee.getToken(tokenId);
// Destination for sending the funds
const destination = {
account: {
sepa: {
iban: '123',
},
},
};
// Payer redeems the token, getting a transfer
return await payee.redeemToken(
transferToken,
5,
'EUR',
'lunch',
[destination],
cartId);
Token transferToken = payee.getTokenBlocking(tokenId);
// Set token destination
TransferDestination tokenDestination = TransferDestination
.newBuilder()
.setToken(TransferDestination.Token.newBuilder()
.setMemberId(payee.memberId())
.setAccountId(accountId))
.build();
// Payee redeems a transfer token.
// Money is transferred to a payee bank account.
Transfer transfer = payee.redeemTokenBlocking(
transferToken,
tokenDestination,
// if refId not set, transfer will use token ref ID:
cartId);
// Set token destination
TransferDestination tokenDestination = new TransferDestination
{
Token = new TransferDestination.Types.Token
{
MemberId = payee.MemberId(),
AccountId = accountId
}
};
// Payee redeems a transfer token.
// Money is transferred to a payee bank account.
Transfer transfer = payee.RedeemTokenBlocking(
transferToken,
tokenDestination,
// if refId not set, transfer will use token refID:
cartId);
The returned transfer object (or its ID) can be used to retrieve the status of the transaction.
transferId |
ID that refers to a transfer stored in TokenOS |
transfer |
A transfer retrieved from TokenOS using a transferId or created with redeemToken |
status |
Represents a transaction status (processing, success, or failed) |
source |
Optional: contains the source account identifier (such as IBAN) and customer data. |
Example of retrieving the status of a transfer
payee.getTransfer(transferId).then(function(transfer) {
const status = transfer.status;
});
Transfer transfer = payee.getTransfer(transferId);
TransactionStatus status = transfer.getStatus()
Transfer transfer = payee.GetTransferBlocking(transferId).Result;
TransactionStatus status = transfer.Status;
Redeeming Standing Order Tokens
To redeem a Standing Order Token, call the redeemStandingOrder
method with the tokenId
. Upon redemption, you get a StandingOrderSubmission
object. This object’s ID can be used to get the updated status of the standing order by calling getStandingOrderSubmission
.
Redeeming a Standing Order Token
const standingOrderToken = await payee.getToken(tokenId);
// Payer redeems the token, getting a standing order submission
return await payee.redeemStandingOrderToken(standingOrderToken.id);
Redeeming Access Tokens
Use the server-side call forAccessToken
to get a Representable
object that represents the user. You can then call getBalance
and getTransactions
on the Representable
object to retrieve account information. These methods will only succeed if the specified access token has sufficient permissions.
getTransactions
will return a paged list of transactions. Set the limit
to any number less than 100 and offset to null
. The result is a PagedList
class containing a list of transactions (starting with the most recent) and a cursor with a new offset
inside. You can use this new offset
for the next page of transactions.
You can now filter transactions using the startDate
and endDate
parameters. startDate
is an optional inclusive lower bound of the transaction booking date and endDate
is an optional inclusive upper bound of the transaction booking date. Each new page of transactions will use the same date range specified in the original request. (Currently this feature is only supported in Java. It will soon be available in other languages.)
grantee |
Member to whom the access token was granted |
tokenId |
ID that refers to an access token stored in TokenOS |
customerInitiated |
Optional. Flag to set if access is customer-initiated. This bypasses the TokenOS cache |
balance |
Balance of the account |
Relevant APIs:
- Member.forAccessToken: Apply the access token
- Representable.getAccounts: Use Representable to list grantor’s accounts
- Representable.getAccount: Use Representable to fetch a specific account
Redeeming an Access Token
Representable grantor = grantee.forAccessToken(tokenId, customerInitiated);
List<Account> accounts = grantor.getAccountsBlocking();
// Get the data we want
Money balance = accounts.get(0).getBalanceBlocking(STANDARD).getCurrent();
const representative = grantee.forAccessToken(tokenId);
const accounts = await representative.getAccounts();
if (!accounts) return;
// Get information we want:
const balance0 = await representative.getBalance(accounts[0].id(), config.KeyLevel.LOW);
/* List of accounts from getAccounts: */
[
{
"id": "a:B3EphxmCi9VfcJevZ8Ad9PLMf41onD6ENEp6aHBHFito:5zKcENpV",
"name": "Checking-714790e2-220a-4017-830c-316cefeff371",
"bankId": "iron"
}
]
IRepresentable grantor = grantee.ForAccessToken(tokenId, customerInitiated);
var accounts = grantor.GetAccountsBlocking();
// Get the data we want
Money balance0 = accounts[0].GetBalanceBlocking(Key.Types.Level.Standard).Current;
The forAccessToken
method allows you to specify which access token to use to access information. You should keep track of the association between your access tokens and your users.
Security Considerations
Flow Overview
Since the Token Request Flow is based on OAuth 2.0, it is strongly recommended that you implement protection against CSRF and “OAuth Cut and Paste” (slides). TokenOS helps you mitigate both threats by providing a way to cryptographically bind CSRF tokens to the token ID.
This modifies the above outlined flow by introducing a step before the beginning of the process.
0. Authenticate the Browser | - Front-end authenticates with back-end service and creates a session. |
1. Create a Token Request | - Back-end service generates csrfToken as hash of session cookie, creates a tokenRequest, and constructs tokenRequestUrl - Back-end service responds with HTTP 302 requesting redirect to tokenRequestUrl. |
2. Redirect to TokenOS to obtain authorization | - Front-end visits tokenRequestUrl. - User proceeds through TokenOS steps. - TokenOS redirects to the callback URL hosted by your back-end service |
3. Redeem the Token | - Back-end service parses the callback URL, validates csrfToken, and obtains the tokenId and optional state. - Back-end service uses tokenId to redeem to access account information or create a payment. |
The Smart Token Request Flow is a web-based flow. In order to mitigate the above attacks, the first step is to authenticate your user in a web-based environment. In a typical application the user is already logged in, so this may mean something different depending on if you are working with a website or a mobile application.
A web application does not need to implement anything additional if the user is already authenticated.
A mobile application will need to authenticate the user in the WebView. In order to avoid authenticating user on a separate web page you may want to share sessions between the mobile application and the WebView, or use the existing mobile app session to create a new session in the WebView.
SDK Methods
There are two methods in the Token SDK to help with improving your application security.
String TokenClient.generateTokenRequestUrl(requestId)
Generates a token request URL that can be used to initiate the token request process.
TokenRequestCallback TokenClient.parseTokenRequestCallbackUrl(callbackUrl, csrfToken)
Parses a Token Request callback URL, validates the cryptographic signature binding the csrfToken
, state
, and tokenId
, and returns the tokenId
and state
.
csrfToken |
Unique string bound to the session of the user (e.g. the hash of a session cookie or associated with server-side user session). The csrfToken set in the TokenRequest builder and passed in parseTokenRequestCallbackUrl to validate that the same user session initiated and completed the request.TokenOS suggests that you follow the IETF-recommended mitigation method of binding the CSRF token to the user’s authenticated state (using, for example, a the hash of a session cookie). Note: Sensitive strings (such as sessionCookie ) can be used directly as the csrfToken in the Token SDK since it hashes the token before using it. For more details on CSRF attacks against OAuth 2.0 and mitigation techniques, refer to this rfc.If your application does not have user authentication, you can use Util.generateNonce to generate a secure random string to use as a CSRF token. |
state |
Developer-specified string that allows state to be persisted between the the request and callback phases of the flow. |
IMPORTANT NOTE: The state parameter can contain additional application-specific information, but should not be used to authenticate a user. The authentication must be performed prior to the initiation of the Token Request Flow, and the callback should use the same authenticated session.
Construct the token request URL using either the generateTokenRequestUrl
or generateTokenRequestParams
method from Step 2.
requestId |
ID of the TokenRequest to be used for this request. |
Retrieve the state
when parsing the callback url in Step 3 by passing in the same csrfToken
that was set in the TokenRequest
in Step 1.
callbackUrl |
The callback URL of your backend-service, along with query parameters |
tokenId |
ID of requested token. Can be redeemed for information or payment initiation. |
Example of using
parseTokenRequestCallbackUrl
with a CSRF token
parseTokenRequestCallbackUrl(callbackUrl, csrfToken).then(function(res) {
const tokenId = res.tokenId;
const state = res.innerState;
})
tokenClient.parseTokenRequestCallbackParamsBlocking(data, csrfToken);
.FlatMap(mem => tokenClient.ParseTokenRequestCallbackUrl(callbackUrl, csrfToken.Value)
// get the token and check its validity
.FlatMap(callback => mem.GetToken(callback.TokenId))
Download Sample Code
If you’d like to see a working implementation of the Token Request Flow, check out our samples below:
- Javascript
- Java
- C#
Clone or download the appropriate sample. Each sample contains a reference back-end service implementation and a reference front-end website. Instructions are in the readme file.
Extensions
Token provides optional extensions to further customize the user experience.
Optional: Displaying Your Own Bank Selection UI
By default, the first screen your user will see when they are redirected to TokenOS is a bank selection screen which prompts them to choose which bank to use.
Alternatively, you can choose to present your own bank selection UI. In order to display a selection of banks to the user, use getBanks
to retrieve a list of bank objects. With this method you can filter the banks that you want to display to the user for selection.
When the user has selected their bank from your UI, use the bankId
of that bank in your application when creating the token request.
Using the getBanks
Method
Use the getBanks
method to retrieve a list of Bank
s.
bankIds |
If specified, getBanks returns banks whose id matches any one of the given IDs (case-insensitive). Can be at most 1000 |
search |
If specified, getBanks returns banks that relate to the given search string (case-insensitive). |
country |
If specified, getBanks returns banks whose country matches the given ISO 3166-1 alpha-2 country code (case-insensitive) |
page |
Retrieves the paged results. Defaults to 1 page if not specified |
perPage |
Maximum number of records per page. The max amount is 200. Defaults to 200 if not specified |
sort |
If not specified, getBanks results are sorted by relevance to the search query. If specified, key by which to sort the results. Can only be name. |
provider |
If specified, getBanks returns banks whose provider e.g FinAPI, Token, matches the given provider (case insensitive) |
bankFeatures |
Used to filter banks. Returns a list of banks who meet the set values. These values are listed in the table under Bank Features. |
Bank Features
To filter banks by features, set the bankFeatures
parameter with one or more of the following values:
For example, if you want to filter banks by those who support standing order payments, set bankFeatures
with the supports_standing_order
variable. A list of banks that support this feature will be returned.
supports_send_payment |
Bank allows for initiating payments |
supports_receive_payment |
Bank allows for receiving payments |
supports_scheduled_payment |
Bank supports scheduled payments |
supports_standing_order |
Bank supports standing orders |
supports_bulk_transfer |
Bank supports bulk payments |
requires_one_step_payment |
Bank only supports immediate redemption of transfers |
supports_guest_checkout |
Bank supports guest checkout |
supports_information |
Bank allows for retrieval of information |
supports_balance |
Bank allows for retrieving balances |
supports_ais_guest_checkout |
Bank allows AIS guest checkout |
supports_funds_confirmation |
Bank supports confirmation of funds |
supported_transfer_destination_types |
A list of transfer destination types supported by the bank e.g SEPA, ELIXIR |
supports-transaction-date-filter |
Bank supports filtering transactions by date |
Bank Object
The Bank
object contains the information for a single bank. See the Bank
object fields below:
id |
|
name |
|
provider |
Provider of the bank e.g. FinApi, Token |
country |
The ISO 3166-1 alpha-2 two letter country code in upper case |
identifier |
Optional identifier of the bank. Not guaranteed to be unique across all banks. Example: BLZ for German banks |
logo_uri |
Square bank avatar icon |
full_logo_uri |
Full-size bank icon |
supports_send_payment |
Bank allows for initiating payments |
supports_receive_payment |
Bank allows for receiving payments |
supports_scheduled_payment |
Bank supports scheduled payments |
supports_standing_order |
Bank supports standing orders |
supports_bulk_transfer |
Bank supports bulk payments |
requires_one_step_payment |
Bank only supports immediate redemption of transfers |
supports_guest_checkout |
Bank supports guest checkout |
supports_information |
Bank allows for retrieval of information |
supports_balance |
Bank allows for retrieving balances |
supports_ais_guest_checkout |
Bank allows AIS guest checkout |
supports_funds_confirmation |
Bank supports confirmation of funds |
supported_transfer_destination_types |
A list of transfer destination types supported by the bank e.g SEPA, ELIXIR |
supports-transaction-date-filter |
Bank supports filtering transactions by date |
Example of using
getBanks
Token.getBanks().then(function(banks) {
String bankId = banks[0].id;
})
List<Bank> banks = tokenClient.getBanksBlocking();
String bankId = banks.get(0).getId(); // take the first bank id
PagedBanks banks = tokenClient.GetBanks().Result;
string bankId = banks.Banks[0].Id;
After obtaining the user-selected bankId
, provide it in the TokenRequest
.
Example of specifying a Bank ID in the Token Request
const tokenRequest = Token.createTransferTokenRequest(10, 'EUR')
.setBankId(bankId);
tokenRequest.setBankId(bankId)
var options = new TokenRequestOptions
{
BankId = "iron"
};
When TokenOS displays the TokenRequest
, it will see that a bankId
has been specified and default to that bank instead of displaying the bank selection screen.
Optional: Provide User Alias For a Better UX
In the case of a Token integrated bank, your user may be prompted to provide their alias in order to enable a customer experience that does not involve entering bank credentials.
To provide an even more seamless experience, you have the option of specifying the user’s email address when creating the TokenRequest
.
Example of Specifying the User Email Address in the Token Request
const tokenRequest = Token.createTransferTokenRequest(10, 'EUR')
.setFromAlias({type: 'EMAIL', value: email});
// Create a TokenRequest to be stored
TokenRequest request = TokenRequest.transferTokenRequestBuilder(10, "EUR")
.setFromAlias(Alias.newBuilder()
.setType(Alias.Type.EMAIL)
.setValue(email)
.build());
var tokenRequest = new TokenRequest
{
RequestOptions = new Proto.Common.TokenProtos.TokenRequestOptions
{
From = new TokenMember
{
Alias = new Alias
{
Type = Alias.Types.Type.Email,
Value = email
}
}
}
};
By providing the email, TokenOS will skip the email input screen entirely and ask the user to directly approve the request on their app.
Optional: View Token Terms
Upon receiving a token, you can check the terms of the token and inspect the Token
object returned by retrieving it with the getToken
method.
Example of getting the token
payee.getToken(tokenId).then(function (token) {
// Verify terms of the token
});
Token transferToken = payee.getTokenBlocking(tokenId);
Token transferToken = payee.GetToken(tokenId).Result;
Optional: Creating a Token on Behalf of Another Party
If you have been given permission by Token to do so, you can create a token on behalf of another party. To do that you need to set the actingAs
field on the token payload with the following properties describing the intended recipient:
display_name |
Name of recipient, to be shown to user |
ref_id |
Optional. Your reference ID of the recipient. Opaque to Token |
logo_url |
URL pointing to recipient’s logo |
secondary_name |
Optional. Domain or email of the recipient, to be shown to user along with display_name |
Example of setting the
actingAs
property
// Transfer Token Request
const payload = {
to: {
id: payee.memberId(),
},
actingAs: {
displayName: displayName,
refId: refId,
logoUrl: logoUrl,
secondaryName: secondaryName,
},
transferBody: {
lifetimeAmount: '100.00',
currency: 'EUR',
},
description: 'Book purchase',
redirectUrl: 'https://tpp-website.com/callback',
};
// Access Token Builder
const payload = {
to: {
id: grantee.memberId(),
},
actingAs: {
displayName: displayName,
refId: refId,
logoUrl: logoUrl,
secondaryName: secondaryName,
},
accessBody: {
type: ['ACCOUNTS', 'BALANCES'],
},
redirectUrl: 'https://tpp-website.com/callback',
};
tokenRequest.setActingAs(ActingAs.newBuilder()
.setDisplayName(displayName)
.setRefId(refId)
.setLogoUrl(logoUrl)
.setSecondaryName(secondaryName));
// Transfer Token Builder
var payload = new TokenRequestPayload
{
RefId = tppRefId,
RedirectUrl = "https://tpp-website.com/callback",
To = new TokenMember
{
Id = member.MemberId()
},
ActingAs = new ActingAs
{
DisplayName = displayName,
LogoUrl = logoUrl,
RefId = refId,
SecondaryName = secondaryName
},
Description = "Book purchase",
CallbackState = tppState,
TransferBody = new TokenRequestPayload.Types.TransferBody
{
Amount = "10.0",
Currency = "EUR"
}
};
// Access Token Builder
var payload = new TokenRequestPayload
{
RefId = tppRefId,
RedirectUrl = "https://tpp-website.com/callback",
To = new TokenMember
{
Id = member.MemberId()
},
ActingAs = new ActingAs
{
DisplayName = displayName,
LogoUrl = logoUrl,
RefId = refId,
SecondaryName = secondaryName
},
CallbackState = tppState,
AccessBody = new TokenRequestPayload.Types.AccessBody
{
Type = {types}
}
};
Retrieving and Canceling Tokens
Get Token
You can retrieve an existing token by calling getToken
on the token ID. This way, you can check on the status of the token by viewing the token’s signatures. For example, if the payer or grantor has canceled the token, then a CANCELLED signature will be present on the retrieved Token object.
Example of Retrieving a Token
Token token = member.getTokenBlocking(tokenId);
const transferToken = await payee.getToken(tokenId);
Token token = member.GetTokenBlocking(tokenId);
/* result with canceled token. It has both ENDORSED
and CANCELED payloadSignatures */
{
"token": {
"id": "tt:7ECKSivyfXzQzp8fAw9Z4aRkedPCKfPPuNH1MiprEmig:5zKcENpV",
"payload": {
"version": "1.0",
"refId": "jh3y2pckfore57u4qp747k3xrqmix3i7kp9irtdtjmdixgk3xr",
"issuer": {
"username": "iron"
},
"from": {
"id": "m:2YLns17mwhzbNYMofToaP34kFAYx:5zKtXEAq"
},
"expiresAtMs": "1502303565723",
"transfer": {
"redeemer": {
"id": "m:2X16B4ZTwqejz9YC3so9JDkbXL6d:5zKtXEAq",
"username": "iqzsaszle5shuscndt8wfusorrxj8xrvi5fs0l2q5fdb6e0zfr"
},
"instructions": {
"source": {
"account": {
"token": {
"memberId": "m:2YLns17mwhzbNYMofToaP34kFAYx:5zKtXEAq",
"accountId": "a:GBTQ7DEbbKyo4wKarkkS3NaAMJiMbPnzf595dAELEaWW:5zKcENpV"
}
}
}
},
"currency": "EUR",
"lifetimeAmount": "100",
"pricing": {
"sourceQuote": {
"id": "17aa3c61992f4061bdf04b7bb48eef33",
"accountCurrency": "EUR",
"feesTotal": "0.25",
"fees": [
{
"amount": "0.17",
"description": "Transaction Fee"
},
{
"amount": "0.08",
"description": "Initiation Fee"
}
],
"expiresAtMs": "1502303565723"
},
"instructions": {
"feesPaidBy": "SHARED_FEE",
"fxPerformedBy": "SHARED_FX"
}
}
}
},
"payloadSignatures": [
{
"action": "ENDORSED",
"signature": {
"memberId": "m:2YLns17mwhzbNYMofToaP34kFAYx:5zKtXEAq",
"keyId": "EVknJb1X_wqpyVc8",
"signature": "1fO5tXvfB27P4_lYgerjxTyYX1q-n-TUdTmn0w-awEBuG0ml-6jEhkQFQxnDFTzGHlYVodIaggtxegsSV60lCg"
}
},
{
"action": "ENDORSED",
"signature": {
"memberId": "m:36XFB8Cx257rJb265Fa5FAkrXN7M:5zKtXEAq",
"keyId": "rki_qdKsD9al2BQf",
"signature": "EE3Zk5VF257RFKcWs0JxyrXnkOCXB3EagATzfOSA_q7eltqTq_mUIL-zP8jT2r-vmRPHd_3BBEVTQtxgFuhjDw"
}
},
{
"action": "CANCELLED",
"signature": {
"memberId": "m:2YLns17mwhzbNYMofToaP34kFAYx:5zKtXEAq",
"keyId": "EVknJb1X_wqpyVc8",
"signature": "drkAuAUhQx8m3enpmjyrrv7WSxDmZAPkWOE-AQbSIdyPjOcxKLXNTpKMZpoaRQjNsmAS7uBi9yr1VxnrrSXJDw"
}
},
{
"action": "CANCELLED",
"signature": {
"memberId": "m:36XFB8Cx257rJb265Fa5FAkrXN7M:5zKtXEAq",
"keyId": "rki_qdKsD9al2BQf",
"signature": "ZX6jpS6iax7DOydg1586Nb0SUu631ATolelWU28dcdAwqGlKW6P1fXHXmCF_sVf5Xqy0elLkda18T8Q88-QQDw"
}
}
]
},
"status": "SUCCESS"
}
Relevant APIs:
Canceling Transfer Tokens
You can cancel the transfer token any time. This will cancel any future payments the transfer token has authorized.
IMPORTANT: Previous payments and pending payments CANNOT be reversed or canceled.
Relevant APIs:
Example of Canceling a Transfer Token
// Retrieve a transfer token to cancel.
Token transferToken = payee.getTokenBlocking(tokenId);
// Cancel transfer token.
return payee.cancelTokenBlocking(transferToken);
const transferToken = await payee.getToken(tokenId);
// Payee cancels the token
return await payee.cancelToken(transferToken);
Canceling Access Tokens
As an access token’s grantee, you can cancel an access token at any time. This prevents retrieving future information with the access token.
Relevant APIs:
Example of Canceling an Access Token
// Retrieve an access token to cancel.
Token accessToken = grantee.getTokenBlocking(tokenId);
// Cancel access token.
return grantee.cancelTokenBlocking(accessToken);
// Grantee gets the token to see details
const accessToken = await grantee.getToken(tokenId);
// Grantee cancels the token
return await grantee.cancelToken(accessToken);
Get Transfer Tokens
The Member getTransferTokens
method returns a paged list of transfer tokens for which the member was the payee. A paged list is a list of transfer tokens, starting with the most recently paid one and going back any number of tokens you wish to see. It also includes an offset that can be used to get any number of future transfer tokens.
This code uses the API:
Get Access Tokens
The Member GetAccessTokens
method returns a paged list of access tokens for which the member was the grantee. A paged list is a list of access tokens, starting with the most recently granted one and going back any number of tokens you wish to see. It also includes an offset that can be used to get any number of future access tokens.
Get Standing Orders
AISPs can access a given standing order with the standingOrderId
or a paged-list of standing order records.
Get Standing Orders (Currently available only in Java)
public static List<StandingOrder> redeemStandingOrdersAccessToken(
Member grantee,
String tokenId) {
// Specifies whether the request originated from a customer
boolean customerInitiated = true;
// Access grantor's account list by applying
// access token to the grantee client.
// forAccessToken snippet begin
Representable grantor = grantee.forAccessToken(tokenId, customerInitiated);
List<Account> accounts = grantor.getAccountsBlocking();
// Get the first 5 standing orders
PagedList<StandingOrder, String> standingOrders = accounts.get(0)
.getStandingOrdersBlocking(null, 5, STANDARD);
// Pass this offset to the next getStandingOrders
// call to fetch the next page of standing orders.
String nextOffset = standingOrders.getOffset();
return standingOrders.getList();
}
Timeouts
A timeout condition is encountered when the elapsed time for receiving a response for a service request exceeds its allotted duration; that is, fulfilling the request took longer than the server was prepared to wait.
TPP-configurable Timeouts - Set in milliseconds,
TokenClient.timeoutMs
is the only timeout that can be set by a TPP. The default value for timeoutMs is 10 seconds (10,000 milliseconds). Currently,timeoutMs
is only supported through the Java SDK.Other - All other timeouts are controlled by the Token platform.
Errors
Possible runtime errors thrown by the Token SDKs.
Java and C# Errors
Both the Java and C# SDKs throw the standard gRPC exceptions; for more information, refer to the status codes. You will see the enum names in stack dumps.
Error Enum | Value | Meaning |
---|---|---|
OK | 0 | Operation completed successfully |
CANCELLED | 1 | Operation was canceled (typically by the caller) |
UNKNOWN | 2 | Unknown error; for example, a status value was received from an unknown error-space, or an API call returned an error with incomplete information |
INVALID_ARGUMENTS | 3 | Client specified an invalid argument |
DEADLINE_EXCEEDED | 4 | Deadline expired before operation could complete |
NOT_FOUND | 5 | Requested entity (such as a file or directory) was not found |
ALREADY_EXISTS | 6 | Entity that you attempted to create (such as a file or directory) already exists |
PERMISSION_DENIED | 7 | Caller does not have permission to execute the specified operation |
RESOURCE_EXHAUSTED | 8 | A resource, such as a per-user quota or the file system is out of space, has been exhausted |
FAILED_PRECONDITION | 9 | Operation was rejected because the system is not in a state required for the operation’s execution |
ABORTED | 10 | Operation was aborted, typically due to a concurrency issue |
OUT_OF_RANGE | 11 | Operation was attempted past the valid range; for example, seeking or reading past the end of a file |
UNIMPLEMENTED | 12 | Operation is not implemented or not supported/enabled |
INTERNAL | 13 | Internal error |
UNAVAILABLE | 14 | Service is unavailable, most likely due to a transient condition that might be corrected by retrying |
DATA_LOSS | 15 | Unrecoverable data loss or corruption |
UNAUTHENTICATED | 16 | Request does not have valid authentication credentials for the operation |
JavaScript Errors
The JavaScript SDK throws different types of HTTP errors. All errors are wrapped in an error object, with a message that contains the SDK method that failed, along with the reason for failure.
Token SDKs
Java SDK
To use the Token Java SDK you will need Java Development Kit (JDK) version 8 or later.
To get the Token Java SDK JARs, source code, and instructions for use go to the
token-sdk-tpp Token Artifactory page.
(Choose the latest jar
under token-sdk-tpp
to use the latest version of the SDK.)
- Java (and Android) SDK source repository
- You can find Javadocs for the Token Java SDK here and here.
Maven
- To depend on the SDK, tell Maven to use Token’s artifact repository:
<repositories> ... <repository> <url>https://token.jfrog.io/token/public-libs-release-local/</url> </repository> </repositories>
- Specify the artifact to depend on:
<dependency> <groupId>io.token.sdk</groupId> <artifactId>tokenio-sdk-tpp</artifactId> <version>2.12.23</version> </dependency>
Gradle
- To depend on the SDK, tell Gradle to use Token’s artifact repository:
repositories { ... maven { url 'https://token.jfrog.io/token/public-libs-release-local/' } }
- Specify the artifact to depend on:
compile(group: 'io.token.sdk', name: 'tokenio-sdk-tpp', version: '2.12.23')
JavaScript SDK
The JavaScript SDK builds code usable in Node.js or (via a build tool
like webpack
or browserify
) in the browser.
It uses ES7, but builds code usable in ES6.
Token requires a recent version of npm
and yarn
to build.
To install the npm package: npm install @token-io/tpp
- Javascript SDK source repository
- Package information for Token JS SDK
- You can find the ESDocs for the Token JS SDK here and here.
C# SDK
The SDK is based on C# 7.0. The target framework is .Net Framework 4.5.1. The package can be found on Nuget.
To use the SDK, add the package as a dependency to your project file:
<ItemGroup> <PackageReference Include="Token.SDK.TPP" Version="1.0.8" /> </ItemGroup>
Other Languages
To use a language other than the Token SDK languages (currently Java, JavaScript, and C#, with additional languages to be added), you can use the Token gRPC or HTTP API. Many of the API endpoints require authentication; to authenticate a request, you must compute a cryptographic signature and attach it.
For information about how to do this, contact Token.
Protocol Buffers
Many important data structures are defined as Protocol Buffer messages.
The build process generates Java code from these.
You can learn more about this Java
Code.
A quick start: If you have a Java object based on a protocol buffer definition,
to get the value of the field named tool_name
, there are methods
named getToolName…
; To create a Java object of a class based on
a protocol buffer definition named Tool
, call
Tool.newBuilder().setToolName("Nice tool").build();
.
You can get the newest protocol buffer definitions by downloading the most recent
jars.
For protocol buffers, you want the “regular” jars, not javadoc
or sources
.
You can also see them as web pages.
The sample code below shows the protocol buffer for signature
and how it is constructed in Java.
message Signature {
string member_id = 1;
string key_id = 2;
string signature = 3;
}
Signature signature = Signature.newBuilder()
.setMemberId(memberId)
.setKeyId(signer.getKeyId())
.setSignature(signer.sign(update))
.build();
Many important data structures are defined in terms of Protocol Buffer messages.
For example, smart token signatures are defined in terms of the
message Signature
.
In Token Javascript, the object corresponding to a protocol buffer
message has fields with camel-case names: protocol buffer
foo_bar
becomes Javascript fooBar
.
A protocol buffer string becomes a Javascript string;
a protocol buffer number becomes a Javascript number.
A protocol buffer enum
value becomes a Javascript string;
for example, if an enum
has a value ENDORSED = 1
, in
Javascript, this value is 'ENDORSED'
.
A repeated
protocol buffer field becomes a Javascript array.
A protocol buffer bytes
becomes a Javascript string,
the base64 encoding of those bytes.
You can also see the Token SDK protocol buffers as web pages.
The sample code below shows the protocol buffer for signature
and how it is constructed in JavaScript.
message Signature {
string member_id = 1;
string key_id = 2;
string signature = 3;
}
/* signature */
message Signature {
memberId = "...",
keyId = "...",
signature = "..."
}
Many important data structures are defined as Protocol Buffer messages.
The build process generates C# code from these.
You can learn more about this C#
code.
A quick start: If you have an object based on a protocol buffer definition,
to get the value of a field named tool_name
, get the property named ToolName
.
If that would give you a property whose name collides with the class that owns the property, e.g., Signature
property inside a Signature
class, get the property named Signature_
instead.
The C# SDK fetches the newest protocol buffer definitions when it is built. You can get the newest protocol buffer definitions by downloading the most recent
jars.
For protocol buffers, you want the “regular” jars, not javadoc
or sources
.
You can also see them as web pages.
The sample code below shows the protocol buffer for signature
and how it is constructed in C#.
message Signature {
string member_id = 1;
string key_id = 2;
string signature = 3;
}
Signature signature = new Signature
{
MemberId = Payer.Id,
KeyId = keyId,
Signature_ = signer.Sign(update)
};
Customer Experience
Depending on the underlying bank and its authentication and authorization method, the user experience may vary. The following screens are representative of most cases.
Example UX of a merchant requesting a bank direct payment using Token; the user is visiting the merchant website on a mobile device.
Security
IMPORTANT: Instead of API keys, Token uses digital signatures Public Key Infrastructure so your information is safe and secure.
TokenOS uses PKI (public key infrastructure) to secure communications between the Token cloud and its clients. Public keys are stored in the Token cloud and are publicly shared, requiring valid signatures to be changed. Private keys are never shared. Each Token API invocation is digitally signed by the caller using its private key(s), and the Token cloud and banks verify the API request by using the caller’s public key. The Token SDK greatly simplifies the development process by wrapping the complex PKI in a set of simple interfaces.
TokenOS supports the SCA (strong customer authentication) requirements of PSD2, whereby transfers over a threshold amount require two-factor authentication (2FA). 2FA requires that authentication include any two of the following:
- knowledge- Something only the Token member knows, such as a password or PIN.
- possession- Something the Token member possesses, such as a private key on a mobile device.
- inherence- Something the Token member is, such as a fingerprint or biometric characteristic.
Frequently Asked Questions
Do I need to have an AISP (account information service provider) or PISP (payment initiation service provider) license to use your service?
No, Token has obtained AISP and PISP approval by the FCA in the UK and passported across Europe. Please note than if you require AIS functionality you may be required to be registered as an agent of Token via the FCA. We will assist you with any required formalities please contact us for further information.
Do you have a sandbox that I can test with?
Yes, Token has a full sandbox with model banks for you to test your integration against. In the initial setup steps, point your SDK client to connect to our SANDBOX environment. We recommend you start out with Wood Bank (United Kingdom), contact us to learn more about the other banks we have available.
Can I test against sandboxes hosted by real banks in your network?
When you have completed your integration and have successfully tested against Token’s model banks, you will be able to test with one of the directly integrated partner banks (pursuant to PSD2 regulations). For more details don’t hesitate to contact us.
Can I set up a standing order or recurring payment using your Smart Token technology?
Yes.
Does Token offer categorization options for retrieving transaction history?
Token does not currently offer categorization; we expect to offer this service in the future.
Can I make cross-border payments?
Yes.
What timezones do banks use?
Token uses UTC as the default time zone across all banks.
How do I notify Token of questions or issues I’m encountering?
Contact Us at our support desk, and we’ll get back to you as soon as we can.
Glossary
Term | Definition |
---|---|
2FA | Two-Factor Authentication |
asset owner | Party whose asset is contained in the smart token; for transfer tokens, the payer; for access tokens, the grantor |
access token | Smart token that provides authorization to access information or perform operations |
AISP | Account Information Service Provider; for example, Yodlee and Mint |
API | Application Programming Interface; for example, the underlying functions of the Token Java SDK |
endorsement | digital signature |
grantee | Token member who is authorized by another Token member (grantor) to retrieve information and perform operations on behalf of the grantor |
grantor | Token member who authorizes another Token member (grantee) to retrieve information and perform operations on his/her behalf |
HSM | Hardware Security Module |
payee | Merchant that receives money in exchange for goods |
payer | Consumer (end user or shopper) who makes a purchase and uses Token to pay the payee |
PII | Personally Identifiable Information |
PISP | Payment Initiation Service Provider; that is, online merchants |
PKI | Public Key Infrastructure; rules, datastores, and trusted entities that manage public-key encryption |
PSD2 | Payment Services Directive 2 |
RTS | Regulatory Technical Standards |
SCA | Strong Customer Authentication; a requirement of RTS for PSD2 |
SEPA | Single Euro Payments Area; payment-integration initiative of the EU |
smart token | Authorization to access an underlying asset |
SDK | Software Development Kit; for example, the Token Java SDK |
Token cloud | Layer through which TokenOS members communicate |
transfer token | Smart token that provides authorization to transfer assets, such as the funds in a bank account |
Copyright © 2019 Token, Inc. All Rights Reserved