Single immediate payments
This section describes how to setup, execute, and monitor progress of a Single Immediate Payment.
A single immediate payment is a one time payment, executed immediately, that requires the user to authenticate with their bank.
Below are instructions for how to:
- Build a provider selection screen for your users to select their bank
- Create a payment
- Redirect a user to authenticate
- Handle the user returning to your application
- Poll for payment status
Build a provider selection screen
The providers endpoint serves two purposes:
- Tells you which banks are active, if a bank isn't listed then we have deactivated it temporarily until its service is returned.
- Gives you the assets to build out a selection screen, for example, logos and icons.
When querying the endpoint, specify capability
in the query string as SingleImmediatePayment
.
Each bank available will have the following fields:
Field | Type | Description |
---|---|---|
id | string | This is the provider ID that you will send us in the create payment request. |
logo | string | This is the address of the logo asset in SVG form. |
icon | string | This is the address of the icon asset in SVG form. |
displayable_name | string | This is a readable name for the provider. |
main_bg_color | string | This is a hexacode color that matches the background of the logo. |
supports_app_to_app | bool | This indicates whether the user will be redirected to the mobile banking app if using their mobile phone. In cases where this is false, the user authenticate in their browser. |
divisions | string array | This array includes all divisions that are available on this provider, for example, retail and business would indicate you can use this single provider to access both retail and business accounts. |
steps | array | This array includes any extra steps in the authentication flow that you are required to send your end user through with some banks. The results of these steps will be used in your create payment request. |
We recommend you query this endpoint every single time you make a payment as we will remove banks that are not currently available.
• The steps
parameter structure
Field | Type | Description |
---|---|---|
title | string | Friendly name for this method within the provider |
fields | array | Array of input fields |
fields.type | string | Input field type. Can be a Single Choice Input or a Free Input |
fields.is_sensitive | boolean | OPTIONAL: For Free Input Values, is set to true for sensitive information like passwords |
fields.values | array | OPTIONAL: For Single Choice Input, an array of values |
fields.values.value | string | OPTIONAL: For Single Choice Input, input field value |
fields.values.display_name | string | OPTIONAL: For Single Choice Input, friendly name for this input field value |
fields.validations | array | OPTIONAL: For Free Input Values, an array of RegEx validation rules that need to be satisfied to consider the input valid |
fields.allowed_characters | string | OPTIONAL: For Free Input Values, character ranges allowed. can be alphanumeric or numeric |
fields.id | string | Unique id for this field within the method |
fields.display_name | string | Friendly name for this field within the method |
fields.help_text | string | Input help text for the end user |
fields.mandatory | boolean | Indicates whether the field is required |
https://pay-api.truelayer.com/providers?capability=SingleImmediatePayment
{
"results":[
{
"id":"ob-sandbox-natwest",
"logo":"example.com",
"icon":"example.com",
"displayable_name":"Natwest Sandbox",
"main_bg_color":"#eee",
"supports_app_to_app":false,
"divisions":["retail"]
}
]
}
Create a payment
This method includes the following fields:
Field | Type | Mandatory/Optional | Description |
---|---|---|---|
amount | int | Mandatory | The amount of money you are requesting in pennies. |
currency | string | Mandatory | The currency code of the payment in three characters. For example, GBP or EUR. |
beneficiary_reference | string | Mandatory | The reference that will appear on your bank statement. Note: this can only be 18 characters or less. |
beneficiary_sort_code | string | Mandatory | The sort code of the receiving account (your account) if the account is a UK account (see below for EU). |
beneficiary_account_number | string | Mandatory | The account number of the receiving account (your account) if the account is a UK account (see below for EU). |
beneficiary_iban | string | Conditional | If making payments in Central European countries the beneficiary IBAN is a compulsory field and replaces the account number and sort code. |
remitter_reference | string | Mandatory | The reference that will appear on the sender's bank statement. Note: this can only be 18 characters or less. |
beneficiary_name | string | Mandatory | The name on the receiving account. |
redirect_uri | string | Mandatory | The URL we will redirect the user to after authorising the payment. |
remitter_provider_id | string | Optional / Conditional | The ID from the providers endpoint of the bank your user has chosen. If this isn't specified the user will be sent to TrueLayer to choose their bank in the UK. For banks outside of the UK, you will need to set this. |
remitter_account_number | string | Optional | The account number of the payer, specify this if you want to lock the account from which the payment is being made if the account is a UK account (see below for EU). This may be an AML requirement. |
remitter_sort_code | string | Optional | The sort code of the payer, specify this if you want to lock the account from which the payment is being made if the account is a UK account (see below for EU). This may be an AML requirement. |
remitter_iban | string | Conditional | If making payments in Central European countries the remitter IBAN is a compulsory field. |
direct_bank_link | bool | Optional / Conditional | If set to true, you will be returned a URI directly from the bank. !WARNING! If using this, you will skip all TrueLayer optimisation and conversion may be lower on desktop. You must specify remitter_provider_id for this request to succeed. This is a required field for European Banks. |
webhook_uri | string | Optional | An address to send payment webhooks to with the status of your payment. This has to be https. |
auth_inputs | dictionary | Conditional | If the providers endpoint returns you required steps you will need to list them and their answers here. For example, banks in France require you to specify the branch name. |
curl -X POST \
-H "Authorization: Bearer ${access_token}" \
--data '{
"amount": 120000,
"currency": "GBP",
"remitter_provider_id": "ob-bank",
"remitter_name": "Mike Smith",
"remitter_sort_code": "987654",
"remitter_account_number": "98765432",
"remitter_reference": "FS-1000001",
"beneficiary_name": "Financial Services Ltd",
"beneficiary_sort_code": "234567",
"beneficiary_account_number": "23456789"
"beneficiary_reference": "FinServ-1a2b3c4d",
"redirect_uri": "https://client.app.com/payment_redirect"
}' \
https://pay-api.truelayer.com/single-immediate-payments
The request will return a simp_id
that you should store as, after authorisation, the user will be redirected to your redirect_uri with this appended as payment_id
.
Use the auth_uri
to redirect your user to authorise the payment.
{
"results": [
{
"simp_id": "44708581-1967-4120-8f6a-1e532f1bf52a",
"auth_uri": "https://example.com",
"created_at": "2018-10-01T17:00:00.0000000+00:00",
"amount": 120000,
"currency": "GBP",
"remitter_provider_id": "ob-bank",
"remitter_name": "Mike Smith",
"remitter_sort_code": "987654",
"remitter_account_number": "98765432",
"remitter_reference": "FS-1000001",
"beneficiary_name": "Financial Services Ltd",
"beneficiary_sort_code": "234567",
"beneficiary_account_number": "23456789",
"beneficiary_reference": "FinServ-1a2b3c4d",
"redirect_uri": "https://client.app.com/payment_redirect",
"status": "new"
}
]
}
### Adapting an existing integration for EU payments (Private beta)
If you've already implemented the payments API and would now like to use our European payment capability, here are some key considerations to help you transition:
- Instead of account numbers and sort codes, you need to use our IBAN fields.
- Both the remitter and beneficiary IBANs are compulsory fields to create a payment with a European Bank in Euros.
- You'll need to change the currency code to EUR.
- European banks can only pay to European bank accounts.
- Some banks have additional steps required (these appear on their provider in the providers endpoint result you get back) and you'll need to complete these steps before creating the payment and pre-fill the
auth_inputs
field. - The remitter provider ID is a compulsory field.
direct_bank_link
is a compulsory field.
Redirecting your user to the bank
After creating the payment, you need to redirect the user to the auth_uri
.
If you did not specify the remitter_provider_id
your user will be redirected to the TrueLayer Provider Selection screen and from there on to their bank.
Handling a returning user
Once the payment is authorised, the user will return to your redirect_uri
. The URL will include a payment_id
query parameter containing the simp_id
from the payment creation response.
https://client.app.com/payment_redirect?payment_id=a55f2e12-55ef-410c-9341-628033a62dc3
Managing payment status with webhooks
When the status of a payment changes, we send a single_immediate_payment_status_changed
event to the uri that you specified when creating the payment.
In the header you will see the following fields:
Field | Type | Description |
---|---|---|
X-TL-Webhook-Timestamp | timestamp | time that the webhook was sent to you. This will be in the following format 2020-05-18T10:17:47Z |
Tl-Signature | string | JSON web signature with a detached payload of the form {HEADER}..{SIGNATURE} |
X-Tl-Signature | string | Legacy JWS, verification using Tl-Signature should be preferred. |
In the body you will see the following fields:
Field | Type | Description |
---|---|---|
event_type | string | describing which event is detailed. In this case the event will be single_immediate_payment_status_changed |
event_body | object | containing both the payment_id as a string and the status as a string |
Tl-Signature: "detached..jws"
X-TL-Webhook-Timestamp: 2020-05-18T10:17:52Z
{
"event_type": "single_immediate_payment_status_changed",
"event_body": {
"payment_id": "77a75df0-af60-4785-8e91-809ac77ca8e3",
"status": "executed"
}
}
Webhook Retry Policy
We consider a webhook as having been successfully delivered when we receive a success status code (2xx) from your webhook URI.
If we receive any other status code (for instance, if your API is temporarily unavailable), we will start retrying. Our retry policy is jittered exponential backoff. We will immediately perform some fast retries and then start waiting increasingly longer. We will keep retrying for up to 72 hours.
Validate the received webhook signature
We recommend developers to use our signing libraries to verify the Tl-Signature
of the received webhooks.
E.g. Java com.truelayer.truelayer-signing
Verifier.verifyWithJwks(jwks)
.method("POST")
.path(path)
.headers(allWebhookHeaders)
.body(body)
.verify(webhookSignature);
Polling for payment status
The application can poll the payment resource to see if the status has been modified. This allows the application to know if the payment was successful or not.
Note that if the remitter was specified, remitter details will also be included in the response.
The payment resource may exist in different states according to the progress of the transaction:
new
: The payment resource has been created, but the payer has not yet authorised the payment. The initial API response will always have this status if the API call is successful.authorised
: The payer has successfully authorised the payment using the bank portal, but the payment has not yet been submitted for execution.cancelled
: The payer has cancelled the payment using the bank portal. This may also happen for some bank UIs if the payer clicks the “Back” button in their browser. This is a terminal state.failed
: The bank has failed to initiate a payment session, this happens after a bank has been selected but before redirecting the user to the bank UI. This is a terminal state.rejected
: The payer authorised the payment, but the bank rejected it after this. This is a terminal state.submitted
: TrueLayer has successfully submitted the authorised payment initiation request to the bank API.executed
: The funds have left the remitter’s account and will arrive in the beneficiary’s account. This is a terminal state.
The submitted
and executed
states are very close; in fact a bank may respond to the payment submission indicating the payment has already been executed. However, it is also possible for a payment to be queued within the bank for a short period of time; in that case, the payment status will initially be submitted, and then may be updated to executed after polling for a status check.
Note that after a payment has been executed, it may take some time for it to be settled, although for transactions executed via Faster Payments, this normally happens very quickly.
curl -H "Authorization: Bearer ${access_token}" \
https://pay-api.truelayer.com/single-immediate-payments/a55f2e12-55ef-410c-9341-628033a62dc3
{
"results": [
{
"simp_id": "c45d38a6-2384-49aa-98ab-60134a50a5d7",
"created_at": "2018-10-01T16:00:00.0000000+00:00",
"amount": 350,
"currency": "GBP",
"beneficiary_reference": "ecommerce-12345",
"beneficiary_name": "Ecommerce Shop",
"beneficiary_sort_code": "102030",
"beneficiary_account_number": "88881234",
"remitter_reference": "ECOM-12345-ABCD",
"redirect_uri": "http://www.ecommerce.com/redirect",
"remitter_provider_id": "ob-lloyds",
"status": "executed"
}
]
}
To see all known states of your payment and trace your payment with the timestamps, add /statuses
to the end of the request.
curl -H "Authorization: Bearer ${access_token}" \
https://pay-api.truelayer.com/single-immediate-payments/a55f2e12-55ef-410c-9341-628033a62dc3/statuses
{
"results": [
{
"status": "New",
"date": "2020-04-08T21:53:35.364038"
},
{
"status": "Authorised",
"date": "2020-04-08T21:54:17.690429"
},
{
"status": "Submitted",
"date": "2020-04-08T21:54:18.546706"
},
{
"status": "Executed",
"date": "2020-04-08T21:54:28.063325"
},
{
"status": "Executed",
"date": "2020-04-08T21:54:28.2761"
}
],
"status": "queued"
}
Testing Guidelines
The following guidelines aim to provide practical guidance while testing the integration.
Before testing with live accounts, we recommend that you test your integration with the sandbox. The Payments Quick Start provides instructions on how to access the sandbox APIs.
When you transition to live testing in the production environment:
- Make sure you change all URIs to end with
truelayer.com
instead oftruelayer-sandbox.com
- Change from using your sandbox
client_id_
andclient_secret
to your productionclient_id
andclient_secret
. You can find your productionclient_id
here: https://console.truelayer.com/settings/Application (you should have saved your productionclient_secret
when you last reset it, or when you created your production account) - Have a live bank account to hand which you own to make payments from, as well as one to make payments into. The payer (or remitter) bank account must be from one of the supported providers listed here
Bank Account Limits
Each bank has its own limits on how much money can be transferred using online banking, which also apply to their open banking payment APIs.
Velocity Limits
All banks have controls in place to protect their end users from fraudulent withdrawals. One type of control is a velocity limit, which allows only a certain number of payments, or a maximum aggregate payment amount, per time period, for example, one hour, one day, and so on.
Banks will not publish these limits, as the information could be taken advantage of by fraudsters. Therefore, when testing:
- Do not make an excessive number of payments from the same account within a short time period.
- If it is not possible to make a successful payment, this will be visible in the bank’s authorisation user interface. At this point, it will be necessary to contact the specific bank’s end user support team to get the account unblocked.
Updated 2 months ago