Guides • Build Banking Apps
Initiate your first payment

Initiate your first payment

This tutorial presents the steps to initiate a domestic payment.

Similar steps apply to all of our payments endpoints, therefore, after you complete this tutorial, you will know how to use all the payments endpoints.

See the Open Banking API: Payment initiation for all the supported payment types.

Before you begin, ensure that you have:

  • Registered your application with the payments scope in Developer Portal
  • Obtained a sandbox/production client_id from Developer Portal
  • Obtained sandbox/production transport.pem and signing.pem certificates, from Developer Portal or QTSP issuing body
  • Uploaded the jwks_url in Developer Portal that is specific to your transport.pem certificate. Ensure that the x5c claim is specified.

1. Generate a client credentials token

Request an access token for client credentials using the /token endpoint and the client_credentials grant type:

curl --cert transport.pem --key private.key \
--location --request POST 'https://oba-auth.revolut.com/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=payments' \
--data-urlencode 'client_id=<your client_id>'

Response:

{
"access_token": "<JWT client credentials>",
"token_type": "Bearer",
"expires_in": 2399
}

Use this token to:

  • Create a payment consent
  • Check the status of a previously created payment consent
  • Check the status of a previously completed payment
note

When your token expires and you need a new one, repeat this procedure to generate a new token.

Create a consent to initiate a domestic payment on behalf of a Revolut customer.

Prerequisites

When you create the consent, ensure that you:

  • Use your access token that you obtained with grant_type=client_credentials as the Bearer token.

  • Provide the x-jws-signature header in the API request.

    It must contain a valid JSON Web Signature (JWS) of the request payload body. When generating the signature, use the following JSON header.

    {
    "alg": "PS256",
    "kid": "<kid parameter of your signing certificate>",
    "crit": ["http://openbanking.org.uk/tan"],
    "http://openbanking.org.uk/tan": "<root domain of your JWKS URL>"
    }

    The result is a string consisting of the above header in base64 encoding, followed by two dots and the cryptographic signature.

    warning

    The signature in the JWS validates the header and payload as text, so make sure the payload used when generating the signature is exactly the same as the payload sent in the request payload including JSON formatting, line breaks and spacing.

  • Specify the x-idempotency-key header so that when there is a network failure and you do not receive the ID from the response, you can safely retry the request.

Here is an example of a domestic payment consent request.

For the full list of available parameters, see the API Reference: Create a domestic payment consent.

curl --location --request POST 'https://oba.revolut.com/domestic-payment-consents' \
--header 'x-fapi-financial-id: 001580000103UAvAAM' \
--header 'Content-Type: application/json' \
--header 'x-idempotency-key: 123' \
--header 'Authorization: Bearer <insert JWT client credentials from step 1.>' \
--header 'x-jws-signature: <insert JWS>' \
--data '
{
"Data": {
"Initiation": {
"InstructionIdentification": "ID412",
"EndToEndIdentification": "E2E123",
"InstructedAmount": {
"Amount": "55.00",
"Currency": "GBP"
},
"CreditorAccount": {
"SchemeName": "UK.OBIE.SortCodeAccountNumber",
"Identification": "11223321325698",
"Name": "Receiver Co."
},
"RemittanceInformation": {
"Unstructured": "Shipment fee"
}
}
},
"Risk": {
"PaymentContextCode": "EcommerceGoods",
"MerchantCategoryCode": "5967",
"MerchantCustomerIdentification": "1238808123123",
"DeliveryAddress": {
"AddressLine": ["7"],
"StreetName": "Apple Street",
"BuildingNumber": "1",
"PostCode": "E2 7AA",
"TownName": "London",
"Country": "UK"
}
}
}

Response:

