Get Sandbox Access

Paybase Developer Centre

OverviewAPI GuidesGetting StartedRecipesGig Economy PlatformsSharing Economy PlatformsMarketplacesBlockchain BusinessesEscrowSandboxRolesRulesDue DiligenceCustomersAccountsBank AccountsCardsTransactionsIntroductionInboundGetting money into the systemTransaction ReferenceAccount ReferenceInternalOutboundEscrowSplit PaymentsRefundsStrong Customer Authentication3D Secure AuthenticationIntroductionCreate a cardCreate a transactionDocument UploadStatementsWebhooksErrorsPQLAPI ReferenceAccountCreate an accountRetrieve an accountTransition account statusList all accountsAnnotate an accountDelete annotation from an accountTag an accountDelete tag from an accountBank AccountCreate a bank accountRetrieve a bank accountUpdate a bank accountTransition bank account statusList all bank accountsAnnotate a bank accountDelete annotation from a bank accountTag a bank accountDelete tag from a bank accountCardCreate a cardRetrieve a cardUpdate a cardTransition card statusList all cardsAnnotate a cardDelete annotation from a cardTag a cardDelete tag from a cardCardholderCreate a cardholderRetrieve a cardholderUpdate a cardholderTransition cardholder statusList all cardholdersAnnotate a cardholderDelete annotation from a cardholderTag a cardholderDelete tag from a cardholderCreate an authentication tokenCheckCreate a checkCustomerIndividual CustomerCreate a customerRetrieve a customerUpdate a customerSole TraderCreate a customerRetrieve a customerUpdate a customerOrganisationCreate a CustomerRetrieve a CustomerUpdate a CustomerIncorporated BusinessCreate a customerRetrieve a customerUpdate a customerBusiness PersonAdd a business personRetrieve a business personUpdate a business personDelete a business personRetrieve a customerTransition state of a customerList all customersAnnotate a customerDelete annotation from a customerTag a customerRemove tag from a customerCreate an authentication tokenTouch a customerDocumentCreate a documentRetrieve a documentList Document TypesReferenceRetrieve a referenceStatementRetrieve a statementStatusRetrieve API statusTransactionCreate inbound transactionCreate internal transactionCreate outbound transactionRetrieve a transactionTransition transaction statusList all transactionsAnnotate a transactionDelete annotation from a transactionTag a transactionDelete tag from a transaction
API version: ba085a6

Sharing Economy Platforms

Sharing Economy Platforms

A large factor in the success of sharing economy platforms is to be able to instil trust and security. Unlike traditional goods marketplaces, sharing economies often integrate with insurance providers and sometimes want to complete Due Diligence on their entire community. With Paybase, not only can you ensure that all your users are verified, but also build conditional payment flows where payments are taken from users but only released to service providers when certain conditions are met.

Onboarding listers

Listers refers to any individuals or businesses that are listing items or services that can be rented via your platform. Given that you control the entire experience of the listers on your platform, and Paybase never interacts directly with them, you will need to build flows to collect the necessary information required for performing Due Diligence on the listers. You will also need to display a link to the Paybase Terms that the workers will need to accept as part of your onboarding flow. Once you collect this information, you will be able to create the lister as a Customer on Paybase, and attach an Account and Bank Account to them.

Before you can create a Customer, you will need to know the following:

  • Whether the lister is an Individual, Sole Trader or Incorporated Business
  • Lister’s Role and Target CDD Level associated with it. The Target CDD Level of the Role will determine the minimum information required to complete due diligence on the lister for them to be able to start receiving payments. This is set-up by Paybase when your Sandbox account is created and can be found on Console.

