NAV Navbar
Javascript Java C#

Overview of Token

Token provides a global open banking API that connects easily with a bank’s existing infrastructure. Whether you are a business needing to initiate payments, or you are a payment service provider wanting to collect account data, the SDK provides the tools you need to get started with Token’s sandbox.

At the core of TokenOS is the smart token.

It is the authorization to access assets such as a user’s balance or transactions, or to initiate a payment on behalf the user. It has a unique ID and is programmable by defining conditions that manage access to the token’s assets. Both the conditions of the token and its assets are immutable, meaning neither can be changed after it is created.

Every business, company, or third party provider that interacts with Token is referred to as a business member. Business members communicate through the Token cloud using the Token API. The Token cloud acts as a router, generating smart tokens and verifying their conditions and digital signatures. Any Token member can use the API, and can do so using one of the language-specific Token SDKs. The Token cloud is distributed, with services hosted on geographically dispersed servers operated by Token and its partners. The Token cloud is integrated with banks and other financial institutions through a thin integration layer that allows access to a bank’s core banking services.

The following diagram shows Token’s ecosystem:

Token Ecosystem

Security

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:

Getting Started

Setting Up the Client

Before anything else, you must set up the Token client object (TokenClient). The client interacts with the Token network, with its configuration controlling the connection. It sends and receives calls and specifies which Token environment to use when testing. The client also functions as an abstraction on top of the network connection reconnecting as needed and killing idle connections. Typically, one SDK client instance is used for all operations.

Before you make calls you will need the latest Token SDK in your preferred language.

While creating the client, the code will specify which Token environment to use:

The sandbox is a stable testing environment which uses fake banks and fake money.

When your code is ready for production, you can contact Token to discuss the next steps for switching to our production environment.

The SDK shows how to set up an Token client that saves your user’s private keys in a directory named keys. If your code runs somewhere with a safer storage space, you can implement a KeyStore that stores keys elsewhere, using UnsecuredFileSystemKeyStore as a guide.

The SDK shows how to set up an Token client that saves your user’s private keys in a directory named keys. If your code runs somewhere with a safer storage space, you can write a crypto engine that stores keys elsewhere, using UnsecuredFileCryptoEngine as a guide.

The SDK shows how to set up an Token client that saves your user’s private keys in a directory named keys. If your code runs somewhere with a safer storage space, you can implement a IKeyStore that stores keys elsewhere, using UnsecuredFileSystemKeyStore as a guide.

Go here for information about Local Key Storage.

Creating the Client

Path keys = Files.createDirectories(Paths.get("./keys"));
TokenClient tokenClient = TokenClient.builder()
        .withKeyStore(new UnsecuredFileSystemKeyStore(keys.toFile()))
        .connectTo(SANDBOX)
        .build();
const Token = new TokenClient({
    env: 'sandbox',
    keyDir: './keys',
});
var tokenClient = TokenClient.NewBuilder()
    .WithKeyStore(new InMemoryKeyStore())
    .ConnectTo(TokenCluster.SANDBOX)
    .Build();

Relevant API:

Creating a Business Member

Before connecting to banks through TokenClient, you must first create a Token business member using the createMember method.

NOTE: If you have already created a member, load your existing member. Otherwise, you will need to set up an alias along with your member.

Creating a Business Member

const domain = Math.random().toString(36).substring(2, 10) + ".com"
const alias = {
    type: DOMAIN,
    value: domain
};
Token.createMember(alias, Token.UnsecuredFileCryptoEngine).then(function(m) {
    member = m;
    member.setProfile({
        displayNameFirst: 'Merchant 123'
    });
});
String domain = generateNonce() + ".com";
Alias alias = Alias.newBuilder()
        .setType(DOMAIN)
        .setValue(domain)
        .build();
Member member = tokenClient.createMemberBlocking(alias);
member.setProfile(Profile.newBuilder()
        .setDisplayNameFirst("Merchant 123")
        .build());
string domain = Util.Nonce() + ".com";
Alias alias = new Alias
{
    Type = Domain,
    Value = domain
};
Member member = tokenClient.CreateMemberBlocking(alias);
var profile = new Profile
{
    DisplayNameFirst = "Merchant 123"
};
member.SetProfile(profile).Result;

Relevant APIs:

