TPP On-boarding
For the latest onboarding guidance, please visit Getting Started.
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:
- TokenClient.create: Create the client
- TokenCluster enum that defines SANDBOX, PRODUCTION, other environments
- withKeystore: Stores keys in a directory
- TokenClientBuilder: Create the client
- TokenCluster: constants that defines SANDBOX, PRODUCTION, other environments
- WithKeyStore: stores keys in a directory
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 domain. This works in test environments,
// but in production, an alias should be verified manually during the onboarding
// process.
// 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.DOMAIN)
.setValue(randomAlphabetic(10).toLowerCase() + "+noverify.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:
- TokenClient.createMember: Create a Business Member
- Member.retryVerification: Retries alias verification
- TokenClient.CreateBusinessMember: Create a Business Member with an Alias and KeyStoreCryptoEngine
- Member.RetryVerification: Retries alias verification
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
The Member method addAlias
attempts to add an alias for a member. It will fail if the alias is already in use, so you will need to choose another.
The Member method removeAlias
removes an alias from a member.
If you already have successfully-verified aliases, call the Member aliases
method to retrieve them.
Relevant APIs:
- Member.addAlias: Attempt to add an alias
- Member.addAliases: Attempt to add more than one alias
- Member.removeAlias: Remove an alias
- Member.removeAliases: Remove selected aliases
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: 'DOMAIN',
value: 'alias2-' + Token.Util.generateNonce() + '.com.noverify',
};
await member.addAlias(alias2);
const alias3 = {
type: 'DOMAIN',
value: 'alias3-' + Token.Util.generateNonce() + '.com.noverify',
};
const alias4 = {
type: 'DOMAIN',
value: 'alias4-' + Token.Util.generateNonce() + '.com.noverify',
};
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:
Add your domain name using the Member
addAlias
method.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:
- Fetch a list of the member’s aliases by calling the Member
aliases
method. - Fetch the first alias used by the member by calling the Member
firstAlias
convenience method.
Relevant APIs:
- Member.aliases: Fetch list of the member’s aliases
- Member.firstAlias: Fetch one of the member’s aliases
- Member.aliases: Fetch a list of the member’s aliases
- Member.firstAlias Fetch one of the member’s aliases
- Member.GetAliases Fetch list of the member’s aliases
- Member.GetFirstAlias: Fetch one of the member’s aliases
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.
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:
- Member.getProfile: Method to get the profile
- Member.getProfilePicture: Method to get the profile picture
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:
- a low-level pair
- a standard pair
- 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:
- Member.approveKey: Add a key
- Member.approveKeys: Add keys
- Member.removeKey: Remove a key
- Member.removeKeys: Remove keys
- CryptoEngine.generateKey
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’slocalStorage
, 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:
- BrowserCryptoEngine
- MemoryCryptoEngine
- UnsecuredFileCryptoEngine
- KeyStoreCryptoEngine: helper wrapper class
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
- When you are ready for production, connect to the production environment by updating
connectTo(SANDBOX)
toconnectTo(PRODUCTION)
orprd
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.
For verification, Token will need your
Alias
value and type, and yourmemberId
.Contact Token Support using the TPP On-boarding category. A support ticket will be generated for you.
Token will perform KYC (Know Your Customer) compliance checks, including checking your eIDAS credentials. Token will also verify your domain alias at this time.
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