To create a lister who is an individual, use the Create an Individual Customer request with the roleSlug as determined above. You will also need to provide the following details about the lister:

  • First Name
  • Last Name
  • Email
  • Phone Number
  • Full Residential Address
  • Date of Birth
  • Version of Paybase Terms accepted and the time of acceptance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const result = await client.createIndividualCustomer({
  roleSlug: "lister",
  profile: {
    firstName: "John",
    lastName: "Doe",
    dob: "1986-04-29T00:00:00.000Z",
    email: "john.doe@email.com",
    phoneNumber: "+442078137221",
    residentialAddress: {
      postalCode: "SE6 9YU",
      countryISO: "GB",
      houseNameNumber: "7",
      street: "Brick Lane",
      townCity: "London"
    }
  },
  terms: {
    acceptedAt: "2019-08-16T18:29:02.013Z",
    revision: "2.1"
  }
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/customers/individual",
  data = json.dumps({
    "roleSlug": "lister",
    "profile": {
      "firstName": "John",
      "lastName": "Doe",
      "dob": "1986-04-29T00:00:00.000Z",
      "email": "john.doe@email.com",
      "phoneNumber": "+442078137221",
      "residentialAddress": {
        "postalCode": "SE6 9YU",
        "countryISO": "GB",
        "houseNameNumber": "7",
        "street": "Brick Lane",
        "townCity": "London"
      }
    },
    "terms": {
      "acceptedAt": "2019-08-16T18:29:02.013Z",
      "revision": "2.1"
    }
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<X-Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/customers/individual",
  [
    "body" => "{
      \"roleSlug\": \"lister\",
      \"profile\": {
        \"firstName\": \"John\",
        \"lastName\": \"Doe\",
        \"dob\": \"1986-04-29T00:00:00.000Z\",
        \"email\": \"john.doe@email.com\",
        \"phoneNumber\": \"+442078137221\",
        \"residentialAddress\": {
          \"postalCode\": \"SE6 9YU\",
          \"countryISO\": \"GB\",
          \"houseNameNumber\": \"7\",
          \"street\": \"Brick Lane\",
          \"townCity\": \"London\"
        }
      },
      \"terms\": {
        \"acceptedAt\": \"2019-08-16T18:29:02.013Z\",
        \"revision\": \"2.1\"
      }
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<X-Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/customers/individual"  -H "Content-Type: application/json" \
  -H "X-Token: <X-Token goes here>" \
  -d '{
    "roleSlug": "lister",
    "profile": {
      "firstName": "John",
      "lastName": "Doe",
      "dob": "1986-04-29T00:00:00.000Z",
      "email": "john.doe@email.com",
      "phoneNumber": "+442078137221",
      "residentialAddress": {
        "postalCode": "SE6 9YU",
        "countryISO": "GB",
        "houseNameNumber": "7",
        "street": "Brick Lane",
        "townCity": "London"
      }
    },
    "terms": {
      "acceptedAt": "2019-08-16T18:29:02.013Z",
      "revision": "2.1"
    }
  }'

The response to a successful API call will be the full Customer object including the id, stateId, cddLevel and targetCDDLevel. Store the id in your database as you will need to pass it for any future requests associated with this Customer. The stateId will be returned as ENABLED. However, note that a Customer can only start transacting when their cddLevel equals their targetCDDLevel. The cddLevel will initially be returned as ZERO because the due diligence checks, whilst automated, are performed asynchronously. However, if automated checks pass, then the cddLevel of the Customer will be updated to the targetCDDLevel shortly after creation. You can monitor asynchronous events using webhooks.

The Customers page provides further guidance on creating other types of Customers such as Incorporated Businesses and Sole Traders.

Creating accounts for listers

With a lister created, you now need to create an Account for them. An Account on the Paybase platform is an entity that holds balances for Customers as e-money under the regulatory licence of Paybase. This is where your listers will be paid before they can manually or automatically withdraw their balance to their bank account.

When creating accounts and transactions for your customers, you will need to generate and use a separate customer authentication token instead of your normal API token.

With the customer authentication token, use the Create Account API to create a new Account and associate it with the lister by providing the previously saved Customer id in the ownerId attribute.

1
2
3
4
5
6
7
8
9
10
11
12
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c"
});