Aliases and Verification

Members have human-readable and verifiable aliases. As a Token business member, you must provide your business web domain as your alias.

An alias can only be claimed once. After that, it is no longer available to other members. An alias belongs to only one (1) member, and must be verified before it can be used.

Adding and Removing an Alias

Alias alias = Alias.newBuilder()
        .setType(DOMAIN)
        .setValue("verified-domain.com")
        .build();

// add the alias
member.addAliasBlocking(alias);

// remove the alias
member.removeAliasBlocking(alias);
const alias1 = (await member.aliases())[0]; // or member.firstAlias();
const alias2 = {
    type: 'EMAIL',
    value: 'alias2-' + Token.Util.generateNonce() + '+noverify@token.io',
};
await member.addAlias(alias2);

const alias3 = {
    type: 'EMAIL',
    value: 'alias3-' + Token.Util.generateNonce() + '+noverify@token.io',
};
const alias4 = {
    type: 'EMAIL',
    value: 'alias4-' + Token.Util.generateNonce() + '+noverify@token.io',
};
await member.addAliases([alias3, alias4]);

await member.removeAlias(alias1);
await member.removeAliases([alias2, alias3]);

Alias alias = new Alias
{
    Type = Alias.Types.Type.Domain,
    Value = "verified-domain.com"
};

member.AddAliasBlocking(alias);

member.RemoveAliasBlocking(alias);

Verifying an Alias

DOMAIN type aliases need to be manually verified by Token. To register a domain alias:

  1. Add your domain name using the Member addAlias method.

  2. Send Token a message that includes your business member ID.

If you do not know your ID, you can retrieve it using the Member memberId method.

Relevant APIs:

Fetching an Alias

There are two ways to fetch aliases:

  1. Fetch a list of the member’s aliases by calling the Member aliases method.
  2. Fetch the first alias used by the member by calling the Member firstAlias convenience method.

Relevant APIs:

Resolving an Alias

To resolve a member’s identity, you can look up a member’s ID using their alias and/or profile.

If you want to find out if an alias already belongs to a member, call the resolveAlias method. This can be helpful in discovering if there is a typo.

Relevant APIs:

Resolving an Alias

const resolved = await Token.resolveAlias(alias4);
/* resolved alias has id and alias fields: */
{
"id": "m:4AaVmfKfY9DQ9tuqWJcEwq9VkXpo:5zKtXEAq",
"alias": {
  "type":"EMAIL",
  "value":"alias4-v6rpfo+noverify@token.io"}
  }
}
Alias alias = Alias.newBuilder()
        .setValue("user-email@example.com")
        .build();

// If this call fails then the alias does not correspond to an existing member.
TokenMember resolved = client.resolveAliasBlocking(alias);

// resolved member ID from alias
String memberId = resolved.getId();

