Payments v2 to v3: single immediate payments

Step 1: Single immediate payment creation call

Initiate the Payments API call from the backend, including the Bearer access_token we got in the [authentication step].

🚧

Learn more about signing requests.
Find merchant_account_id in Console > Payments > Merchant account or via /GET /merchant-accounts

Using Payments v2, you created a pay-in into your merchant account by making an API call request to the https://pay-api.truelayer.com/v2/single-immediate-payment-initiation-requests endpoint.

In the v2 API call request body, these values were passed:

  • a single_immediate_payment_id that had to be passed with a uuid value, representing then the payment_id of the payment.
  • currency
  • The end user's remitter provider_id.
  • a beneficiary object with all the beneficiary details
  • an auth_flow object, including both the type and the return_uri values
  • the webhook_uri, with as a value the URL where TrueLayer would send the Payment's webhook notification.

Click on the Figma link above for the full screen.

As shown in the image above, you can make the equivalent Payment creation call in v3 by hitting the https://api.truelayer.com/payments endpoint.

Payments v2 endpointPayments v3 endpoint
https://paydirect.truelayer.com/v1/users/depositshttps://api.truelayer.com/payments

Then, apply the following changes in the request body (see here for the API reference):

  • The beneficiary account (where the money will be sent to) must be passed in the beneficiary object with:
    • type set to external account
    • account details inside the account_identifier object
    • referencebeing a mandatory field
  • user is a new dictionary object that includes multiple values. See https://docs.truelayer.com/docs/user-details-for-aml for more information.
  • payment_method.type needs to be specified as bank_transfer value.
  • deposit_id is no longer passed. TrueLayer will instead automatically generate one and return it in the response payload as an id parameter.
  • If and only if the provider_selection.type value has been passed as preselected then, the provider_selection.provider_id is passed as part of the provider_selection object (representing the v3 equivalent of the v2 mandatory provider_id field.

Important note

This specific example shows how to convert a Payment v2 single payment API call into a Payments v3 single payment call.

Some notes on what changes in v3:

  • If you want to keep your custom provider selection flow, and continue to follow the v2 structure that requires you to pass the remitter provider_id in the request, v3 fully supports this. You can continue to present your white-labeled provider selection and consent pages to your users (and therefore, perform the payment creation API call after the provider has been selected and passed in the request).
    You probably used the v2 /providers endpoint to fetch the provider_id and other info. You can still keep this logic in your code. The provider_id values can be used for the provider_selection.provider_id value.
    However, the deposit.provider_id (in v3, the provider_selection.provider_id) is no longer mandatory in v3.
  • This means that you can create a pay-in without specifying the provider_id the payment is coming from.
    Instead, the user selects their provider in the next step (payment authorisation). So, if you would like to benefit from v3's out-of-the-box payment authorisation flows for handling bank selection, you don't need to pass any remitter provider information at the payment creation stage.
    The Payments v2 auth_flow object, as you can see above, is not passed in v3 payment creation requests.

With Payments v2, after the payment creation request to `https://pay-api.truelayer.com/v2/single-immediate-payment-initiation-requests`, the API response included a direct link to the provider specified in the request.

Instead, with Payments v3, the https://api.truelayer.com/payments request will create a payment that will still have to be authorised. It will return an object including the following:

{
    "id": "e9931912-da0b-4aa7-8209-357741836691",
    "user":
    {
        "id": "f61c0ec7-0f83-414e-8e5f-aace86e0ed35"
    },
    "resource_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6InBheW1lbnQiLCJjbGllbnRfaWQiOiJzYW5kYm94LWRhcnlsdGVzdC03NmI5MDgiLCJqdGkiOiJlOTkzMTkxMi1kYTBiLTRhYTctODIwOS0zNTc3NDE4MzY2OTEiLCJuYmYiOjE2NjMxNzIwMzQsImV4cCI6MTY2MzE3MjkzNCwiaXNzIjoiaHR0cHM6Ly9hcGkudHJ1ZWxheWVyLXNhbmRib3guY29tIiwiYXVkIjoiaHR0cHM6Ly9hcGkudHJ1ZWxheWVyLXNhbmRib3guY29tIn0.yjQHGEASEJmvpl-PTxkngXWwVpzoWSWnuIQb_6bKaCU",
"status": "authorization_required"
}

Here:

  • id is the payment_id (or deposit_id)
  • user.id is the user.id being passed in the request. If no user.id is passed in the request, TrueLayer will generate one and return it here.
  • resource_token is the token that you use in payment authorisation if and only if you want to use TrueLayer UIs (HPP, EPP, SDK) for the payment authorisation flow.

📘

Resource tokens are valid for 15 minutes

After 15 minutes, the resource_token associated with the payment expires, causing the payment itself to fail.

The status of the payment will move to expired, instead of remaining in initiated indefinitely.

The payment has now been created. Its status is authorization_required.

In the next step, the payment will be authorised.

Step 2: Payment authorisation

Using Payments v3 UI/UX components

In most circumstances, to authorise the payment, we recommend that you use the TrueLayer UI components (the Hosted Payment Pages, Embedded Payment Page or Mobile Native and React SDK). These enable you to benefit from a smoother integration experience and better scalability as you expanding into new geographies. As TrueLayer UI components evolve, we support all possible different requirements from all supported providers (for example, additional screens for if you need to pass additional inputs such as branch name).

Important: If you are expanding in the EU, integration becomes more complex without using the embedded/hosted payment pages or SDKs. In some cases (German banks using embedded flows) they are actually mandatory if you are not regulated as a PISP (operating with TrueLayer certificates).

If you’d like to know more about how to use the TrueLayer UX/UI components for payment authorisation, see our docs.


Using Payments v3 direct API integration

That said, if you are currently integrated with Payments v2, and especially if you are going to operate in the UK only, you may want to keep your current white labeled frontend and proceed with a Direct API integration, handling payment authorisation using TrueLayer APIs from the backend. If that’s the case, keep reading.

As explained in the previous section, once the Payment is created, you will get in the response:

  • The payment_id
  • The user.id

To progress with payment authorisation, make an API call to a new endpoint:
https://api.truelayer.com/payments/<payment_id>/authorization-flow.

This will return the return_uri (which corresponds to the redirect_uri in the PayDirect APIs).

The JSON body is:

{
  "provider_selection": {},
	"redirect": {
    "return_uri": "{{RETURN_URI}}"
  }
}

This is where auth flow information is passed.

In v3, the return_uri corresponds to the auth_flow.return_uri that was passed in the Payments v2 payment creation call.

The provider_id is passed during payment creation, so the response now contains the direct link to the bank. The user needs to be directed to this link to authenticate and authorise the payment with their bank.

{
	"status": "authorizing",
	"authorization_flow": {
		"actions": {
			"next": {
				"type": "redirect",
				"uri": "https://pay-mock-connect.truelayer-sandbox.com/login/73b3a702-8441-44e4-baf0-462bb07f1d39#token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI3M2IzYTcwMi04NDQxLTQ0ZTQtYmFmMC00NjJiYjA3ZjFkMzkiLCJzY29wZSI6InBheS1tb2NrLWNvbm5lY3QtYXBpIiwibmJmIjoxNjcxNDUxNDA4LCJleHAiOjE2NzE0NTUwMDgsImlzcyI6Imh0dHBzOi8vcGF5LW1vY2stY29ubmVjdC50cnVlbGF5ZXItc2FuZGJveC5jb20iLCJhdWQiOiJodHRwczovL3BheS1tb2NrLWNvbm5lY3QudHJ1ZWxheWVyLXNhbmRib3guY29tIn0.hTDULBpxulHeSEQd4lHb_xRTwQCTtlFTZPdILaSs98k"
			}
		}
	}
}

📘

If no provider_selection.provider_id was passed in payment creation, the response at this point would have been a list of providers to be presented to the end user.

You will then need an additional /submit-provider-selection API call to submit the provider being selected.

Step 3: Consume webhooks and payment statuses

Once the payment authorisation process is complete, the user is redirected back to the return_uri. Now you just have to display indications to the user based on the payment status received via webhooks.

With Payments API v3, these are the payment statuses to expect:

Below is an explanation of what each status means:

StatusDescriptionNotification method
authorization_requiredThe payment has been created successfully. No further action has been taken.Status only via
/GET /payments/{payment_id}
authorizingThe end user has started the authorisation journey by interacting with the Hosted Payments Page or your UI. They have not completed the journey yet.Status only via
/GET /payments/{payment_id}
authorizedThe end user has completed the authorization journey. The payment has successfully completed its authorization flow.Status only via
/GET /payments/{payment_id}
payment_executedTrueLayer has submitted the payment to the bank and the payment has been accepted successfully. The bank will now start processing the payment.Status via
/GET /payments/{payment_id}

Webhook sent to your endpoint
payment_failedThe payment has failed to transition to the next status. Within this notification, TrueLayer will provide information to why the payment was not successful. The failure reason will be shared in the failure_reason field on the payment resource and the failure_stage field will share at which payment status the payment has transitioned to failed. This is a terminal statusStatus via
/GET /payments/{payment_id}

Webhook sent to your endpoint

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. If we continue to receive any status codes besides 2xx ones after retrying for 72 hours, we will discard the webhook.

We apply this retry policy to payments, payment refunds, payouts, and mandates.

See the following sections for more detailed information about Payments v3 statuses and failure_resons.

Payment webhook examples

🚧

We recommend that developers use our signing libraries to verify the Tl-Signature of the received webhooks.

Add webhook URLs in the TrueLayer Console. See Setting up the TrueLayer Console.

{
    "type": "payment_executed",
    "event_version": 1,
    "event_id": "ed5c3c60-6760-4830-86c3-bced4ce63f21",
    "payment_id": "e9931912-da0b-4aa7-8209-357741836691",
    "payment_method":
    {
        "type": "bank_transfer",
        "provider_id": "mock-payments-gb-redirect",
        "scheme_id": "faster_payments_service"
    },
    "executed_at": "2022-09-14T16:14:24.217Z",
    "metadata":
    {
        "brand": "TL Casino"
    }
}
{
    "type": "payment_failed",
    "event_version": 1,
    "event_id": "8dfba42f-72db-4aa1-b573-e0602ca65506",
    "payment_id": "09baadd0-499f-480f-a451-0ed6cd74593e",
    "payment_method":
    {
        "type": "bank_transfer"
    },
    "failed_at": "2022-09-14T16:22:00.528Z",
    "failure_stage": "authorizing",
    "failure_reason": "canceled",
    "metadata":
    {
        "brand": "TL Casino"
    }
}