NAV Navbar
Javascript Java C#

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:

Image showing smart token components

There are two kinds of smart tokens:

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
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: Used to narrow down the country selection in the web-app UI
userRefId Optional: Used to track a member claimed by a TPP
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”).

You can set the following fields in the transfer token request builder:

chargeAmount The maximum amount per charge on a multi-use transfer token
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

An important field 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")
        .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")
        .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'
)
    .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:

  1. Set your business name. Call setProfile on your business member and set display_name_first to your business name.

  2. Send Token your member ID requesting permission to customize.

  3. Once permission has been granted, call createCustomization. This will return a customizationId.

  4. Include this customization ID in your TokenRequest.

Parameters

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();

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:

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.

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 ResourceTypes.

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 ResourceTypes.

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)
        .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)
        .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

Due to international payments usually having fees and foreign exchange charges attached to them, Token provides TPPs an option to specify a callback URL, giving them the ability to make an informed decision about setting the TransferDestinations in the storeTokenRequest(tokenRequest) call. Token calls this provided URL when a user makes a selection of the bank and country of the account with which they want to make the payment.

Set the callback URL in the TokenRequest, but DO NOT set TransferDestinations yet.

Setting the Callback URL

TokenRequest tokenRequest = TokenRequest.transferTokenRequestBuilder(250, "EUR")
                .setToMemberId(payee.memberId())
                .setDescription("Book purchase")
                // This TPP provided url gets called by Token after the user selects bank and
                // country on the Token web app.
                .setSetTransferDestinationsUrl(setTransferDestinationsCallback)
                // This TPP provided Redirect URL gets called after Token is ready
                // for redemption.
                .setRedirectUrl("https://tpp-sample.com/callback")
                .setFromAlias(AliasProtos.Alias.newBuilder()
                        .setValue("payer-alias@token.io") // user alias
                        .setType(AliasProtos.Alias.Type.EMAIL)
                        .build())
                .setBankId("iron") // bank ID
                .setCsrfToken(generateNonce()) // nonce for CSRF check
                .build();

        String requestId = payee.storeTokenRequestBlocking(tokenRequest);

When Token sees that TransferDestinations has not been set it will call the callback URL provided by the TPP. Once that URL is called, the TPP will receive a few informational parameters that will allow them to make a decision on what accounts can be selected as TransferDestinations.

Parsing the Callback URL

TokenRequestTransferDestinationsCallbackParameters params =
                tokenClient.parseSetTransferDestinationsUrl(setTransferDestinationsCallback);

TPPs can check the following parameters:

parameters.getCountry() Country selected
parameter.getBankName() Bank selected
parameters.getSupportedTransferDestinationTypes() Payment types supported by the bank i.e. SEPA, FASTER_PAYMENTS. Details about the TransferDestination object and payment types can be found here

Once the TransferDestinations are chosen, they need to be set in the previously stored TokenRequest.

Set TransferDestinations

List<TransferDestination> transferDestinations = new ArrayList<>();
        if (params.getSupportedTransferDestinationTypes()
                .contains(DestinationCase.FASTER_PAYMENTS)) {
            TransferDestination destination = TransferDestination
                    .newBuilder()
                    .setFasterPayments(TransferDestination.FasterPayments
                            .newBuilder()
                            .setSortCode(generateNonce())
                            .setAccountNumber(generateNonce())
                            .build())
                    .build();
            transferDestinations.add(destination);
        } else {
            transferDestinations.add(TransferDestination
                    .newBuilder()
                    .setSepa(TransferDestination.Sepa
                            .newBuilder()
                            .setBic(generateNonce())
                            .setIban(generateNonce())
                            .build())
                    .build());
        }
        payee.setTokenRequestTransferDestinationsBlocking(requestId, transferDestinations);
    }

Sample Source

After this step, TPPs can call generateTokenRequestUrl and follow the rest of the Token flow.

String tokenRequestUrl =
           tokenClient.generateTokenRequestUrlBlocking(requestId);

NOTE: These are optional steps. TPPs can set the TransferDestinations in the original storeTokenRequest call if they do not want to use a callback. In that case, 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

Example of Constructing a Token Request Url

