NAV Navbar
Javascript Java C#

TPP On-boarding

Getting Started in the Sandbox

The sandbox is a virtual testing environment that mimics production. Here you can play around with fake banks and money so you can be sure your code works before accessing real accounts.

1. Samples and SDK

See working implementations of a payment flow (PISP) or how a PFM can access information (AISP), with our samples

Download the SDK in your preferred language.

2. Setting up the Client

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',
});
 Tokenio.Tpp.TokenClient tokenClient = Tokenio.Tpp.TokenClient.NewBuilder()
.ConnectTo(Tokenio.TokenCluster.SANDBOX)
.WithKeyStore(new UnsecuredFileSystemKeyStore(key.FullName))
.Build();

Relevant API:

3. Creating a Business Member

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

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

// Initialize SDK:
// 'sandbox' is a good value for TEST_ENV here.
const devKey = require('../src/config.json').devKey[TEST_ENV];
const Token = new TokenClient({env: TEST_ENV, developerKey: devKey});

// Generate a random-nonsense-string alias.
// ('name.com' would be more typical than a random domain.
// But if we run this code with the same alias twice,
// the 2nd time it will fail because the name is taken.)
//
// In test environments, we can use this domain as an alias
// without verifying it; but in production, we can't.
const alias = {
    type: 'DOMAIN',
    value: `${Token.Util.generateNonce()}.com.noverify`,
};

// Create a member with keys stored in memory:
return await Token.createMember(
public static Member createMember() {
    // Create the client, which communicates with
    // the Token cloud.
    try {
        Path keys = Files.createDirectories(Paths.get("./keys"));
        TokenClient tokenClient = TokenClient.builder()
                .withKeyStore(new UnsecuredFileSystemKeyStore(keys.toFile()))
                .connectTo(SANDBOX)
                .build();

        // An alias is a "human-readable" reference to a member.
        // Here, we use a random email. This works in test environments.
        // but in production, TokenOS would try to verify we own the address,
        // so a random address wouldn't be useful for much.
        // We use a random address because otherwise, if we ran a second
        // time, Token would say the alias was already taken.
        Alias alias = Alias.newBuilder()
                .setType(Alias.Type.EMAIL)
                .setValue(randomAlphabetic(10).toLowerCase()
                        + "+noverify@example.com")
                .build();

public static TppMember CreateMember()
{
    // Create the client, which communicates with
    // the Token cloud.
    try
    {

        var key = Directory.CreateDirectory("./keys");

        Tokenio.Tpp.TokenClient tokenClient = Tokenio.Tpp.TokenClient.NewBuilder()
       .ConnectTo(Tokenio.TokenCluster.SANDBOX)
       .WithKeyStore(new UnsecuredFileSystemKeyStore(key.FullName))
       .Build();



        // An alias is a "human-readable" reference to a member.
        // Here, we use a random email. This works in test environments.
        // but in production, TokenOS would try to verify we own the address,
        // so a random address wouldn't be useful for much.
        // We use a random address because otherwise, if we ran a second
        // time, Token would say the alias was already taken.
        Alias alias = new Alias
        {
            Value = TestUtil.RandomNumeric(10) + "+noverify@example.com",
            Type = Alias.Types.Type.Email
        };

Relevant APIs:

4. Aliases and Verification

Members have human-readable and verifiable aliases. As a Token business member with the DOMAIN type, 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"

};
// add the alias
member.AddAliasBlocking(alias);
// remove the 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
    {
        Value = "user-email@example.com"

    };


    // 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.Id;

    // The resolved alias
    // will have the correct type, e.g. EMAIL.
    Alias resolvedAlias = resolved.Alias;

5. 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.GetMember(memberId);

6. 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”.

Setting the Profile

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

Relevant APIs:

Getting the Profile Name and Picture

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

To get the member profile picture, call Member getProfilePicture with the memberId.

Relevant APIs:

Example for Setting and Getting the Profile Name and Picture

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

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

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.

Going into Production

  1. When you are ready for production, connect to the production environment by updating connectTo(SANDBOX) to connectTo(PRODUCTION) or prd if you are using JavaScript.

IMPORTANT: Make sure you have set your profile display name and picture. You will not be able to move to Production until you have set your profile.

  1. For verification, Token will need your Alias value and type, and your memberId.

  2. Contact Token Support using the TPP On-boarding category. A support ticket will be generated for you.

  3. Token will perform KYC (Know Your Customer) compliance checks, including checking your eIDAS credentials. Token will also verify your domain alias at this time.

  4. When this process is complete, you will be informed through your support ticket.

What’s Next?

PIS

Go HERE for PIS related functionality such as initiating a payment.

AIS

Go HERE for AIS related functionality such as accessing customer account information.

CBPII

Go HERE for CBPII related functionality.

Copyright © 2019 Token, Inc. All Rights Reserved