Testing the payments API
Now let's get started with some testing! But first you have to sign up and create your own Apps, if not already done.
You have to go through all steps, chronologically, in this guide to get successful results as each step is dependent on previous step.
Sandbox Gateway Host URL
When testing our APIs in our Sandbox environment, please ensure that the below base URL is used in the calls.
Important 1:
When using our APIs in the Live environment, the base URL changes to https://api.handelsbanken.com/openbanking
Important 2:
The client id:s you get when creating your apps, have a format that differs from the live environment. The live environment creates client id:s that have a UUID format while the sandbox creates client id:s that have a UUID format without dashes.
Important 3:
The Transaction Status, received at execution of the payment, might not be fully correct as we haven't created test data for all scenarious that can happen in the Live environment. But in moste cases they are correct..
Step-by-step guide
- StepsDescription
- 1. Client CredentialsClient Credentials Grant Access Token request using the Client Credentials Grant API.
- 2. Initiate PaymentInitiate the payment process using the Payment API endpoint.
- 3. End user (PSU) authorizationEnd user authorization using the available SCA Approach(es) received in the payment initiation response.
- 4. Request payment executionRequest the execution of a payment after it has been authorized.
- 5. Get Payment statusCheck the status of the payment.
Step 1: Request Client Credential Grant (CCG) token
The authorization process starts with requiring a Client Credentials Grant (CCG). The result of this call will be a CCG access token which is used to call the POST /payments/{paymentProduct} endpoint of the Payments API (which is used to acquire PIS consents for the PSU).
The Client Credential Grant request is identfied using the scope object (e.g. PIS). The CCG request is identical for all countries and is PSU independent.
Replace the “client_id” object with the client id you got when you created your App.
Further on in this guide you have to replace all objects in green color, with your client id, objects received from a previous step and objects that you have to choose yourself. Objects in red color are examples and cannot be used in requests.
Request example
curl -X POST https://sandbox.handelsbanken.com/openbanking/oauth2/token/1.0 \
-H 'Accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials&scope=PIS&client_id=YOUR CLIENT ID'
See below for the type of information that needs to be in your request.
Parameter | Description | Example |
---|---|---|
grant_type | Type of token request - CCG in this case. Allowed value: “client_credentials”. Mandatory |
client_credentials |
scope | A static scope with one or several values. For the Payment APIs, "PIS" must be the scope. Mandatory |
PIS |
client_id | The client_id (app-id) you got when you created your app". Mandatory |
f31b7318-8f21-4eaf-8817-6b5e4e02d6bc |
Response example
This is what the response looks like when the CCG token has been successfully created.
HTTP/1.1 200 OK
{
"access_token": "QVQ6YmNlZjI0M2QtZDBhZi00OGZiLWE0OTgtZGUwMTJhMDdjMjYz",
"expires_in": 86400,
"token_type": "Bearer"
}
Not that the access token is an example. The response you will receive is unique for your request. So be aware of that objects in red are examples and cannot be used by you.
Parameter | Description | Example |
---|---|---|
access_token | The returned CCG token, associated with your registered application for the scope(s) you have requested. | QVQ6YmNlZjI0M2QtZDBhZi00OGZiLWE0OTgtZGUwMTJhMDdjMjYz |
expires_in |
Number of seconds the access_token is valid. |
86400 |
token_type | Always the value ”Bearer”. | Bearer |
Step 2: Initiate payment (using CCG token)
The payment process starts with requesting a Payment ID and available SCA methods. These objects will later be used in Step 3.The CCG access token, received from previous step, is pasted into the Authorization header of the request.
Note that requests to the GB and the LU markets have the country defined in the URL while requests to the other markets defines the country with a header in the request.
For GB and LU the URL looks like this:
POST https://sandbox.handelsbanken.com/openbanking/psd2/gb/v1/payments
and
POST https://sandbox.handelsbanken.com/openbanking/psd2/lu/v1/payments
This applies to all /openbanking requests further down in this guide
Important 1:
In the body of the request, you should include payment fields and accepted values as per our Implementation Guidelines. If you don't follow the Impementation Guidelines and don't add the information required, you will receive errors.
Important 2:
If the PSU is a Corporate customer, then the header parameters "PSU-Corporate-ID" and "PSU-Corporate-ID-Type" must be included in the request. The "PSU-Corpotate-ID-Type" header is alway equal to "BANK". The PSU-Corporate-ID" is found in the test data document.
Request example
-H 'X-IBM-Client-Id: YOUR CLIENT ID' \
-H 'Authorization: Bearer CCG ACCESS TOKEN RECEIVED FROM PREVIOUS STEP' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Country: SE/NL/FI' \
-H 'TPP-Request-ID: YOUR REQUEST ID' \
-H 'TPP-Transaction-ID: YOUR TRANSACTION ID' \
-d '{"debtorAccount": {"value": "CHOOSE AN ACCOUNT FROM THE TEST DATA DOCUMENT","accountType": "IBAN"},"instructedAmount": {"currency": "SEK","amount": AMOUNT},"creditorAccount":{"value": "CREDITOR ACCOUNT","accountType": "BG OR PG"},"remittanceInformation":{"text": "REFERENCE TEXT"}}'
See below for the type of information that needs to be in your request.
Parameter | Description | Example |
---|---|---|
paymentproduct | Payment product identifier. Mandatory. |
swedish-domestic-giro-payment swedish-domestic-credit-transfer british-domestic-credit-transfer sepa-credit-transfer cross-currency-credit-transfer |
X-IBM-Client-Id | The client_id (app-id) for your test app. Mandatory. |
f31b7318-8f21-4eaf-8817-6b5e4e02d6bc |
Authorization |
The CCG access token. Use the CCG access token received in previous step. |
Bearer QVQ6YmNlZjI0M2QtZDBhZi00OGZiLWE0OTgtZGUwMTJhMDdjMjYx |
Country | ISO 3166-1 country code. Mandatory. |
SE, FI, NL (one country per request) |
TPP-Request-ID | Unique identifier for the request. For Sandbox testing, you can make this up. Mandatory. |
c8271b81-4229-5a1f-bf9c-758f11c1f5b1 |
TPP-Transaction-ID | Unique identifier for the transaction. For Sandbox testing, you can make this up. Mandatory. |
6b24ce42-237f-4303-a917-cf778e5013d6 |
Response example
This is what the response looks like when the payment initiation has been done. A successful response will include a Payment-id as well as links to endpoints for continuing the authorization process (next step) with the signing of the payment by the end user (PSU).
HTTP/1.1 200 OK {
"paymentId": "d3f3dd9f-9d41-85b2-4866-789a23caccc6",
"scaMethods": [{
"_links": {
"authorization": [{
"href": "https://sandbox.handelsbanken.com/openbanking/oauth2/authorize/1.0",
"name": "authorize_1.0",
"type": "application/x-www-form-urlencoded"
}
]
},
"scaMethodType": "REDIRECT"
},
{
"_links": {
"authorization": [{
"href": "https://sandbox.handelsbanken.com/openbanking/decoupled/mbid/initAuthorization/2.0",
"name": "decpld_mbid_2.0",
"type": "application/json"
}]
},
"scaMethodType": "DECOUPLED"
}
]
"transactionStatus":"ACTC"
}
Parameter | Description | Example |
---|---|---|
paymentId | The payment-id that has been created to be used later on when the PSU signs the payment. | 58cdfef9-7f6e-476e-a1af-c54c0a9a3135 |
scaMethods.[]._links.authorization. [].href |
Link to authorization endpoint for starting the PSU authorization process. |
Redirect: Decoupled: |
scaMethods.[].scaMethodType | The type of PSU authorization method. | REDIRECT DECOUPLED |
transactionStatus | ACTC |
Step 3: Payment Authorization Grant
We support two different authorization flows, Redirect and Decoupled. Redirect is available for all countries, while Decoupled is only available in Sweden.
Redirect Authorization flow
In this first phase of the authorization flow (Authorize Request) the PSU is signing the payment. However, the signing is not implemented in the sandbox, therefore the response comes immediately as if the PSU had signed the payment.
Authorize Request
Request example
curl -v -X GET 'https://sandbox.handelsbanken.com/openbanking/oauth2/authorize/1.0?response_type=code&scope=PIS:PAYMENT ID YOU GOT FROM PREVIOUS STEP&client_id=YOUR CLIENT ID&state=YOUR CHOICE&redirect_uri=CHOOSE AN ARBITRARY URI' \
-H 'Accept: application/json'
Parameter | Description | Example |
---|---|---|
response_type | The type of response, should always be "code". Mandatory. |
code |
scope | "PIS" for Payment information plus payment-id Mandatory. |
PIS:5871c132-e2d1-0b18-4a14-5c86bc6130a5 |
client_id | Your application's client-id. Mandatory. |
f31b7318-8f21-4eaf-8817-6b5e4e02d6bc |
state | An identifier chosen by you (the TPP), to be able to identify the current transaction. This will be provided in the call to your redirect URI. Mandatory. |
bc4b933c-bfc2-44c8-b858-eba90f559f91 |
redirect_uri | This is the callback URI to your own service, that will be called after the PSU finishes authorizing themselves. In the sandbox it will be called immediately since we don't have to wait for a user. Mandatory. |
https://YourCallbackServer/redirect/result |
Response example
In the sandbox the response is returned with a HTTP code 302 with Location header set to the redirect URI with query parameters of state (from the request) and an authorization code to be used in the next step: Request Authorization Code Grant token.
If there is an error, a Location header will be returned with the query parameters "error" and "error_description".
http://YourCallbackServer/redirect/result?state=bc4b933c-bfc2-44c8-b858-eba90f559f91&code=a445beb2-d1a1-b4e8-e05b-1803244c6722
Parameter | Description | Example |
---|---|---|
state | The identifier that you provided in the authorize request, to identify the current transaction. Mandatory. |
bc4b933c-bfc2-44c8-b858-eba90f559f91 |
code | An authorization code to be used in the next step. Mandatory. |
a445beb2-d1a1-b4e8-e05b-1803244c6722 |
Request Authorization Code Grant token
The final step in the redirect authorization flow is to retrieve an access token, by calling the Authorization Code Grant API
Request example
curl -X POST https://sandbox.handelsbanken.com/openbanking/oauth2/token/1.0 \
-H 'Accept: application/json' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code&scope=PIS:PAYMENT ID YOU GOT FROM PREVIOUS STEP&client_id=YOUR CLIENT ID&code=AUTHORIZATION CODE FROM PREVIOUS STEP&redirect_uri=CHOOSE AN ARBITRARY URI'
See below for the type of information that needs to be in your request.
Parameter | Description | Example |
---|---|---|
grant_type | The type of token call. Should be "authorization_code". Mandatory. |
authorization_code |
scope | "PIS" for Payment information plus payment-id. Mandatory. |
PIS:5871c132-e2d1-0b18-4a14-5c86bc6130a5 |
client_id | Your application's client-id. Mandatory. |
f31b7318-8f21-4eaf-8817-6b5e4e02d6bc |
code | The authorization code from previous step. Mandatory. |
a445beb2-d1a1-b4e8-e05b-1803244c6722 |
redirect_uri | This is the callback URI to your own service. Mandatory. |
http://YourCallbackServer/redirect/result |
Response example
This is what the response looks like when an access token has been successfully retrieved from the authorization server.
HTTP/1.1 200 OK {
"access_token": "QVQ6YTU3ZWMxZTgtYzFkYS0zZTEwLTFmMmQtNDA5Zjk4ODhhNzg1",
"expires_in": 900,
"token_type": "Bearer"
}
Parameter | Description | Example |
---|---|---|
access_token | The access token to be used in Authorization header in some of the coming requests against the endpoints of the payment API. | QVQ6YTU3ZWMxZTgtYzFkYS0zZTEwLTFmMmQtNDA5Zjk4ODhhNzg1 |
expires_in | Number of seconds the access_token is valid. Note that the sandbox only has static data - the access_token will never expire. |
900 |
token_type | Always the value ”Bearer”. | Bearer |
Decoupled Authorization flow
The Decoupled Authorization flow is only available in Sweden. It uses the Mobile BankID for authentication. However the Sandbox is not connected to mobile bank id. Instead the authentication will always be successful.
Authorize Request
Request example
curl -X POST https://sandbox.handelsbanken.com/openbanking/decoupled/mbid/initAuthorization/2.0 \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"client_id": "YOUR CLIENT ID",
"scope": "PIS:PAYMENT ID RECEIVED FROM PREVIOUS STEP",
"psu_client_ip": "YOUR CHOICE",
"bisa_same_device": true
}'
See below for the type of information that needs to be in your request. Please make sure you replace the example values with the correct ones in your request.
Parameter | Description | Example |
---|---|---|
client_id | Your application's client-id. Mandatory. |
f31b7318-8f21-4eaf-8817-6b5e4e02d6bc |
scope | "PIS" for Payment information plus payment-id Mandatory. |
PIS:d3f3dd9f-9d41-85b2-4866-789a23caccc6 |
psu_client_ip | IP address of the PSU's device. Both IPV4 and IPV6 address formats are allowed. Mandatory. |
127.0.0.1 |
bisa_same_device | Data type : boolean. Set value to true when the customer BankID app (BISA) run on the same device as the TPP client. An autoStartToken will be returned in the response. Set value to false when the BISA app run on a different device then the TPP client. A complete QR code will be returned in the response. Note that the parameter is not used by the sandbox, since the sandbox is not connected to Mobile BankID. Mandatory. |
true |
Response example when bisa_same_device=true
HTTP/1.1 200 OK
{
"auto_start_token": "Not a valid auto_start_token",
"sleep_time": 2000,
"_links": {
"token": {
"href": "https://sandbox.handelsbanken.com/openbanking/decoupled/mbid/token/2.0?sessionId=d5471d98-decd-a249-82ad-5b477df28e89",
"hints": {
"allow": ["POST"]
}
},
"cancel": {
"href": "https://sandbox.handelsbanken.com/openbanking/decoupled/mbid/cancel/2.0?sessionId=d5471d98-decd-a249-82ad-5b477df28e89",
"hints": {
"allow": ["POST"]
}
}
}
}
Response when bisa_same_device=false
HTTP/1.1 200 OK
{
"qr_code": "Not a valid qr_code",
"sleep_time": 2000,
"_links": {
"token": {
"href": "https://sandbox.handelsbanken.com/openbanking/decoupled/mbid/token/2.0?sessionId=d5471d98-decd-a249-82ad-5b477df28e89",
"hints": {
"allow": ["POST"]
}
},
"cancel": {
"href": "https://sandbox.handelsbanken.com/openbanking/decoupled/mbid/cancel/2.0?sessionId=d5471d98-decd-a249-82ad-5b477df28e89",
"hints": {
"allow": ["POST"]
}
}
}
}
Description of response parameters.
Parameter | Description | Example |
---|---|---|
auto_start_token | Optional, will only be returned when bisa_same_device=true. Token will be invalid since the sandbox is not connected to BankID. | Not a valid auto_start_token |
qr_code | Optional, will only be returned when bisa_same_device=false. The QR-code will be invalid since the sandbox is not connected to BankID. | Not a valid qr_code |
sleep_time | The minimum number of milliseconds to wait before invoking the token endpoint and between each call to that endpoint. | 2000 |
links.token.href | Link to the token endpoint, with your unique session-id, to use in the next step. | https://sandbox.handelsbanken.com/openbanking/decoupled/mbid/token/2.0?sessionId=e42e8bcb-cbdc-40b5-97a7-ca47284b1b69 |
links.cancel.href | Link to the cancel endpoint, to cancel the ongoing authorization process. Note that the sandbox uses static data. It is therefore not possible to actually cancel the authorization process. Though it is possible to call the endpoint, but it will have no effect. |
https://sandbox.handelsbanken.com/openbanking/decoupled/mbid/cancel/2.0?sessionId=e42e8bcb-cbdc-40b5-97a7-ca47284b1b69 |
Request Decoupled Grant token
In the Live environment it is necessary to start the BankID app, or display a QR-code to the user. However the sandbox is not connected to BankID. Therefor this skip is skipped.
The final step in the decoupled authorization process is to retrieve an access token, by calling the Decoupled Grant API
The URL to be used was received in previous step. In the Live environment it is necessary to poll this URL repeatedly, but in the sandbox it will always send a response with the "COMPLETE" status.
Request example
curl -X POST 'https://sandbox.handelsbanken.com/openbanking/decoupled/mbid/token/2.0?sessionId=SESSION ID RECEIVED FROM PREVIOUS STEP' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{}'
Response example
This is what the response looks like when the authorization has been successfully completed by the end user (PSU).
Note that the status will always be COMPLETE for the sandbox.
HTTP/1.1 200 OK
{
"result" : "COMPLETE”,
"access_token" : "QVQ6ZjdlODAyMTgtNGIwZi1kMDBkLThkMTctYjkwZjA4MjI2Njhi",
"token_type" : "Bearer",
"expires_in" : 900,
}
Parameter | Description | Example |
---|---|---|
result | Information about which "status" the token request is in: COMPLETE - the PSU has verified and the response includes token info. |
COMPLETE |
access_token | The access token to be used in Authorization header in the coming requests against the endpoints of theaccounts and cards APIs. Note! This token can only be used for the particular scope+PSU consent, that was given at initiation of the authorization. |
QVQ6ZjdlODAyMTgtNGIwZi1kMDBkLThkMTctYjkwZjA4MjI2Njhi |
token_type | Always the value "Bearer". | Bearer |
expires_in | Number of seconds the access_token is valid. Note that the sandbox only has static data - the access_token will never expire. |
900 |
Step 4: Request payment execution
The payment continues with payment execution, that is, approval of the payment. The ACG or DG access tokens, received from previous step, is pasted into the Authorization header of the request.
Request example
-H 'X-IBM-Client-Id: YOUR CLIENT ID' \
-H 'Authorization: Bearer ACG OR DG TOKEN RECEIVED IN PREVIOUS STEPS' \
-H 'Accept: application/json' \
-H 'TPP-Request-ID: YOUR OWN CHOICE' \
-H 'TPP-Transaction-ID: YOUR OWN CHOICE' \
See below for the type of information that needs to be in your request.
Parameter | Description | Example |
---|---|---|
paymentproduct | Payment product identifier. Mandatory. |
swedish-domestic-giro-payment swedish-domestic-credit-transfer british-domestic-credit-transfer sepa-credit-transfer cross-currency-credit-transfer |
paymentId | The payment-id that was created in previous step.. Mandatory. |
58cdfef9-7f6e-476e-a1af-c54c0a9a3135 |
X-IBM-Client-Id | The client_id (app-id) for your test app which we said you should note down earlier. Mandatory. |
f31b7318-8f21-4eaf-8817-6b5e4e02d6bc |
Authorization |
The ACG/DG access token from previous step. Mandatory. |
Bearer QVQ6MGQ2YmYtMmZjOC00Yzg3LzYyOGU0NTg5YmM3 |
TPP-Request-ID | Unique identifier for the request. For Sandbox testing, you can make this up. Mandatory. |
fcbf8e7c-46ad-4a94-9853-0c3b5c1323ba |
TPP-Transaction-ID | Unique identifier for the request. For Sandbox testing, you can make this up. Mandatory. |
c311a0b8-ed90-4af4-a5ca-1a8540644841 |
Response example
This is what the response looks like when the request to execute the payment is successfully done. The response contains the payment id and the current status of the payment.
{
"paymentId": "58cdfef9-7f6e-476e-a1af-c54c0a9a3135",
"transactionStatus": "ACCC",
}
Parameter | Description | Example |
---|---|---|
paymentId | The payment-id that was created in Step 2. | "58cdfef9-7f6e-476e-a1af-c54c0a9a3135" |
transactionStatus | The status of the payment. | "ACCC" "ACCP" "ACSC" "RJCT" |
Note that execution of payments in the sandbox always will be successful. That is, you will never get a payment that will be rejected.
Step 5: Get payment status
To find out if a payment has been executed successfully, you can check its status.
Please note: The "Authorization" HTTP-header accepts either the CCG token from Step 1 or the ACG/DG token from Step 3.
Request example
-H 'X-IBM-Client-Id: YOUR CLIENT ID' \
-H 'Authorization: Bearer CCG, ACG OR DG TOKEN RECEIVED IN PREVIOUS STEP' \
-H 'Accept: application/json' \
-H 'Country: SE/FI/NL' \
-H 'TPP-Request-ID: YOUR OWN CHOICE' \
-H 'TPP-Transaction-ID: YOUR OWN CHOICE' \
See below for the type of information that needs to be in your request.
Parameter | Description | Example |
---|---|---|
paymentproduct | Payment product identifier. Mandatory. |
swedish-domestic-giro-payment swedish-domestic-credit-transfer british-domestic-credit-transfer sepa-credit-transfer cross-currency-credit-transfer |
paymentId | The payment-id that was created in previous step. Mandatory. |
58cdfef9-7f6e-476e-a1af-c54c0a9a3135 |
X-IBM-Client-Id | The client_id (app-id) for your test app which we said you should note down earlier. Mandatory. |
f31b7318-8f21-4eaf-8817-6b5e4e02d6bc |
Authorization | The CCG token from Step 1 or the ACG/DG access token from Step 3. Mandatory. |
Bearer QVQ6MGQ2YmYtMmZjOC00Yzg3LzYyOGU0NTg5YmM3 |
Country | ISO 3166-1 country code Mandatory. |
SE, FI, NL (one country per request) |
TPP-Request-ID | Unique identifier for the request. For Sandbox testing, you can make this up. Mandatory. |
c8271b81-4229-5a1f-bf9c-758f11c1f5b1 |
TPP-Transaction-ID | Unique identifier for the request. For Sandbox testing, you can make this up. Mandatory. |
6b24ce42-237f-4303-a917-cf778e5013d6 |
Response example
This is what the response looks like when you retrieve a payment's status. The response contains the current status of the payment.
{
"paymentId": "58cdfef9-7f6e-476e-a1af-c54c0a9a3135",
"transactionStatus": "ACCC",
}
Parameter | Description | Example |
---|---|---|
paymentId | The payment-id that was created in Step 2. | 58cdfef9-7f6e-476e-a1af-c54c0a9a3135 |
transactionStatus | The current status of the payment. | "ACCP" "RJCT" "ACCC" |