{
"Data": {
"Status": "AwaitingAuthorisation",
"StatusUpdateDateTime": "2020-11-20T08:35:53.523806Z",
"CreationDateTime": "2020-11-20T08:35:53.523806Z",
"ConsentId": "6686e444-c103-4077-8085-8e094200c425",
"Initiation": {
"InstructionIdentification": "ID412",
"EndToEndIdentification": "E2E123",
"InstructedAmount": {
"Amount": "55.00",
"Currency": "GBP"
},
"CreditorAccount": {
"SchemeName": "UK.OBIE.SortCodeAccountNumber",
"Identification": "11223321325698",
"Name": "ReceiverCo."
},
"RemittanceInformation": {
"Unstructured": "Shipment fee"
}
}
},
"Risk": {
"PaymentContextCode": "EcommerceGoods",
"MerchantCategoryCode": "5967",
"MerchantCustomerIdentification": "1238808123123",
"DeliveryAddress": {
"AddressLine": [
"7"
],
"StreetName": "AppleStreet",
"BuildingNumber": "1",
"PostCode": "E27AA",
"TownName": "London",
"Country": "UK"
}
},
"Links": {
"Self": "https://oba.revolut.com/domestic-payment-consents/6686e444-c103-4077-8085-8e094200c425"
},
"Meta": {
"TotalPages": 1
}
}

3. Create a JWT URL parameter

After you create a consent, you need the user to authorise the consent so that you can initiate a payment on their behalf.

Create a JWT request parameter with the following header and payload. Use the private key corresponding to your signing certificate. This signature will be validated using the JWKS endpoint that you specified when you registered your application.

caution

The values of client_id, redirect_uri, kid, and scope should correspond to those for your specific application and consent request.

The value of openbanking_intent_id is the value of the ConsentId field returned in the consent creation response.

Header:

{
"alg": "PS256",
"kid": "<insert kid>"
}

Body:

{
"response_type": "code id_token",
"client_id": "<insert client_id>",
"redirect_uri": "<insert redirect_uri>",
"scope": "payments",
"claims": {
"id_token": {
"openbanking_intent_id": {
"value": "<insert ConsentId>"
}
}
}
}

Create an authorisation URL with the following parameters. Make sure they are URL-encoded.

ParameterDescriptionRequired
response_typeAlways set to code id_token.yes
client_idThe client ID for your application.yes
redirect_uriOne of the redirect URIs that you defined when you created the application.yes
scopeThe scope that you are requesting, for example, accounts or payments.yes
requestThe encoded JWT generated in the previous step.yes
response_modeIf set to fragment, parameters are passed in the fragment section of the redirect URI. Otherwise, they are passed in the URI query. Passing parameters in fragment is considered to be more secure.no
stateOAuth parameter that lets you restore the state of the application after redirection. If provided, this value is returned in the redirect URI.no

Example URL:

https://oba.revolut.com/ui/index.html?response_type=code%20id_token&scope=accounts&redirect_uri=<insert redirect URL>&client_id=<insert client_id>&request=<insert JWT from step 3.>&state=example_state

Once you have redirected the user to the authorisation URL, they will need to provide their Revolut credentials and complete the payment authorisation.

After authorising the payment, the user will be redirected back to the redirect_uri as in the below example:

https://example.com/my_callback_url?code=oa_sand_sPoyVs-oMhyR36j5N-ZEVLfK9rQWPNssgIQqsOFZQ-c&id_token=<JWT id_token>&state=example_state

You must use the code parameter from the URL parameters in order to complete the process.

note

The authorisation code is valid only for two minutes.

5. Exchange the authorisation code for access token

Exchange the code parameter obtained when getting the consent for a new access token:

curl  --key private.key --cert transport.pem \
--location --request POST 'https://oba-auth.revolut.com/token' \
--header 'Content-Type:application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode'code=<insert code>'

Response

{
"access_token":"oa_prod_tP1Nofi1ixsRfBmVBtVPdIVN0J5x91imqmheQIWTS5s",
"token_type":"Bearer",
"expires_in":86376,
"id_token":"<JWT id_token>"
}

The access_token returned in this response will allow you to execute the payment.

note

This access token is valid only for 24 hours.

6. Initiate a domestic payment

Now you can initiate the domestic payment on the user's behalf. Make a call to the /domestic-payments endpoint to initiate the payment.

In the request body, use the same JSON content which was previously used in the consent request, and add the Data.ConsentId key with the ConsentId value which you received when creating the consent.

note

This request also requires the x-jws-signature header, which you must calculate again with the new payload.

