PayDirect to Payments v3 API: Pay-ins

Send a payment to your TrueLayer merchant account with v3.

Step 1: Pay-in creation call

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

🚧

Signing requests

It is now mandatory to sign all POST API requests, including both pay-ins and payouts. You must also include certain user data within payment creation requests.
Learn more about signing requests and get links to TrueLayer signing requests libraries.

Merchant account ID

You can find your merchant_account_id in Console > Payments > Merchant Account or via the /merchant-accounts endpoint.

Using PayDirect APIs, you created a pay-in into your merchant account by making an API request to the https://paydirect.truelayer.com/v1/users/deposits endpoint.

Also, in the PayDirect request body, these values were passed:

  • currency, which is also used to identify the merchant account later.
  • The end user's remitter provider_id.
  • The payment deposit_id.
  • The end user's unique user_id.

The image below shows how you can make the equivalent Payment creation call with Payments v3 APIs, hitting the https://api.truelayer.com/paymentsendpoint.

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

This specific example shows how to convert a PayDirect deposit API call into a Payments v3 pay-in call. You can apply the following changes in the request body (see the API reference here):

  • The beneficiary merchant account ID (where the money will be sent to) must be passed explicitly as the merchant_account_id value. Note that currency value needs to match the currency of the merchant account.
  • user is now a dictionary object that includes multiple values.
  • 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.
  • If and only if the type value has been passed as preselected, provider_id can be passed as part of the provider_selection object.
  • If and only if the type value is passed as preselected, scheme_id can be passed as part of the scheme_selection object.

Some important notes on what's changed in v3:

If you want to keep your custom provider selection flow, and continue to follow the PayDirect structure that requires you to pass the remitter provider_id in the request, v3 fully supports this. You can continue to present white-labelled 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).

The Providers Endpoint and provider_id values

If you are currently using the /v2/single-immediate-payments-providers endpoint to fetch the provider_id, you can still keep this logic in your code. The provider_id values can be used for the payment_method.provider_selection.provider_id value for payments from a specific provider. However, the deposit.provider_id (in v3, the payment_method.provider_selection.provider_id) is no longer mandatory in v3.

You may currently be using the /v2/single-immediate-payments-providers endpoint to fetch the provider_id. If so, we have now released a new version, the /v3/payments-providers/search endpoint. Learn more about how to use the new endpoint.

The provider_id values are not changing, but there are some differences in how the endpoint works. The most prominent changes are:

  • The endpoint is now /v3/payment-providers/search.
  • The API call is now a POST (instead of a GET).
  • Instead of passing your client_id, the request needs to be authenticated with a valid access_token as a Bearer header.
    The request does not need to be signed, however.

Our documentation provides an overview of how to use the new /v3/payment-providers/search endpoint.

You can find the API reference for the /v3/payments-providers/search endpoint API resources here, which should help configure the request in line with your needs.

Comparison of provider calls in V2 and V3

In the V2 version of the providers endpoint, /v2/single-immediate-payments-providers, you included query parameters that determined the returned providers. For example:

GET https://pay-api.truelayer.com/v2/single-immediate-payments-providers?country=GB,ES&auth_flow_type=redirect&account_type=sort_code_account_number,iban&currency=GBP,EUR&client_id=penny

In the V3 version of the providers endpoint, /v3/payments-providers/search, you instead include a JSON body that contains the parameters that determine the returned providers. For example:

POST https://api.truelayer.com/v3/payments-providers/search

{
	"currencies": [
		"GBP",
		"EUR"
	],
	"countries": [
		"GB",
		"ES"
	],
	"customer_segments": [
		"retail"
	],
	"authorization_flow": {
		"configuration": {
			"redirect": {}
		}
	}
}

The documentation for the endpoint contains a variety of example requests and responses you can reference.

Payment creation call

As mentioned before, the PayDirectdeposit.provider_id parameter, which in V3 is the payment_method.provider_selection.provider_id parameter, is no longer mandatory in /v3/payments creation calls.

This means that you can also create a pay-in without specifying the provider_id the payment is coming from. Instead, the user selects their provider in the next step (which will be the payment authorisation).

Now, if you would also like to benefit from V3's out-of-the-box payment authorisation Web UI components or Mobile UI components for handling bank selection, you also don't need to pass any remitter provider information at the payment creation stage.