// The resolved alias
// will have the correct type, e.g. EMAIL.
Alias resolvedAlias = resolved.getAlias();
{
    Alias alias = new Alias
    {
        Type = Alias.Types.Type.Email,
        Value = "user-email@example.com"
    };

    TokenMember resolved = client.ResolveAliasBlocking(alias);

    string memberId = resolved.Id;

    Alias resolvedAlias = resolved.Alias;

Load Existing Member

To obtain a Member object for an already-existing member, you must have a TokenClient, the member ID of an existing member, AND the member’s keys. Add the keys to your SDK’s keystore and then call TokenClient.getMember on the member ID.

This code uses the API:

Most of the newly created member data is stored in the Token cloud, such as public keys. Private keys remain in local storage.

Use already-stored private key

return tokenClient.getMemberBlocking(memberId);
member = Token.getMember(Token.UnsecuredFileCryptoEngine, mid);
return tokenClient.GetMemberBlocking(memberId);

Profile

Each business member has a profile that allows users to find the member they are searching for without the alias or ID. This display name must be your business name. This identifies you in push notifications sent to your users’ apps, avoiding confusion and unintentional purchase cancellations. E.g. “Southside is requesting €10” as opposed to “Unknown merchant is requesting €10”.

Getting the Profile

To get member profile information, call Member getProfile. This gets a Profile data structure containing the member’s display name.

Relevant APIs:

Setting the Profile Picture

Call Member setProfile method to set your display name. This method takes a Profile data structure.

Relevant APIs:

Example for Setting the Profile

const name = {
    displayNameFirst: 'Tycho',
    displayNameLast: 'Nestoris',
};
await member.setProfile(name);
const jpeg = loadPicture('tycho.jpg'); // file contents as byte array
await member.setProfilePicture('image/jpeg', jpeg);

const profile = await member.getProfile(member.memberId());
/* Profile structure: */
{
 displayNameFirst: 'Tycho',
 displayNameLast: 'Nestoris',
 originalPictureId: 'b:2aT7GiHkqhDzpLw...rfBaag4jw:5zKtXEAq'
}
Profile name = Profile.newBuilder()
        .setDisplayNameFirst("Tycho")
        .setDisplayNameLast("Nestoris")
        .build();
member.setProfileBlocking(name);
member.setProfilePictureBlocking("image/jpeg", PICTURE);

Profile profile = member.getProfileBlocking(member.memberId());
Profile name = new Profile
{
    DisplayNameFirst = "Tycho",
    DisplayNameLast = "Nestoris"
};
member.SetProfileBlocking(name);
member.SetProfilePictureBlocking("image/jpeg", PICTURE);

Profile profile = member.GetProfileBlocking(member.MemberId());

Keys

Each member has cryptographic keys used to endorse or sign a token.

When you create a member with the createMember method, it automatically creates three (3) key pairs:

  1. a low-level pair
  2. a standard pair
  3. a privileged pair

Each pair has a private and a public key. The three public keys are uploaded to the Token cloud; the private keys are put into secure local storage.

The private keys will be used to sign all requests made through the Member object, such as initiating requests for account access or payments, or changing the Member’s profile. Token will verify each request using the public keys. Requests made directly from the TokenClient, such as resolving an alias or retrieving a list of connected banks, are not signed and are therefore accessible by anyone.

The following methods control keys in the Token Cloud ONLY.

To add more keys: call the Member approveKey or Member approveKeys method.

To remove previously-uploaded keys, call Member removeKey or Member removeKeys.

IMPORTANT: Removed keys are no longer valid and cannot be used to sign requests.

Relevant APIs:

Generating, Approving, and Removing Keys

Key lowKey = crypto.generateKey(LOW);
member.approveKeyBlocking(lowKey);

Key standardKey = crypto.generateKey(STANDARD);
Key privilegedKey = crypto.generateKey(PRIVILEGED);
member.approveKeysBlocking(Arrays.asList(standardKey, privilegedKey));

member.removeKeyBlocking(lowKey.getId());
const keypair4 = await Token.Crypto.generateKeys('LOW');
keypair4.publicKey = Util.strKey(keypair4.publicKey);
await member.approveKey(keypair4);
const keypair5 = await Token.Crypto.generateKeys('STANDARD');
const keypair6 = await Token.Crypto.generateKeys('PRIVILEGED');
keypair5.publicKey = Util.strKey(keypair5.publicKey);
keypair6.publicKey = Util.strKey(keypair6.publicKey);
await member.approveKeys([keypair5, keypair6]);

await member.removeKey(keypair4.id);
await member.removeKeys([keypair5.id, keypair6.id]);
Key lowKey = crypto.GenerateKey(Key.Types.Level.Low);
member.ApproveKeyBlocking(lowKey);

Key standardKey = crypto.GenerateKey(Key.Types.Level.Standard);
Key privilegedKey = crypto.GenerateKey(Key.Types.Level.Standard);
member.ApproveKeysBlocking(new List<Key> {standardKey, privilegedKey});

member.RemoveKeyBlocking(lowKey.Id);

Local Key Storage

To configure where the client stores member’s private keys, choose a CryptoEngine type and pass it as a parameter to Token.createMember, Token.provisionDevice, and Token.provisionDeviceLow.

The Token SDK client class provides some built-in key storage CryptoEngine types:

  • Token.MemoryCryptoEngine keeps keys in memory and forgets them on restart, which is useful for unit tests, but not for persistent members.
  • Token.UnsecuredFileCryptoEngine keeps keys in files in a directory. You must specify which directory as a parameter when creating the Token SDK client.
  • Token.BrowserCryptoEngine keeps keys in browser’s localStorage, which is useful for web clients.

You can define another CryptoEngine key storage type if you don’t want to use the one above. One option for implementation is the BrowserCryptoEngine implementation - a thin wrapper around BrowserKeyStore and stores keys. Use the KeyStoreCryptoEngine helper class to ease defining a similar, thin, wrapper.

For more information about implementing a custom CryptoEngine, see the SDK’s README.

Relevant APIs:

To configure where the client stores member’s private keys, choose the KeyStore implementation and pass it as a parameter to TokenClient.builder().withKeyStore().

The Token SDK client class provides basic built-in KeyStore classes:

  • InMemoryKeyStore keeps keys in memory, but forgets on restart. This is useful for unit tests, but not for persistent members.
  • UnsecuredFileSystemKeyStore keeps keys in files in a directory. You must specify which directory as a constructor parameter.

You can define another KeyStore class if you don’t want to use the one above.

Relevant APIs:

To configure where the client stores member’s private keys, choose the IKeyStore implementation and pass it as a parameter to TokenClient.Builder.WithKeyStore().

The Token client class provides basic built-in IKeyStore classes:

  • InMemoryKeyStore keeps keys in memory, and forgets on restart. This is useful for unit tests, but not for persistent members.
  • UnsecuredFileSystemKeyStore keeps keys in files in a directory. You must specify which directory as a constructor parameter.

You can define another IKeyStore class if you don’t want to use the one above.

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?

As mentioned in the Overview, 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:

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

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
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 60 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 two additional fields in the transfer token request builder:

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

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);
var request = TokenRequest.TransferTokenRequestBuilder(100, "EUR")
    .SetToMemberId(payee.MemberId())
    .SetDescription("Book purchase")
    .SetRedirectUrl("https://token.io/callback")
    .SetFromAlias(new Alias
    {
        Value = "payer-alias@token.io,",
        Type = Alias.Types.Type.Email,
    })
    .SetBankId("iron")
    .SetCsrfToken(Util.Nonce())
    .build();