const result = await client.createAccount({
  ownerId: "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c",
  currencyISO: "GBP"
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/accounts",
  data = json.dumps({
    "ownerId": "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c",
    "currencyISO": "GBP"
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<Customer Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/accounts",
  [
    "body" => "{
      \"ownerId\": \"customer/82c6fb26-8027-454a-8451-ae37c0f9d72c\",
      \"currencyISO\": \"GBP\"
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/accounts"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    "ownerId": "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c",
    "currencyISO": "GBP"
  }'

The response to the API call will be the full Account object. You should store the account id as this will be required when you create transactions and interact with the account.

Adding bank accounts for listers

Finally, you should add the lister’s bank details for making withdrawals by adding a bank account for them. Provide the lister’s id in the ownerId attribute. You will need to use a customer authentication token for adding bank accounts.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "customer/0315c31b-b316-486f-bce1-90325f49286a"
});

const result = await client.createBankAccount({
  ownerId: "customer/0315c31b-b316-486f-bce1-90325f49286a",
  accountNumber: "41406760",
  sortCode: "309457",
  countryISO: "GB",
  currencyISO: "GBP",
  billingAddress: {
    postalCode: "SE6 9YU",
    countryISO: "GB",
    houseNameNumber: "7",
    street: "Brick Lane",
    townCity: "London"
  }
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/bank-accounts",
  data = json.dumps({
    "ownerId": "customer/0315c31b-b316-486f-bce1-90325f49286a",
    "accountNumber": "41406760",
    "sortCode": "309457",
    "countryISO": "GB",
    "currencyISO": "GBP",
    "billingAddress": {
      "postalCode": "SE6 9YU",
      "countryISO": "GB",
      "houseNameNumber": "7",
      "street": "Brick Lane",
      "townCity": "London"
    }
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<Customer Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/bank-accounts",
  [
    "body" => "{
      \"ownerId\": \"customer/0315c31b-b316-486f-bce1-90325f49286a\",
      \"accountNumber\": \"41406760\",
      \"sortCode\": \"309457\",
      \"countryISO\": \"GB\",
      \"currencyISO\": \"GBP\",
      \"billingAddress\": {
        \"postalCode\": \"SE6 9YU\",
        \"countryISO\": \"GB\",
        \"houseNameNumber\": \"7\",
        \"street\": \"Brick Lane\",
        \"townCity\": \"London\"
      }
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/bank-accounts"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    "ownerId": "customer/0315c31b-b316-486f-bce1-90325f49286a",
    "accountNumber": "41406760",
    "sortCode": "309457",
    "countryISO": "GB",
    "currencyISO": "GBP",
    "billingAddress": {
      "postalCode": "SE6 9YU",
      "countryISO": "GB",
      "houseNameNumber": "7",
      "street": "Brick Lane",
      "townCity": "London"
    }
  }'

The response to the API call will be the full bank account object. As the first bank account created for the lister, it will automatically be set as their preferred payment instrument and any automated withdrawals will be made to this bank account. If a customer has multiple bank accounts, any one of them can be set as preferred from the update bank account API.

To reduce friction during onboarding, you do not necessarily need to create bank accounts for listers as part of your initial onboarding flow, and can also do this when they withdraw funds for the first time. However, if you plan to automate withdrawals you will need to create bank accounts at the onboarding stage.

Onboarding users

Users refers to the participants on your platform who will rent the items or services listed on your platform. These will again be individuals or businesses depending on the type of platform you operate.

Similar to onboarding listers, you will need to build flows to collect the necessary information required to allow users to make payments on your platform. This will vary depending on whether you want your users to be able to pay with card or bank transfers.

Users will also be created on Paybase as Customers with the relevant role. If accepting only card payments, the Target CDD Level of the user’s role will have been set to zero. This means that you will only be required to provide the mandatory fields on the create customer API to create a user that can start making card payments. However, if you also wish to allow your users to pay in via bank transfers, Paybase will perform due diligence on the users and therefore the Target CDD Level of the role will usually be two.

In both cases, you will also need to display a link to the Paybase Terms that the users will need to accept as part of your onboarding flow.

Before you can create a user, you will need to know the following:

  • Whether the user is an Individual, Sole Trader or Incorporated Business
  • User’s Role and Target CDD Level associated with it.

To create a user who is an individual, use the Create an Individual Customer request with the roleSlug as determined above. You will also need to provide the following details about the user:

  • First Name
  • Last Name
  • Email
  • Phone Number
  • Full Residential Address
  • Date of Birth
  • Version of Paybase Terms accepted and the time of acceptance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const result = await client.createIndividualCustomer({
  roleSlug: "user",
  profile: {
    firstName: "John",
    lastName: "Doe",
    dob: "1986-04-29T00:00:00.000Z",
    email: "john.doe@email.com",
    phoneNumber: "+442078137221",
    residentialAddress: {
      postalCode: "SE6 9YU",
      countryISO: "GB",
      houseNameNumber: "7",
      street: "Brick Lane",
      townCity: "London"
    }
  },
  terms: {
    acceptedAt: "2019-08-16T18:29:02.013Z",
    revision: "2.1"
  }
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/customers/individual",
  data = json.dumps({
    "roleSlug": "user",
    "profile": {
      "firstName": "John",
      "lastName": "Doe",
      "dob": "1986-04-29T00:00:00.000Z",
      "email": "john.doe@email.com",
      "phoneNumber": "+442078137221",
      "residentialAddress": {
        "postalCode": "SE6 9YU",
        "countryISO": "GB",
        "houseNameNumber": "7",
        "street": "Brick Lane",
        "townCity": "London"
      }
    },
    "terms": {
      "acceptedAt": "2019-08-16T18:29:02.013Z",
      "revision": "2.1"
    }
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<X-Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/customers/individual",
  [
    "body" => "{
      \"roleSlug\": \"user\",
      \"profile\": {
        \"firstName\": \"John\",
        \"lastName\": \"Doe\",
        \"dob\": \"1986-04-29T00:00:00.000Z\",
        \"email\": \"john.doe@email.com\",
        \"phoneNumber\": \"+442078137221\",
        \"residentialAddress\": {
          \"postalCode\": \"SE6 9YU\",
          \"countryISO\": \"GB\",
          \"houseNameNumber\": \"7\",
          \"street\": \"Brick Lane\",
          \"townCity\": \"London\"
        }
      },
      \"terms\": {
        \"acceptedAt\": \"2019-08-16T18:29:02.013Z\",
        \"revision\": \"2.1\"
      }
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<X-Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/customers/individual"  -H "Content-Type: application/json" \
  -H "X-Token: <X-Token goes here>" \
  -d '{
    "roleSlug": "user",
    "profile": {
      "firstName": "John",
      "lastName": "Doe",
      "dob": "1986-04-29T00:00:00.000Z",
      "email": "john.doe@email.com",
      "phoneNumber": "+442078137221",
      "residentialAddress": {
        "postalCode": "SE6 9YU",
        "countryISO": "GB",
        "houseNameNumber": "7",
        "street": "Brick Lane",
        "townCity": "London"
      }
    },
    "terms": {
      "acceptedAt": "2019-08-16T18:29:02.013Z",
      "revision": "2.1"
    }
  }'

The response to a successful API call will be the full Customer object including the id, stateId, cddLevel and targetCDDLevel. Store the id in your database as you will need to pass it for any future requests associated with this Customer. The stateId will be returned as ENABLED. However, note that a Customer can only start transacting when their cddLevel equals their targetCDDLevel. If the role for your users has been set up to only use cards, their cdd level at creation will already be their target cdd level i.e. 0. If bank transfers are enabled, the cddLevel will initially be returned as ZERO because the due diligence checks, whilst automated, are performed asynchronously. If automated checks pass, then the cddLevel of the Customer will be updated to the targetCDDLevel shortly after creation. You can monitor asynchronous events using webhooks.

The Customers page provides further guidance on creating other types of Customers such as Incorporated Businesses and Sole Traders.

Adding cards for users

Card details should be collected from your users via Paybase Capture to ensure that sensitive card data doesn’t touch your servers and is transmitted securely to Paybase from the user’s device. Paybase Capture also automatically handles 3D Secure for you. Once the card has been added, a card id will be returned which you should store in order to use it for creating transactions.

Creating accounts for users

If your users will pay on your platform via bank transfers, you will also need to create an Account for them. Users will be able to deposit funds into this account and pay listers from their balance.

Generate a customer authentication token and use the Create Account API to create a new Account associating it with the user by providing the previously saved Customer id in the ownerId attribute.

1
2
3
4
5
6
7
8
9
10
11
12
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c"
});

const result = await client.createAccount({
  ownerId: "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c",
  currencyISO: "GBP"
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/accounts",
  data = json.dumps({
    "ownerId": "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c",
    "currencyISO": "GBP"
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<Customer Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/accounts",
  [
    "body" => "{
      \"ownerId\": \"customer/82c6fb26-8027-454a-8451-ae37c0f9d72c\",
      \"currencyISO\": \"GBP\"
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/accounts"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    "ownerId": "customer/82c6fb26-8027-454a-8451-ae37c0f9d72c",
    "currencyISO": "GBP"
  }'

The response to the API call will be the full Account object. You should store the account id as this will be required when you create transactions and interact with the account.

Adding bank accounts for users

For users paying in via bank transfers, you don’t need to explicitly collect the sending bank details upfront. When the user sends their first payment, a bank account object will automatically be created with the sender’s details and linked to the user as their preferred payment instrument. This bank account can later be used to process any refunds to the user. You can use the retrieve customer API to get the id of the preferred bank account of the user.

Creating conditional card payments from users to listers

Having created listers, and users with cards you can now start creating transactions from users to listers.

In the conditional card payment flow, the user’s card will be debited but funds will not be credited immediately to the worker’s account. They will instead be HELD until you make another API call to release this transaction based on any conditions that you choose to build on your UI (e.g. releasing a payment 24 hours after a guest has checked into a lister’s property, releasing a payment only when a user confirms an item has been received etc.)

You will need to generate a customer authentication token for the user and call the create inbound transaction API providing the user’s card id and the worker’s account id. The targetStateId of the transaction will need to be set to HELD.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "<- Sending Customer ID ->"
});

const result = await client.createInboundTransaction({
  accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
  amount: "500",
  method: "card",
  purpose: "PURCHASE",
  targetStateId: "HELD",
  paymentInstrumentId: "card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8"
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/txs/inbound",
  data = json.dumps({
    "accountId": "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
    "amount": "500",
    "method": "card",
    "purpose": "PURCHASE",
    "targetStateId": "HELD",
    "paymentInstrumentId": "card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8"
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<Customer Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/txs/inbound",
  [
    "body" => "{
      \"accountId\": \"account/b98774fc-3c41-43bc-bd03-13e327dcfbbb\",
      \"amount\": \"500\",
      \"method\": \"card\",
      \"purpose\": \"PURCHASE\",
      \"targetStateId\": \"HELD\",
      \"paymentInstrumentId\": \"card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8\"
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
12
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/txs/inbound"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    "accountId": "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
    "amount": "500",
    "method": "card",
    "purpose": "PURCHASE",
    "targetStateId": "HELD",
    "paymentInstrumentId": "card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8"
  }'

The response to a successful API request will be the full transaction object. The transaction state will be HELD indicating that the transaction was successfully debited from the user’s card.

If 3D Secure authentication is activated for transactions on your account, the transaction will be created in a PROCESSING state and the object will return the 3D Secure authentication URL in the requirements attribute. In this case, you will need to handle the 3DS transaction flow. When the 3DS flow is completed successfully, the state will be updated to HELD.

When you are ready to release this payment to the lister, generate a customer authentication token for the user and call the transition transaction status API providing the transaction id as a URI parameter. You will transition the transaction from a state of HELD to EFFECTED which will release the original transaction and credit it to the listers account.

1
2
3
4
5
6
7
8
9
10
11
12
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "<- Sending Customer ID ->"
});

const result = await client.transitionTransaction({
  id: "tx/a6af0aa7-2749-4715-b652-27c49b923421",
  toStateId: "EFFECTED"
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
import json
import requests

requests.patch(
  "https://api-json.sandbox.paybase.io/v1/tx/tx/a6af0aa7-2749-4715-b652-27c49b923421Params: id/state",
  data = json.dumps({
    "toStateId": "EFFECTED"
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<Customer token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$client = new \GuzzleHttp\Client();
$client->request(
  "patch",
  "https://api-json.sandbox.paybase.io/v1/tx/tx/a6af0aa7-2749-4715-b652-27c49b923421Params: id/state",
  [
    "body" => "{
      \"toStateId\": \"EFFECTED\"
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
curl -X PATCH \
"https://api-json.sandbox.paybase.io/v1/tx/tx/a6af0aa7-2749-4715-b652-27c49b923421Params: id/state"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer token goes here>" \
  -d '{
    "toStateId": "EFFECTED"
  }'

Alternatively, if you do not want to use the conditional payments workflow, you can simply create the initial inbound card transaction with the targetStateId set to EFFECTED. This will immediately debit the user’s card and credit funds to the lister.

Creating bank transfer payments from users to listers

Bank transfer payments involve users sending funds to their account on Paybase which are then credited to the lister’s account as a separate transaction.

The first step is to get the user to make a bank transfer to their account on Paybase. Call the Retrieve Reference API providing the account id of the user as a URI parameter.

1
2
3
4
5
6
7
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const result = await client.getReference({
  toId: "account/28958679-e8a8-47a8-967c-f979ae8509a2"
});

1
2
3
4
5
6
7
8
9
10
import json
import requests

requests.get(
  "https://api-json.sandbox.paybase.io/v1/reference/account/28958679-e8a8-47a8-967c-f979ae8509a2Params: toId",
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<X-Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
$client = new \GuzzleHttp\Client();
$client->request(
  "get",
  "https://api-json.sandbox.paybase.io/v1/reference/account/28958679-e8a8-47a8-967c-f979ae8509a2Params: toId",
  [
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<X-Token goes here>",
    ]
  ]
);

1
2
3
4
curl -X GET \
"https://api-json.sandbox.paybase.io/v1/reference/account/28958679-e8a8-47a8-967c-f979ae8509a2Params: toId"  -H "Content-Type: application/json" \
  -H "X-Token: <X-Token goes here>" \

The API call will return the bank details to which the user will need to make a transfer. This also includes a reference which will need to be provided on the bank transfer. You should display these bank details to the user instructing them to make a transfer with the reference provided. Since you provided an account id in your API request, the reference returned is a static account reference which should be used each time the user wants to deposit funds in their account. It is recommended that you highlight to your users the importance of sending the transfer with the accurate reference as otherwise the transaction will automatically be returned to the sender. Allowing users to easily copy the reference on your UI is often useful.

In Sandbox, you can simulate an incoming bank transfer from your users for testing purposes.

Once the funds are credited on the user’s account, generate a customer authentication token for the user and create an internal transaction from the user’s account to the lister’s account. You will need to provide their respective account id’s in the fromAccountId and toAccountId attributes. If you want to create a conditional payment as in the card payment example above, set the targetStateId to HELD.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "<- Sending Customer ID ->"
});

const result = await client.createInternalTransaction({
  fromAccountId: "account/46092ec4-83d1-4ed9-845e-a942078e62cf",
  toAccountId: "account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f",
  amount: "1000",
  targetStateId: "HELD"
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/txs/internal",
  data = json.dumps({
    "fromAccountId": "account/46092ec4-83d1-4ed9-845e-a942078e62cf",
    "toAccountId": "account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f",
    "amount": "1000",
    "targetStateId": "HELD"
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<Customer Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/txs/internal",
  [
    "body" => "{
      \"fromAccountId\": \"account/46092ec4-83d1-4ed9-845e-a942078e62cf\",
      \"toAccountId\": \"account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f\",
      \"amount\": \"1000\",
      \"targetStateId\": \"HELD\"
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/txs/internal"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    "fromAccountId": "account/46092ec4-83d1-4ed9-845e-a942078e62cf",
    "toAccountId": "account/b199c5ac-46dd-457e-a86b-c2d9e0a39c3f",
    "amount": "1000",
    "targetStateId": "HELD"
  }'

When you are ready to release this payment to the lister, generate a customer authentication token for the user and call the transition transaction status API providing the transaction id as a URI parameter. You will transition the transaction from a state of HELD to EFFECTED which will release the original transaction and credit it to the listers account.

Alternatively, if you do not want to use the conditional payments workflow, you can simply create the internal transaction with the targetStateId set to EFFECTED. This will immediately debit the user’s account and credit funds to the lister.

Charging fees on transactions

So far, you have been able to onboard your users and listers, and create conditional transactions between users and listers. The full amount of the transaction was sent to listers, however you might want to charge your platform fees on these transactions.

Paybase’s fee rules allow you to configure how you charge fees at one or multiple stages of the payment flows. For example, you want to charge your users a fee when they make a booking and also deduct a fee from listers on each payment they receive. Fee rules can be configured on your account by Paybase, and you will be given an ‘annotation key’ for each rule (e.g. user_fee, lister_fee).

Fees are specified at the time of setting up a transaction and therefore the structure of the API call will differ slightly.

Generate a customer authentication token for the user and call the create inbound transaction API providing the user’s accountId and a targetStateId as HELD. This time you will provide annotations as in the example below to indicate the user and listers fees that you want to deduct.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "<- Sending Customer ID ->"
});

const result = await client.createInboundTransaction({
  accountId: "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
  amount: "500",
  method: "card",
  purpose: "PURCHASE",
  targetStateId: "HELD",
  paymentInstrumentId: "card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8",
  annotations: {
    user_fee_1: "10",
    lister_fee_1: "20"
  }
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/txs/inbound",
  data = json.dumps({
    "accountId": "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
    "amount": "500",
    "method": "card",
    "purpose": "PURCHASE",
    "targetStateId": "HELD",
    "paymentInstrumentId": "card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8",
    "annotations": {
      "user_fee_1": "10",
      "lister_fee_1": "20"
    }
  }),
  headers = {
    "Content-Type": "application/json",
    "X-Token": "<Customer Token goes here>"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/txs/inbound",
  [
    "body" => "{
      \"accountId\": \"account/b98774fc-3c41-43bc-bd03-13e327dcfbbb\",
      \"amount\": \"500\",
      \"method\": \"card\",
      \"purpose\": \"PURCHASE\",
      \"targetStateId\": \"HELD\",
      \"paymentInstrumentId\": \"card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8\",
      \"annotations\": {
        \"user_fee_1\": \"10\",
        \"lister_fee_1\": \"20\"
      }
    }",
    "headers" => [
      "Content-Type" => "application/json",
      "X-Token" => "<Customer Token goes here>",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/txs/inbound"  -H "Content-Type: application/json" \
  -H "X-Token: <Customer Token goes here>" \
  -d '{
    "accountId": "account/b98774fc-3c41-43bc-bd03-13e327dcfbbb",
    "amount": "500",
    "method": "card",
    "purpose": "PURCHASE",
    "targetStateId": "HELD",
    "paymentInstrumentId": "card/6c1633c1-db04-4c40-b9f2-a38c65b2b7b8",
    "annotations": {
      "user_fee_1": "10",
      "lister_fee_1": "20"
    }
  }'

As in the previous example, when you are ready to release this payment to the lister, generate a customer authentication token for the user and call the transition transaction status API providing the transaction id as a URI parameter. You will transition the transaction from a state of HELD to EFFECTED which will release the original transaction and credit it to the listers account after debiting the fees and transferring them to your own account.

Making withdrawals for listers

With listers having been paid in their accounts, they need to be able to withdraw these funds to their bank account.

Generate a customer authentication token for the lister and call the create outbound transaction API providing the lister’s accountId to indicate the source account and the lister’s bank account id as the paymentInstrumentId.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { v1 } from '@paybase/client';

const client = v1('<- API Key ->', { sandbox: true });

const { accessToken } = await client.createCustomerAuthenticationToken({
  id: "<- Sending Customer ID ->"
});

const result = await client.createOutboundTransaction({
  accountId: "account/8810a798-3141-11ea-978f-2e728ce88125",
  paymentInstrumentId: "bank_account/7ef61227-0087-434f-bf8d-408c552a4efc",
  amount: "990"
}, { apiKey: accessToken });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import json
import requests

requests.post(
  "https://api-json.sandbox.paybase.io/v1/txs/outbound",
  data = json.dumps({
    "accountId": "account/8810a798-3141-11ea-978f-2e728ce88125",
    "paymentInstrumentId": "bank_account/7ef61227-0087-434f-bf8d-408c552a4efc",
    "amount": "990"
  }),
  headers = {
    "X-Token": "<... snip ...>",
    "Content-Type": "application/json"
  }
).json()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$client = new \GuzzleHttp\Client();
$client->request(
  "post",
  "https://api-json.sandbox.paybase.io/v1/txs/outbound",
  [
    "body" => "{
      \"accountId\": \"account/8810a798-3141-11ea-978f-2e728ce88125\",
      \"paymentInstrumentId\": \"bank_account/7ef61227-0087-434f-bf8d-408c552a4efc\",
      \"amount\": \"990\"
    }",
    "headers" => [
      "X-Token" => "<... snip ...>",
      "Content-Type" => "application/json",
    ]
  ]
);

1
2
3
4
5
6
7
8
9
curl -X POST \
"https://api-json.sandbox.paybase.io/v1/txs/outbound"  -H "X-Token: <... snip ...>" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "account/8810a798-3141-11ea-978f-2e728ce88125",
    "paymentInstrumentId": "bank_account/7ef61227-0087-434f-bf8d-408c552a4efc",
    "amount": "990"
  }'

The API call will create an outbound payment to the lister’s bank account via Faster Payments in the UK.

Automating withdrawals for listers

You can also use Paybase’s Triggered Withdrawal rule to automate withdrawals for your listers. This will result in an outbound payment being executed automatically to the lister’s bank account as soon as they receive funds in their Paybase account from a user.

Once the rule has been configured on your account by Paybase, you don’t need to take any additional steps. Simply create a transaction from the user to the lister’s account as you normally would, and the rule will automatically create an outbound transaction to the lister’s bank account as soon as any funds are credited to their account.

Note that you may still want to consider building UI to handle manual withdrawals if automated withdrawals fail (for e.g. due to incorrect bank details, fraud rules etc.)

Webhooks

You should also ensure that you have a webhook set up to listen to some events that will allow you to provide a better experience to your users. Paybase will whitelist the URLs to which you want events to be posted.

In particular, you should consider listening to the following webhooks:

  • customer_cdd_level_changed to keep track of the cdd level of customers and know when they can start transacting
  • check_failed to be notified if any of the automated due diligence checks failed and therefore require additional information
  • alert_action_required if an alert has been raised that requires actioning on the Console before the associated event can be completed (e.g. transaction paused, customer verification failed etc.)
  • transaction_failed to monitor any transactions that failed, particular those that were refunded to sender due to an incorrect reference
  • account_balance_updated to know when a user’s bank transfer has been credited to their Paybase account

Other considerations

  • Strong Customer Authentication - You should ensure that your platform complies with SCA requirements as detailed in the SCA guide.
  • Front End Checklist - Please refer to general guidance on front end requirements that you need to comply with when building your platform.
  • Handle Refunds to make sure you can provide refunds to your users if required.
  • Uploading Documents - useful for uploading additional verification documents required when automated checks fail. This can also be done manually on the Console.