This also applies to payment scheme selection. You do not need to provide a scheme.id in the payment creation call. Instead, you can enable your user to select the payments rail they would like to use as part of the authorisation flow. See the scheme selection docs to explore the payment scheme options available in V3 .

The PayDirect auth_flow object, as you can see above, is not passed in V3 payment creation requests. It will be passed in the payment authorisation step, which happens after the payment creation.

With PayDirect APIs, after the payment creation request to https://paydirect.truelayer.com/v1/users/deposits, the API response includes 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. The payment creation API 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"
}

This response contains these objects:

  • id is the payment_id (formerly a deposit_id)
  • user.id is the user.id being passed in the request.
    If no user.id is passed in the request, TrueLayer generates one and returns 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.

The payment has now been created. It has status of authorization_required.

In the next step, the payment will be authorised.

Step 2: Payment authorisation

Payments v3 UX/UI components

In most circumstances, to authorise the payment, we recommend that you use the TrueLayer UI components (the hosted payment page, embedded payment page or one of the mobile SDKs). These enable you to benefit from a smoother integration experience and better scalability as you expand into new geographies. As TrueLayer UI components evolve, we support all possible different requirements from all supported providers (for example, additional screens if you need to pass additional inputs such as branch name).

If you are expanding in the EU, integration becomes much more complex without using the embedded/hosted payment pages or SDKs. In some cases, such as 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.

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

Payments v3 direct API integration

As explained in the payment creation 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 PayDirect 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 is passed in payment creation, the response at this point is a list of providers to be presented to the end user.

You 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:

A flow diagram showing the possible statues of a payment.

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 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.Status via /GET /payments/{payment_id}

Webhook sent to your endpoint
payment_settledThe payment has settled into the TrueLayer merchant account. This is a terminal status. Payment source details are available.

When a payment settles in your TrueLayer merchant account, we will confirm the bank details of the remitter account. As well as the bank details we will include a payment_source_id which identifies that particular bank account. This is a unique ID TrueLayer creates that refers to the bank account used for the deposit.
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.

Pay-in 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_settled",
    "event_version": 1,
    "event_id": "1d10e4e7-26a0-44b5-8f76-226ccf7e9c6c",
    "payment_id": "e9931912-da0b-4aa7-8209-357741836691",
    "payment_method":
    {
        "type": "bank_transfer",
        "provider_id": "mock-payments-gb-redirect",
        "scheme_id": "faster_payments_service"
    },
    "settled_at": "2022-09-14T16:14:26.958Z",
    "payment_source":
    {
        "account_identifiers":
        [
            {
                "type": "sort_code_account_number",
                "sort_code": "040668",
                "account_number": "00000871"
            },
            {
                "type": "iban",
                "iban": "GB75CLRB04066800000871"
            }
        ],
        "id": "17a580e4-7d58-4f84-9b80-a649ba3982b9",
        "account_holder_name": "JOHN SANDBRIDGE"
    },
    "user_id": "f61c0ec7-0f83-414e-8e5f-aace86e0ed35",
    "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"
    }
}

Handling duplicated webhooks

TrueLayer can't guarantee that you’ll only receive a single webhook notification for each payment status. As such, your integration should have logic that can handle receiving multiple webhooks for a given payment status.

For example, imagine TrueLayer sends an executed webhook, but doesn't receive a 200 response due to network issues from the recipient. In this case, TrueLayer sends an extra executed webhook as it cannot confirm the previous one was received – regardless of the current status of the payment. The recipient's integration should be able to handle such possibilities.

🚧

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_settled",
    "event_version": 1,
    "event_id": "1d10e4e7-26a0-44b5-8f76-226ccf7e9c6c",
    "payment_id": "e9931912-da0b-4aa7-8209-357741836691",
    "payment_method":
    {
        "type": "bank_transfer",
        "provider_id": "mock-payments-gb-redirect",
        "scheme_id": "faster_payments_service"
    },
    "settled_at": "2022-09-14T16:14:26.958Z",
    "payment_source":
    {
        "account_identifiers":
        [
            {
                "type": "sort_code_account_number",
                "sort_code": "040668",
                "account_number": "00000871"
            },
            {
                "type": "iban",
                "iban": "GB75CLRB04066800000871"
            }
        ],
        "id": "17a580e4-7d58-4f84-9b80-a649ba3982b9",
        "account_holder_name": "JOHN SANDBRIDGE"
    },
    "user_id": "f61c0ec7-0f83-414e-8e5f-aace86e0ed35",
    "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"
    }
}