PayDirect to Payments v3 API: payouts and refunds
Send money out of your merchant account, and refund a specific pay-in.
With Payments API V3, both types of payouts supported by PayDirect are still available:
- closed-loop payouts: when the payout is sent to a user who already made a deposit into the merchant account.
- open-loop payouts: when the payout is sent to a new, “unknown” bank account.
We've also introduced a new closed-loop payout API with v3:
- Refunds: when you're refunding a specific payment into your merchant account, partially or fully. The amount refunded cannot be greater than the original pay-in.
Closed-loop payouts
PayDirect endpoint | Payments v3 endpoint |
---|---|
https://paydirect.truelayer.com/v1/users/withdrawals | https://api.truelayer.com/payouts |
To make a closed-loop payout in v3, you only need to specify a payment_source.id
as the beneficiary. As in the payment_settled
webhook, the payment_source.id
is a unique ID TrueLayer creates that refers to the original bank account that the user deposited funds from. This ensures that we can send funds to the exact same bank account that was used to deposit.
This table shows which values in a PayDirect request correspond to which values in a Payments API v3 request.
PayDirect request object | Payments v3 request object equivalent |
---|---|
account_id | beneficiary.payment_source_id |
user_id | beneficiary.user_id |
beneficiary_reference | beneficiary.reference |
transaction_id | This is not used in Payments v3 |
A merchant account ID is not required in PayDirect | merchant_account_id |
Below is an example:
Below is an example request and response:
{
"merchant_account_id": "01829515-3525-4812-a6c0-6d9e195c947e",
"amount_in_minor": 1000,
"currency": "GBP",
"metadata":
{
"brand": "TL Casino"
},
"beneficiary":
{
"type": "payment_source",
"user_id": "4d9af4be-f75b-4ca7-be9b-b842bf7237c1",
"payment_source_id": "1c928e0e-6dc9-497c-8154-83dce43b2377",
"reference": "Payout0001"
}
}
Here:
beneficiary.payment_source_id
is returned from the pay-in webhook (or GET status) once the payment has been settled.beneficiary.user_id
is the sameuser.id
used in payment creation. It can also be returned from the pay-in webhook (or GET status) once the payment has been settled.
{
"id": "5016c85d-de28-406d-9866-0a3ed85af3b1"
}
See our documentation for more about closed-loop payouts.
Refunds
If you are issuing refunds using PayDirect APIs, Payments v3 makes it a lot easier with a dedicated endpoint for refunds.
The Refunds API is similar to, but not the same as, closed-loop payouts. For refunds:
- The maximum amount you can send is the amount of the pay-in being refunded.
- The only information required for the Refund API request is the
payment_id
. - It is possible to issue multiple refunds for the same pay-in, as long as they don't exceed the original pay-in amount.
See our documentation for more about refunds.
PayDirect to closed-loop payouts and refunds
This diagram is meant to show:
- On the left (grey underground), a PayDirect API payout (withdrawal) example.
- On the center (blue underground), the equivalent of the Payout (withdrawal) API call in v3. Below is an example instead of the Refunds API (which as you can see is instead using the payment_id in the URI for reconciliation and refund up to the original amount).
- On the right (black background), is an example of a Pay-In settled into the Merchant Account, the information incoming from it, and how those are used for then executing Payouts or Refunds.
Go to our Figma prototype to see a larger version of this image.
Open-loop payouts
PayDirect endpoint | Payments v3 endpoint |
---|---|
https://paydirect.truelayer.com/v1/users/withdrawals | https://api.truelayer.com/payouts |
In Payments v3, you can still also make payouts to any supported bank account by using the beneficiary type external_account
and submitting the relevant account details. This is called an open-loop payout.
PayDirect request object | Payments v3 request object equivalent |
---|---|
beneficiary_iban | beneficiary.account_identifier.iban or beneficiary.account_identifier.sort_code and beneficiary.account_identifier.account_number |
beneficiary_name | beneficiary.account_holder_name |
beneficiary_reference | beneficiary.reference |
transaction_id | This is not used in Payments v3 |
A merchant account ID wasn't required in PayDirect | merchant_account_id |
TrueLayer conducts sanction and AML screening on all transactions to and from your merchant account. To reduce the number of RFIs raised due to sanctions investigations, you must also include the user's date of birth and address in the API request.
Below is an example request and response for a v3 open-loop payout:
{
"merchant_account_id": "c6ba13f2-35ef-4556-a99e-a6afd6275044",
"amount_in_minor": 100,
"currency": "GBP",
"beneficiary":
{
"type": "external_account",
"reference": "Withdrawal",
"account_holder_name": "John Smith",
"account_identifier":
{
"type": "sort_code_account_number",
"sort_code": "<sort code>",
"account_number": "<account number>"
},
"date_of_birth": "1992-08-03",
"address":
{
"address_line1": "1 Hardwick St",
"address_line2": "Clerkenwell",
"city": "London",
"state": "London",
"zip": "EC1R 4RB",
"country_code": "GB"
}
},
"metadata":
{
"prop1": "value1",
"prop2": "value2"
}
}
{
"id": "f5a6c87e-446a-4dcc-a91e-a74e3e4e0875"
}
Consume payouts webhooks and payout statuses
This diagram shows possible payout statuses:
Status | Description | Notification method |
---|---|---|
pending | The payout has been created with TrueLayer's API. It has not yet been authorised and sent to the payment scheme for execution. | Status only via /GET /payouts/{payment_id} |
authorized | The payout has been created via TrueLayer's API and has been sent to the payment scheme for execution. | Status only via /GET /payouts/{payment_id} |
payout_executed | The payout has been executed by TrueLayer. The payout amount has been deducted from your merchant account. This does not indicate that the payment amount has been credited to the destination account. | Status via /GET /payouts/{payment_id} Webhook sent to your endpoint |
payout_failed | The payout failed. The payout amount has not been deducted from your merchant account. | Status via /GET /payouts/{payment_id} Webhook sent to your endpoint |
We recommend that developers use our signing libraries to verify the
Tl-Signature
of the received webhooks.Webhook URLs need to be added in the TrueLayer Console. See Setting up the TrueLayer Console.
Webhook examples:
{
"type": "payout_executed",
"event_id": "1b712c55-6bc4-487c-8a5b-2107623fe3bf",
"event_version": 1,
"payout_id": "5016c85d-de28-406d-9866-0a3ed85af3b1",
"executed_at": "2022-09-14T16:39:50.593Z",
"beneficiary":
{
"type": "payment_source",
"user_id": "4d9af4be-f75b-4ca7-be9b-b842bf7237c1",
"payment_source_id": "1c928e0e-6dc9-497c-8154-83dce43b2377"
},
"metadata":
{
"brand": "TL Casino"
}
}
{
"type": "payout_failed",
"event_id": "f8a144dc-a550-4681-844b-4de231ec3764",
"event_version": 1,
"payout_id": "f8a144dc-a550-4681-844b-4de231ec3764",
"failed_at": "2022-09-14T16:41:21.006718283Z",
"failure_reason": "insufficient_funds",
"beneficiary":
{
"type": "payment_source",
"user_id": "4d9af4be-f75b-4ca7-be9b-b842bf7237c1",
"payment_source_id": "1c928e0e-6dc9-497c-8154-83dce43b2377"
},
"metadata":
{
"brand": "TL Casino"
}
}
Webhook examples:
{
"type": "payout_executed",
"event_id": "1b712c55-6bc4-487c-8a5b-2107623fe3bf",
"event_version": 1,
"payout_id": "5016c85d-de28-406d-9866-0a3ed85af3b1",
"executed_at": "2022-09-14T16:39:50.593Z",
"beneficiary":
{
"type": "payment_source",
"user_id": "4d9af4be-f75b-4ca7-be9b-b842bf7237c1",
"payment_source_id": "1c928e0e-6dc9-497c-8154-83dce43b2377"
},
"metadata":
{
"brand": "TL Casino"
}
}
{
"type": "payout_failed",
"event_id": "f8a144dc-a550-4681-844b-4de231ec3764",
"event_version": 1,
"payout_id": "f8a144dc-a550-4681-844b-4de231ec3764",
"failed_at": "2022-09-14T16:41:21.006718283Z",
"failure_reason": "insufficient_funds",
"beneficiary":
{
"type": "payment_source",
"user_id": "4d9af4be-f75b-4ca7-be9b-b842bf7237c1",
"payment_source_id": "1c928e0e-6dc9-497c-8154-83dce43b2377"
},
"metadata":
{
"brand": "TL Casino"
}
}
Updated 7 months ago