var tokenRequestUrl = Token.generateTokenRequestUrl(requestId);
return tokenClient.generateTokenRequestUrlBlocking(requestId);
.FlatMap(requestId => tokenClient.GenerateTokenRequestUrl(requestId))

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&currency=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 two variations:

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);
TokenRequestCallback callback = 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.nonce);
TokenRequestCallback callback = 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.

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 have random 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)

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

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.

Token Request Flow Secure Sequence Diagram

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.

Example of using generateTokenRequestUrl with a CSRF token

const tokenRequestUrl = Token.generateTokenRequestUrl(requestId);
return tokenClient.generateTokenRequestUrlBlocking(requestId);
.FlatMap(requestId => tokenClient.GenerateTokenRequestUrl(requestId))


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;
})
TokenRequestCallback callback = tokenClient.parseTokenRequestCallbackParamsBlocking(
.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:

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

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.

By specifying in your application the bankId of the user-selected bank when creating the token request, you can filter the banks available to the user or control the bank selection UI.

Bank Selection

To retrieve the list of banks to display, use the getBanks method.

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;

Each bank also specifies additional properties which can be used to filter the banks list displayed to the user:

supports_information Allows for retrieval of account information
supports_send_payment Allows for payment initiation
supports_receive_payment Allows for receiving payments
provider Underlying connectivity type; *e.g. Yodlee FinAPI Token
country ISO 3166-1 alpha-2 two letter country code in uppercase

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.

Approve Screen

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);
Token transferToken = payee.GetTokenBlocking(tokenId);

// Cancel transfer token.
return payee.CancelTokenBlocking(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);
Token accessToken = grantee.GetTokenBlocking(tokenId);
// Cancel access token.
return grantee.CancelTokenBlocking(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 Transfer Tokens

PagedList<Token, String> pagedList = member.getTransferTokensBlocking("", 10);
const pagedResult = await member.getTransferTokens('', 10);
PagedList<Token> pagedList = member.GetTransferTokensBlocking("", 10);

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 Access Tokens

PagedList<Token, String> pagedList = member.getAccessTokensBlocking("", 10);
const pagedResult = await member.getAccessTokens('', 10);
PagedList<Token> pagedList = member.GetAccessTokensBlocking("", 10);

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.

Errors

This section describes runtime errors thrown by the Token SDKs, such as program exceptions.

Java SDK Errors

The Token Java SDK throws 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 SDK Errors

The Token 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.

C# SDK Errors

The Token C# SDK throws System.AggregateException that wraps the standard gRPC exceptions; for more information, refer to the status codes.

Error Enum Value Meaning
OK 0 Operation completed successfully
Cancelled 1 Operation was canceled (typically by the caller)
Unknown 2 Unknown error
InvalidArgument 3 Client specified an invalid argument
DeadlineExceeded 4 Deadline expired before operation could complete
NotFound 5 Requested entity (such as a file or directory) was not found
AlreadyExists 6 Entity that you attempted to create (such as a file or directory) already exists
PermissionDenied 7 Caller does not have permission to execute the specified operation
ResourceExhausted 8 A resource, such as a per-user quota or the file system is out of space, has been exhausted
FailedPrecondition 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
OutOfRange 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
DataLoss 15 Unrecoverable data loss or corruption
Unauthenticated 16 Request does not have valid authentication credentials for the operation

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.)

Maven

<repositories>
    ...
    <repository>
      <url>https://token.jfrog.io/token/public-libs-release-local/</url>
    </repository>
</repositories>
<dependency>
    <groupId>io.token.sdk</groupId>
    <artifactId>tokenio-sdk-tpp</artifactId>
    <version>2.7.1</version>
</dependency>

Gradle

repositories {
  ...
  maven { url 'https://token.jfrog.io/token/public-libs-release-local/' }
}
compile(group: 'io.token.sdk', name: 'tokenio-sdk-tpp', version: '2.7.1')

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

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="0.0.1" />
</ItemGroup>

Other Languages

To use a language other than the Token SDK languages (currently Java, JavaScript, and Objective-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.

Screen 0 Screen 01 Screen 2 Screen 3 Screen 4 Screen 5 Screen 7 Screen 8

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:

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.

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