curl --location --request POST 'https://oba.revolut.com/domestic-payments' \
--header 'x-fapi-financial-id: 001580000103UAvAAM' \
--header 'Content-Type: application/json' \
--header 'x-idempotency-key: 123' \
--header 'Authorization: Bearer <insert access_token from step 5.>' \
--header 'x-jws-signature: <insert JWS>' \
--data '
{
"Data": {
"ConsentId": "<insert ConsentId>"
"Initiation": {
"InstructionIdentification": "ID412",
"EndToEndIdentification": "E2E123",
"InstructedAmount": {
"Amount": "55.00",
"Currency": "GBP"
},
"CreditorAccount": {
"SchemeName": "UK.OBIE.SortCodeAccountNumber",
"Identification": "11223321325698",
"Name": "Receiver Co."
},
"RemittanceInformation": {
"Unstructured": "Shipment fee"
}
}
},
"Risk": {
"PaymentContextCode": "EcommerceGoods",
"MerchantCategoryCode": "5967",
"MerchantCustomerIdentification": "1238808123123",
"DeliveryAddress": {
"AddressLine": ["7"],
"StreetName": "Apple Street",
"BuildingNumber": "1",
"PostCode": "E2 7AA",
"TownName": "London",
"Country": "UK"
}
}
}

Response:

{
"Data": {
"DomesticPaymentId": "dd817a13-3198-48ca-a5dc-f22b09ee82c0",
"Status": "AcceptedSettlementInProcess",
"StatusUpdateDateTime": "2020-11-20T08:54:30.115047Z",
"CreationDateTime": "2020-11-20T08:54:30.115047Z",
"ConsentId": "6686e444-c103-4077-8085-8e094200c425",
"Initiation": {
"CreditorAccount": {
"Name": "ReceiverCo.",
"SchemeName": "UK.OBIE.SortCodeAccountNumber",
"Identification": "11223321325698"
},
"InstructedAmount": {
"Amount": "55.00",
"Currency": "GBP"
},
"RemittanceInformation": {
"Unstructured": "Shipment fee"
},
"EndToEndIdentification": "E2E123",
"InstructionIdentification": "ID412"
}
},
"Links": {
"Self": "https://oba.revolut.com/domestic-payments/dd817a13-3198-48ca-a5dc-f22b09ee82c0"
},
"Meta": {
"TotalPages": 1
}
}

Congratulations! You have successfully initiated your first payment.

tip

Save the DomesticPaymentId value from the response so that you can later check the status of the payment execution.

7. Check the status of a payment

After executing a payment, it will typically go into the AcceptedSettlementInProcess status.

To check the status of the payment, make the following request using the DomesticPaymentId value obtained when initiating the payment.

You can use the previously generated access token with the grant type client_credentials and scope payments while it's still valid. After the token has expired, generate a new one in the same way.

Request:

curl --location 'https://oba.revolut.com/domestic-payment-consents/<DomesticPaymentId>' \
--header 'x-fapi-financial-id: 001580000103UAvAAM' \
--header 'Authorization: Bearer <access_token>'

Response:

{
"Data": {
"DomesticPaymentId": "dd817a13-3198-48ca-a5dc-f22b09ee82c0",
"Status": "AcceptedSettlementCompleted",
"StatusUpdateDateTime": "2020-11-20T08:54:32.511691Z",
"CreationDateTime": "2020-11-20T08:54:30.115047Z",
"ConsentId": "6686e444-c103-4077-8085-8e094200c425",
"Initiation": {
"CreditorAccount": {
"Name": "ReceiverCo.",
"SchemeName": "UK.OBIE.SortCodeAccountNumber",
"Identification": "11223321325698"
},
"InstructedAmount": {
"Amount": "55.00",
"Currency": "GBP"
},
"RemittanceInformation": {
"Unstructured": "Shipment fee"
},
"EndToEndIdentification": "E2E123",
"InstructionIdentification": "ID412"
}
},
"Links": {
"Self": "https://oba.revolut.com/domestic-payments/dd817a13-3198-48ca-a5dc-f22b09ee82c0"
},
"Meta": {
"TotalPages": 1
}
}

What's next

Was this page helpful?