return payee.StoreTokenRequestBlocking(request);

Requesting Access Tokens

To make a request for an access token, use the static TokenRequest.transferTokenRequestBuilder(resources) method, where resources is a list of the following ResourceType:

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);
var request = TokenRequest.AccessTokenRequestBuilder(
        TokenRequestPayload.Types.AccessBody.Types.ResourceType.Accounts,
        TokenRequestPayload.Types.AccessBody.Types.ResourceType.Balances)
    .SetToMemberId(grantee.MemberId())
    .SetRedirectUrl("https://token.io/callback")
    .SetFromAlias(new Alias
    {
        Value = "grantor-alias@token.io",
        Type = Alias.Types.Type.Email
    })
    .SetBankId("iron")
    .SetCsrfToken(Util.Nonce())
    .build();

return grantee.StoreTokenRequestBlocking(request);

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

const tokenRequestUrl = Token.generateTokenRequestUrl(requestId);
String tokenRequestUrl = tokenClient.generateTokenRequestUrlBlocking(requestId);
var tokenRequestUrl = tokenClient.GenerateTokenRequestUrlBlocking(requestId);

After constructing the token request URL, 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

Another option is to create a Token Button that either redirects to, or creates a pop-up of, the Token web app in the user’s browser.

Creating a Redirect Button

function createRedirectButton() {
    var Token = new window.Token({
        env: 'sandbox',
    });

    // create TokenPopupController to handle Popup messages
    tokenController = Token.createRedirectController();

    // get button placeholder element
    var element = document.getElementById(elementId);

    // create the button
    button = window.Token.createTokenButton(element, {
        label: "Redirect Token Quick Checkout",
    });

    // bind the Token Button to the Redirect Controller when ready
    tokenController.bindButtonClick(button, function(action) {
        // Each time the button is clicked, a new tokenRequestUrl is created
        getTokenRequestUrl(function(tokenRequestUrl) {
            // Redirect to the Token web app with the generated tokenRequestUrl
            action(tokenRequestUrl);
        });
    });
    // enable button after binding
    button.enable();
}

Creating a Popup Button

