Make a payment [Payments V1]
This section describes how to set up, execute, and monitor the 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.
about configuring single immediate payments, and also testing your integration.
Single immediate payments
To set up, execute and monitor payments:
- Build a provider selection
- 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 provider selection screen is where your user chooses the provider they want to pay with.
The /v2/single-immediate-payments-providers
endpoint:
- returns a list of which providers are active
- provides the assets you need to build a provider selection screen, such as logos and icons
When querying the endpoint, specify capability
in the query string as SingleImmediatePayment
.
Response
Each available bank returned has the following fields.
Field | Type | Description |
---|---|---|
id | string | The provider ID to send us in the create payment request. |
logo | string | The address of the logo asset in SVG form. |
icon | string | The address of the icon asset in SVG form. |
displayable_name | string | A human-readable name for the provider. |
main_bg_color | string | The hex code of the colour in the background of the logo. |
supports_app_to_app | bool | If true, this means the user will be redirected to their mobile banking app if they are using their mobile phone. /n/n If false, the user authenticates in their browser. |
divisions | string array | An array that includes all divisions that are available on this provider. For example, retail and business would indicate you can use this provider to access both retail and business accounts. |
steps | array | This array includes any extra steps in the authentication flow required to send your end user through with some banks. The results of these steps will be used in your request to create a payment. |
We do not recommend that you store the information from the /v2/single-immediate-payments-providers
endpoint locally. This is because TrueLayer occasionally updates the response returned by the endpoint.
Structure of the steps
parameter
steps
parameterField | Type | Required? | Description |
---|---|---|---|
title | string | Yes | User-friendly name for this method within the provider. |
fields | array | Yes | Array of input fields. |
fields.type | string | Yes | Input field type. Can be a single choice input or a free input. |
fields.is_sensitive | boolean | No | For free input values, is set to true if a field requires the user to input sensitive information (like a password). |
fields.values | array | No | For single choice input, an array of values. |
fields.values.value | string | No | For single choice input, input field value. |
fields.values.display_name | string | No | For single choice input, a user-friendly name for this input field value. |
fields.validations | array | No | For free input values, an array of RegEx validation rules that need to be satisfied to consider the input valid. |
fields.allowed_characters | string | No | For Free Input Values, character ranges allowed. Can be alphanumeric or numeric. |
fields.id | string | Yes | Unique id for this field within the method. |
fields.display_name | string | Yes | User-friendly name for this field within the method. |
fields.help_text | string | Yes | Input help text for the end user. |
fields.mandatory | boolean | Yes | Indicates whether the field is required. |
https://pay-api.truelayer.com/providers?capability=SingleImmediatePayment
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 beneficiary account (your account) if the account is a UK account (see below for EU). |
beneficiary_account_number | string | Mandatory | The account number of the beneficiary 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 user's bank statement. Note: this can only be 18 characters or less. |
beneficiary_name | string | Mandatory | The name on the beneficiary 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 provider your user has chosen. If this isn't specified, the user will be sent to TrueLayer to choose their provider in the UK. For providers 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 that the payment is being made from, and 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 that the payment is being made from, and if the account is a UK account (see below for EU). This may be an AML requirement. |
remitter_iban | string | Conditional | If you are making payments in Central European countries, this is a compulsory field. |
direct_bank_link | bool | Optional / Conditional | If set to true, a URI directly from the bank will be returned. Note: If using this, you will skip all TrueLayer optimisation. Conversion may be lower on desktop. You must specify the 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
: store this. Once they are authorised, 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
If you've already implemented the Payments API and would now like to use our European payment capabilities, 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 alongside the provider in the
/providers
endpoint result you get back). You'll need to complete these steps before creating the payment and prefill theauth_inputs
field. - The remitter provider ID is a compulsory field.
direct_bank_link
is a compulsory field.
Redirecting a user to authenticate
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 to their bank.
Handle the user returning to your application
Once the payment is authorised, the user will return to your redirect_uri
. The URL includes a payment_id
query parameter that contains 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.
The header has the following fields:
Field | Type | Description |
---|---|---|
X-TL-Webhook-Timestamp | timestamp | The time that the webhook was sent to you. It should look like this: 2020-05-18T10:17:47Z |
Tl-Signature | string | JSON web signature with a detached payload of the form {HEADER}..{SIGNATURE} |
X-Tl-Signature | string | This is legacy JWS. Verification using Tl-Signature is preferred. |
The body has the following fields:
Field | Type | Description |
---|---|---|
event_type | string | Describes the event. In this case, the event will be single_immediate_payment_status_changed . |
event_body | object | Contains 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 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 webhooks they receive.
E.g. Java com.truelayer.truelayer-signing
Verifier.verifyWithJwks(jwks)
.method("POST")
.path(path)
.headers(allWebhookHeaders)
.body(body)
.verify(webhookSignature);
Poll 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 are 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
: TrueLayer has successfully submitted the payment to the bank and the bank has confirmed it as accepted. This does not mean that the payment will settle in the creditor’s account.
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
After a payment has been executed, it may take some time for it to be settled — but 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
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
Testing guidelines
Before testing with live accounts, we recommend that you test your integration in sandbox.
When you transition to live testing in the production environment:
- Ensure 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. Make sure that you have stored your productionclient_secret
since you last reset it. - Have two live bank accounts that you own to hand: one to make payments from, and one to make payments into. The payer bank account must be from one of the supported providers listed here
Bank account limits
Providers set their own limits on how much money can be transferred using online banking. These 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. This allows only a certain number of payments, or a maximum aggregate payment amount, per time period (for example, per hour or per day).
Banks don't publish these limits, since fraudsters might take advantage of this information. Therefore, when testing:
- Do not make too many 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 UI. If this happens, you need to contact the bank’s support team to unblock the account.
Updated 6 months ago