Tutorial
How the Wero Merchant Payment API Works
- Request an access token using OAuth 2.0 client credentials. The authentication inputs depend on whether you access the Sandbox or Production environment.
Note: >Note: Please include the required scopes for the endpoints you wish to access in your authorization request. They are detailed in the OAuth 2.0 Scopes.
- Call Create Wero Payment to create a Wero payment and use the redirect url in the response to direct the customer to Wero e-commerce flow.
- Consumer with the help of their Issuing bank grants the consent to the Wero payment. Issuing bank directs consumer to the merchant.
- Retrieve the payment status by using Get Wero Payment Status endpoint.
Requirements
To use this API in a production environment as an ABN AMRO client, you must have the following:
- A contract for Wero Direct merchant or Wero Payment facilitator
- A completed OAuth setup. For detailed steps, refer to the Production access.
Sandbox access
The sandbox and production environments are functionally identical. The sandbox is static, which means that you can perform all operations without making any payments on an account. Payments posted in the sandbox environment are cleaned daily.
To use the API in the sandbox environment, complete the following steps:
- Register and create an account:
- Click Sign up.
- Enter your details, and click Create new account.
- Developer Support will send you an activation link by email.
- Click the activation link.
- Create and register an application:
- Log in to your account.
- In the left-side navigation bar, click Apps.
- Click Add app.
- In the App name field, enter a name for your application.
- In the APIs field, select Wero Merchant Payment API, and click Add app.
- Complete the How to use this API.
Sandbox access details
- Sandbox authorization URL: https://auth-sandbox.abnamro.com/as/token.oauth2
| Attribute | Value |
|---|---|
| client_id | wero_merchant2 |
| API-Key | The API Key for your application on the Developer Portal |
Production access
To get access to production for ABN AMRO clients:
- Log in to your account.
- In the left-side navigation bar, click Apps.
- Click Request Production Access.
- Select the API category that you want to request production access to.
Note: It is not possible to request production access for multiple API categories in one request.
- Fill in the form, and click Submit.
- You receive a confirmation email and ticket Id.
- ABN AMRO Developer Support validates the form and subsequently reaches out to you to collect your public key required for private_key_jwt authentication.
- When the setup is complete, ABN AMRO Developer Support contacts you and supplies you with a client_id.
- A new app is added in Apps. This new app contains your API key.
For production access, contact us.
Production access details
- Production Token URL: https://auth.abnamro.com/as/token.oauth2
| Attribute | Value for Production |
|---|---|
| client_id | As supplied to you by ABN AMRO |
| API-Key | The API Key for your production application on the Developer Portal |
Authentication
API consumers can authenticate themselves with ABN AMRO through OAuth 2.0 by using the jwt-bearer grant as specified by by RFC 7523.
The process for obtaining an access token from ABN AMRO includes the following steps:
- Create and sign a JWT assertion:
- Generate an assertion token in the form of a JSON Web Token (JWT).
- Sign the JWT asymmetrically using a valid private key or certificate.
- Making an endpoint call to obtain an access token from ABN AMRO OAuth services and including the following in the call: - The assertion token - The scope of the actions that represent the requested access rights
Assertion token
Assertion tokens consist of the following parts, which are separated by a dot:
> BASE64URL(header) . BASE64URL(payload) . BASE64URL(signature)
Header
{
"alg": "RS256",
"kid": "2c9b3e1a-8001-4c6f-95ae-2959d38d0000",
"typ": "JWT"
}
| Path | Type | Description |
|---|---|---|
| alg | String | Identifies the cryptographic algorithm used to sign the assertion token. Permitted values are: RS256, RS384, RS512, ES256, ES384, ES512. |
| kid | String | Key ID. This is the unique identifier of the key used to sign the assertion token. The value of this field must match the kid value of the public key registered with ABN AMRO. |
| typ | String | (Optional) Type. Must be set to "JWT" if included. |
Payload
{
"jti": "99325f42-1c85-4427-b081-81fbeeeb1c91",
"iss": "wero_merchant2",
"sub": "wero_merchant2",
"aud": "https://auth-sandbox.abnamro.com",
"scope": "merchant:wero-payment:all",
"iat": 1776757599,
"exp": 1776758199
}
| Path | Type | Description |
|---|---|---|
| jti | String | JWT ID. A unique identifier for the assertion token. This value is used to prevent replay attacks. |
| iss | String | Issuer. This is the unique identifier of the client that is requesting the access token. The value of this field must match the client_id value registered with ABN AMRO. |
| sub | String | Subject. This field is typically used to identify the principal that is the subject of the JWT. this is same as the iss field. |
| aud | String | Audience. This field identifies the recipients that the JWT is intended for. For ABN AMRO's authorization server, this value must be set to the token endpoint URL Production: https://auth.abnamro.com/as/token.oauth2Sandbox: https://auth-sandbox.abnamro.com/as/token.oauth2 |
| scope | String | List of identifiers that denote the scope of the actions for which the client requests authorization. Multiple scopes are delimited by blanks |
| iat | NumericDate | Issued At. This is the time at which the assertion token was issued. The value must be a Unix timestamp (number of seconds since January 1, 1970). |
| exp | NumericDate | Expiration Time. This is the time at which the assertion token expires. The value must be a Unix timestamp (number of seconds since January 1, 1970). |
Signature
The signature is calculated from the first two parts, header and payload, by using the signing algorithm that is specified in the alg header field as follows:
> sign(base64urlEncoded(header) '.' base64urlEncoded(payload))
The signature must be calculated using the signing algorithm specified in the alg header field. Various libraries exist to calculate the signature. The following example uses Nimbus JWT and an RSA signature with SHA-256 hash (RS256):
Example: compute an encoded assertion token
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import org.bouncycastle.util.io.pem.PemReader;
public class JwtGenerator {
public static void main(String[] args) {
var header = """
{
"alg": "RS256",
"kid": "TKLA3x5rx7QJcwJz_b57M7x7pUrgFYQ6shri1XYqvrM",
"typ": "JWT"
}""";
var payload = """
{
"jti": "99325f42-1c85-4427-b081-81fbeeeb1c91",
"iss": "client_id_123",
"sub": "client_id_123",
"aud": "https://auth-sandbox.abnamro.com",
"scope": "merchant:wero-payment:all",
"iat": 1776757599,
"exp": 1776758199
}""";
try {
var signedJwt = generateSignedJwt(header, payload, "key_prv_pkcs8.pem");
System.out.println("Signed JWT: " + signedJwt);
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException | SignatureException | InvalidKeyException e) {
System.out.println("Error generating signed JWT: " + e.getMessage());
throw new RuntimeException(e);
}
}
public static String generateSignedJwt(String header, String payload, String privateKeyPath)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
// 1. Base64Url encode header and payload
String headerB64 = base64UrlEncode(header.getBytes(StandardCharsets.UTF_8));
// 2. Base64Url encode the payload
String payloadB64 = base64UrlEncode(payload.getBytes(StandardCharsets.UTF_8));
// 3. Create the signing input
String signingInput = headerB64 + "." + payloadB64;
// 4. Read private key and sign the input
PrivateKey privateKey = loadPrivateKey(privateKeyPath);
Signature signature = Signature.getInstance("SHA256withRSA"); // algorithm for RS256
signature.initSign(privateKey);
signature.update(signingInput.getBytes(StandardCharsets.UTF_8));
byte[] sigBytes = signature.sign();
// 5. Base64Url encode the signature
String signatureB64 = base64UrlEncode(sigBytes);
// 6. Combine to form the final JWT
return signingInput + "." + signatureB64;
}
/**
* Helper method to Base64Url encode a byte array without padding.
*/
private static String base64UrlEncode(byte[] input) {
return Base64.getUrlEncoder().withoutPadding().encodeToString(input);
}
/**
* Helper method to load a private key from a PEM file.
*/
private static PrivateKey loadPrivateKey(String filename)throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
try (PemReader pemReader = new PemReader(new FileReader(filename))) {
byte[] content = pemReader.readPemObject().getContent();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(content);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
}
}
```
### Requesting an access token
To request an access token, make a POST request to the token endpoint of ABN AMRO's authorization server (e.g., `https://auth.abnamro.com/as/token.oauth2` for production environment) with the following parameters:
| Attribute | Value |
| -------------- | ------------------------------------------------------------------------------------------------------- |
| client_id | As supplied to you by ABN AMRO |
| grant_type | client_credentials |
| client_assertion_type | urn:ietf:params:oauth:client-assertion-type:jwt-bearer |
| scope | specify the scope for the operation that is to be authorized. Choose one from scope table [OAuth 2.0 Scopes](#section/OAuth-2.0-Scopes) |
| client_assertion | assertion token, refer following instruction to generate the token. Refer [Assertion token](#section/Authentication/Assertion token) to generate the token|
**Example**: curl
```shell
curl --location 'https://auth-sandbox.abnamro.com/as/token.oauth2' \
--header 'User-Agent: python-requests/2.32.5' \
--header 'Accept-Encoding: gzip, deflate' \
--header 'Accept: */*' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cookie: PF=dfv3puRBTjbzFSzlKPO78Z' \
--d 'client_id=<client id>' \
--d 'grant_type=client_credentials' \
--d 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--d 'client_assertion=<signed assertion token>' \
--d 'scope=<scope>'
OAuth-2.0-Scopes
| Use | Operation | Request for scope |
|----------------------------|-------------------------------------------|----------------------------------|
| Merchant access | Initiate Wero payment and retrieve status | merchant:wero-payment:all |
| Service Provider access | Initiate Wero payment and retrieve status | service-provider:wero-payment:all |
| Payment Facilitator access | Initiate Wero payment and retrieve status | payfac:wero-payment:all |
This tutorial describes how to connect an application to the Wero Merchant Payment API in the sandbox environment, and initiate Wero payment.
Note: Before you start this tutorial, you must complete the steps described in Sandbox access.
Step 1 - Request an access token
In this step, the OAuth 2.0 client credentials flow is used to obtain access to the Wero Merchant Payment API.
Option 1 - using private_key_jwt authentication
Refer to Authentication and use following properties to get the access token
| Attribute | Value |
|---|---|
| private key (RSA) | AAB.SYS.027400 |
| client_id | wero_merchant2 |
| client_assertion_type | urn:ietf:params:oauth:client-assertion-type:jwt-bearer |
| aud | https://auth-sandbox.abnamro.com |
| scope | specify the scope for the operation that is to be authorized. refer OAuth-2.0-Scopes |
| client_assertion | signed assertion token |
| Private Key to sign assertion token |
|---|
| Download private key: Download |
Sample request
curl --location 'https://auth-sandbox.abnamro.com/as/token.oauth2' \
--header 'User-Agent: python-requests/2.32.5' \
--header 'Accept-Encoding: gzip, deflate' \
--header 'Accept: */*' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Cookie: PF=dfv3puRBTjbzFSzlKPO78Z' \
--d 'client_id=<client_id>' \
--d 'grant_type=client_credentials' \
--d 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--d 'client_assertion=ewogICJhbGciOiAiUl.d121212.qbZe4sH5ZlCfbtNc7A' \
--d 'scope=merchant:wero-payment:all'
Option 2 - Obtaining the access token using client_secret_basic
Warning: This authentication method will be deprecated soon. Use private_key_jwt authentication method to obtain the access token.
Request attributes
The table below provides the required attributes for requesting access token on Sandbox environment.
| Attribute | Value |
|---|---|
| client_id | AAB.SYS.027400 |
| API-Key | The API Key for your application on the Developer Portal |
| grant_type | client_credentials |
| client_secret | OzTBGCKBS3dc2Mo72x8ibTKsGzgBoFLTy4YBl9YOLPVw56l8bCpJYZz2I4PIesz9 |
| scope | You must specify the scope for the operation that is to be authorized. refer OAuth-2.0-Scopes |
Sample request
Request an access token using the following sample:
curl -X POST "https://auth-sandbox.abnamro.com/as/token.oauth2" \
-d "grant_type=client_credentials" \
-d "client_id=AAB.SYS.027400" \
-d "client_secret=OzTBGCKBS3dc2Mo72x8ibTKsGzgBoFLTy4YBl9YOLPVw56l8bCpJYZz2I4PIesz9" \
-d "scope=merchant:wero-payment:all"
If the access token request is valid and authorized, the authorization server issues an access token.
Note: It is recommended that you use the access token for up to 90% of its expiration time (ms).
Sample response
{
"token_type": "Bearer",
"access_token": "X1PTWZre0fnW72l263yrhAWB2FDwx3tg",
"expires_in": 7199
}
Payment initiation attributes
To create a Wero merchant payment, using the App Token you created in Step 1, execute: POST Create Wero Payment.
This operation creates a new Wero payment as specified by the body of the request, and returns a paymentId which is used as unique identifier of a payment.
Sample request
curl -X POST -k https://api-sandbox.abnamro.com/wero-merchant-payments/v1 \
-v \
-H 'Accept: application/json' \
-H 'Authorization: Bearer X1PTWZre0fnW72l263yrhAWB2FDwx3tg' \
-H 'Trace-Id: 00000000-0000-0000-1111-000000000001' \
-H 'Content-Type: application/json' \
-H 'API-Key: YourAPIKeyHere' \
-d '{
"payment": {
"type": "SINGLE",
"amount": {
"euroInCents": 1000
},
"acceptor": {
"id": "AcceptorId",
"shop": {
"id": "ShopId"
}
}
},
"returnUrl": "http://merchantUrl",
"orderReference": "8798798755",
"orderDescription": "Lunch with friends"
}'
Sample response
{
"paymentId": "SIN-01KHXN46XPM1C6V56FWW682WC3",
"redirectUrl": "https://dummy.wero.ideal.nl/f6a28050-afb5-4d2c-9240-f9fc4caff44a"
}
Use the redirectUrl attribute from the response of Step 2 to direct the consumer to the Wero payment page.
Important: The
redirectUrlvalue in sandbox environment is a dummy URL and will not lead to an actual payment page. In production, theredirectUrlwill lead to the Wero payment page where the consumer can choose their bank and complete the payment flow.
Consumer grants consent to the payment in their consumer PSP and are redirected to the merchant page ('returnUrl' in the request body of GET Payment Status).
ABN AMRO Wero Acquiring after receiving the consent notification from WERO, orchestrates the payment authorization and payment capture.
Merchant calls GET Payment Status using the paymentId received in Step 2 to retrieve the status of the payment. The status endpoint can be polled in regular intervals to check the status of the payment.
Sample request
curl -X GET -k https://api-sandbox.abnamro.com/wero-merchant-payments/v1/SIN-01KHXN46XPM1C6V56FWW682WC3/status \
-v \
-H 'Accept: application/json' \
-H 'Authorization: Bearer X1PTWZre0fnW72l263yrhAWB2FDwx3tg' \
-H 'Trace-Id: bc69c490-9524-413f-971a-21f1aecd9fe5' \
-H 'Content-Type: application/json' \
-H 'API-Key: YourAPIKeyHere'
Sample response
{
"paymentId": "SIN-01KHXN46XPM1C6V56FWW682WC3",
"currentStatus": "PAYMENT_AUTHORISED"
}
To simulate a few possible payment flows, you can provide specific payment amount values in the POST Create Wero Payment. Use the paymentId returned in the response to retrieve the payment status and check the behavior of the API.
The following amount values can be used in the sandbox environment for testing purposes.
| Amount value | Behavior of Create Wero Payment | Behavior When Retrieving Payment Status |
|---|---|---|
| euroInCents = 111 | Responds with HTTP 200, with a valid paymentId |
Responds with HTTP 200, with currentStatus as PAYMENT_AUTHORISED |
| euroInCents = 222 | Responds with HTTP 200, with a valid paymentId |
Responds with HTTP 200, with currentStatus as CANCELED |
| euroInCents = 333 | Responds with HTTP 200, with a valid paymentId |
Responds with HTTP 200, currentStatus as EXPIRED |
Need help?