function createPopupButton() {

    var Token = new window.Token({
        env: 'sandbox',
    });
    // create TokenPopupController to handle Popup messages
    tokenController = Token.createPopupController();

    // get button placeholder element
    var element = document.getElementById(elementId);

    // create the button
    button = Token.createTokenButton(element, {
        label: "Popup Token Quick Checkout",
    });

    // setup onLoad callback
    tokenController.onLoad(function(controller) {
        // bind the Token Button to the Popup Controller when ready
        tokenController.bindButtonClick(button, function(action) {
            // Each time the button is clicked, a new tokenRequestUrl is created
            getTokenRequestUrl(function(tokenRequestUrl) {
                // Initialize popup using the tokenRequestUrl
                action(tokenRequestUrl);
            });
        });
        // enable button after binding
        button.enable();
    });

    // setup onSuccess callback
    tokenController.onSuccess(function(data) { // Success Callback
        // build success URL
        var successURL = "/redeem"
            + "?tokenId=" + window.encodeURIComponent(data.tokenId);
        // navigate to success URL
        window.location.assign(successURL);
    });

    // setup onError callback
    tokenController.onError(function(error) { // Failure Callback
        throw error;
    });
}

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

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 with an HTTP 302.

Example Token Request Callback URL

https://merchant-site.com/callback?tokenId=tt:3PFXqSnRX7LCXo7MenmW26VVgMnHi44Br4WCM5MgEq4f:5zKxyCKM

TokenOS will include the necessary parameters for your server-side code to redeem the token in the callback URL.

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 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.parseTokenRequestCallbackParamsBlocking(
        queryParams,
        csrfToken);
TokenRequestCallback callback = tokenClient.ParseTokenRequestCallbackUrlBlocking(
    queryParams,
    csrfToken.Value);

3. Redeeming Tokens

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
TransferEndpoint tokenDestination = TransferEndpoint
        .newBuilder()
        .setAccount(BankAccount.newBuilder()
                .setToken(BankAccount.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 have random refID:
        cartId);


var transfer = payee.RedeemToken(
    transferToken,
    new TransferEndpoint
    {
        Account = new BankAccount
        {
            Token = new Token
            {
                MemberId = payee.MemberId(),
                AccountId = accountId
            }
        }
    },
    cartId).Result;

The transfer object (or its ID) returned 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 Access Tokens

Use the server-side call forAccessToken to get a Representable object that represents the user. You can then call getBalance, getTransactions, on the Representable object to retrieve account information. These methods will only succeed if the specified access token has sufficient permissions.

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 balance0 = 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"
    }
]
var grantor = grantee.ForAccessToken(tokenId, customerInitiated);
var accounts = grantor.GetAccounts().Result;

Money balance0 = accounts[0].GetBalance(Standard).Result.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);
String tokenRequestUrl = tokenClient.generateTokenRequestUrlBlocking(requestId);
var tokenRequestUrl = tokenClient.GenerateTokenRequestUrlBlocking(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(
        callbackParameterMap,
        csrfToken);

String tokenId = callback.getTokenId();
String state = callback.getState();
TokenRequestCallback callbackParams = tokenClient.ParseTokenRequestCallbackUrl(
        callbackUrl,
        csrfToken)
        .Result;

string tokenId = callbackParams.TokenId;
string state = callbackParams.State;

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: Create 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

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);
var transferToken = payee.GetToken(tokenId).Result;
return payee.CancelToken(transferToken).Result;

Canceling Access Tokens

Like transfer 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);
var accessToken = grantee.GetToken(tokenId).Result;
return grantee.CancelToken(accessToken).Result;

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

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 grpc-java Status.java. 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 grpc-csharp StatusCode.cs.

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 7 or later.

To get the Token Java SDK JARs, source code, and instructions for use go to the token-sdk-java Token Artifactory page. (Choose the latest jar under token-sdk-java 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-java</artifactId>
    <version>2.0.3</version>
</dependency>

Gradle

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

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

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.Net" Version="1.2.0" />
</ItemGroup>

Other Languages

In development: Python

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

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?

While our technology is capable of this, the banks in our network currently only support single immediate payments. Standing orders, future dated payments, and additional payment options will be delivered during 2019.

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?

The banks in our network currently only offer domestic payments; banks in select regions in our network are expected to support this later in 2019. As such, UK banks only support GBP payments, and EU banks only support EUR payments. Token uses underlying bank rails to initiate payments, the initiating bank determines the most suitable rail to use given the payment details.

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