Skip to main content
payments
Payments

Wero Merchant Payment 1.1.1

  • Early access

Enables merchants to create Wero payment and retrieve the request status.

Tutorial

How the Wero Merchant Payment API Works

  1. Request an access token using OAuth 2.0 client credentials. The authentication inputs depend on whether you access the Sandbox or Production environment.

    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.

  2. 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.
  3. Consumer with the help of their Issuing bank grants the consent to the Wero payment. Issuing bank directs consumer to the merchant.
  4. 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:

  1. Register and create an account:
    1. Click Sign up.
    2. Enter your details, and click Create an account.
    3. Developer Support will send you an activation link by email.
    4. Click the activation link.
  2. Create and register an application:
    1. Log in to your account.
    2. In the left-side navigation bar, click Apps.
    3. Click Add app.
    4. In the App name field, enter a name for your application.
    5. Select Wero Merchant Payment from the APIs list, and click Add app.
  3. Complete the How to use this API.

Sandbox access details

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:

  1. Log in to your account.
  2. In the left-side navigation bar, click Apps.
  3. Click Request Production Access.
  4. 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.

  1. Fill in the form, and click Submit.
  2. You receive a confirmation email and ticket-Id.
  3. ABN AMRO Developer Support validates the form and subsequently reaches out to you to collect your public key details required for private_key_jwt authentication. Refer Authentication.
  4. When the setup is complete, ABN AMRO Developer Support contacts you and supplies you with a client_id.
  5. A new app is added in Apps. This new app contains your API key.

For production access, contact us.

Production access details

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 consumer creates a signed JWT, commonly called a client assertion, using a private key. The corresponding public key is registered with our OAuth platform with a unique kid.

Note: The private key must always remain under the control of the consumer and must never be shared with us.

Key Generation Options

  1. Software-generated keys, for example using OpenSSL.
  2. Hardware-protected keys, using an HSM or equivalent cryptographic key-management service. The private key is generated and securely stored within the hardware device and is never exported. All signing operations are performed within the secure device using the private key. The corresponding public key can be exported from the device and shared with ABN AMRO for registration.
Example of generating a key pair using OpenSSL

Following are OpenSSL commands to generate a 3072-bit RSA key pair:

Generate the RSA private key

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out private_key.pem

Extract the corresponding public key

openssl pkey -in private_key.pem -pubout -out public_key.pem

This creates:

  • private_key.pem - RSA private key. Keep this key secure and never share it.
  • public_key.pem - RSA public key. The public key is shared with ABN AMRO Developer Support upon request.

Request Access Token

The process for obtaining an access token from ABN AMRO includes the following steps:

  1. 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.
  2. 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)

{
  "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 the scope table OAuth 2.0 Scopes
client_assertion The signed assertion token. Refer to Authentication to generate the token

Example: curl

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

How to use this API

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

Options 1 - using private_key_jwt authentication

Refer 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
}

Step 2 - Initiate a Wero payment

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"
}

Step 3 - Redirect the consumer to the Wero payment page

Use the redirectUrl attribute from the response of Step 2 to direct the consumer to the Wero payment page.

Important: The redirectUrl value in sandbox environment is a dummy URL and will not lead to an actual payment page. In production, the redirectUrl will lead to the Wero payment page where the consumer can choose their bank and complete the payment flow.

Step 4 - Retrieve the status of the payment

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"
}

Simulated behavior in sandbox

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, with currentStatus as EXPIRED

TelephoneNeed help?

Check the frequently asked questions or contact us. We are happy to help.
 

Get support Learn the basics