This tutorial presents how to initiate a domestic payment.
Similar steps apply to all of our domestic and international payment endpoints, including scheduled payments and standing orders; therefore, after you complete this tutorial, you will know how to use all of them.
For detailed information on requests, responses, their parameters and how they differ depending on the payment type, see the Open Banking API reference.
To learn how to initiate multiple payments in a batch, see the tutorial Create a file payment.
Before you begin, ensure that you have:
payments
scope in the Developer Portalclient_id
from the Developer Portaltransport.pem
and signing.pem
certificates from the Developer Portal or from a QTSP issuing bodyjwks_url
specific to your signing.pem
certificate. Ensure that the x5c
claim is specified.If unsure, head to Get Started and review the setup process.
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:
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.
When you create the consent, ensure that you:
Use your access token that you obtained with grant_type=client_credentials
as the Bearer token.
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.
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.
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.
You can read more about JSON Web Signatures here.
Make a domestic payment consent request. An example request looks like this:
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
}
}
For the full list of available parameters and other details:
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.
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.
state
is an OAuth parameter that lets you restore the state of the application after redirection. If provided, this value is returned in the redirect URI.
Header:
{
"alg": "PS256",
"kid": "<insert kid>"
}
Body:
{
"response_type": "code id_token",
"client_id": "<insert client_id>",
"redirect_uri": "<insert redirect_uri>",
"scope": "payments",
"state": "<insert state>",
"claims": {
"id_token": {
"openbanking_intent_id": {
"value": "<insert ConsentId>"
}
}
}
}
Create an authorisation URL with the following parameters. Make sure they are URL-encoded.
Parameter | Description | Required |
---|---|---|
response_type | Always set to code id_token . | yes |
client_id | The client ID for your application. | yes |
redirect_uri | One of the redirect URIs that you defined when you created the application. | yes |
scope | The scope that you are requesting, for example, accounts or payments . For payments, use the scope payments . | yes |
request | The encoded JWT generated in the previous step. | yes |
response_mode | If 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 |
A sample authorisation URL looks like this:
https://oba.revolut.com/ui/index.html?response_type=code%20id_token&scope=payments&redirect_uri=<insert redirect URL>&client_id=<insert client_id>&request=<insert JWT from step 3.>
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 URL (redirect_uri
) containing the authorisation code (code
) as a URL parameter, as in the below example:
https://example.com/?code=oa_sand_sPoyVs-oMhyR36j5N-ZEVLfK9rQWPNssgIQqsOFZQ-c&id_token=<JWT id_token>&state=example_state
Use this authorisation code in the next step.
The code
is valid only for two minutes.
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.
This access token is valid only for 24 hours.
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.
For this request, you must also use:
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": "Pending",
"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
}
}
For more details about this call:
You have successfully initiated your first payment.
Save the DomesticPaymentId
value from the response so that you can later check the status of the payment execution.
After you create a single payment, it can go to either the Pending
or AcceptedSettlementInProcess
status.
Once the settlement on the debtor's account has been completed and the payment has been sent, it goes to the AcceptedSettlementCompleted
status.
To check the current status of the payment that you created, make the following request and provide the payment ID.
client_credentials
and scope payments
while it's still valid.
After the token has expired, generate a new one in the same way.DomesticPaymentId
value obtained when initiating the payment.curl --location 'https://oba.revolut.com/domestic-payments/<DomesticPaymentId>' \
--header 'x-fapi-financial-id: 001580000103UAvAAM' \
--header 'Authorization: Bearer <access_token>'
{
"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
}
}
For more details about this call: