Get started

 

Welcome to LUXHUB Developer Portal - the centralized access to many European Banks’ official PSD2 APIs.

You can start by exploring our API Catalog and register a free account to start consuming the APIs in our Sandbox Environment - see How to register section below.

The exposed APIs are developed using modern industry standards such as REST and OAuth2.

The PSD2 regulation leaves open the details of the APIs that third parties will use to connect with ASPSPs. Therefore, some initiatives comprising banks, associations and PSPs from across the EU, defined common API standards.

The banks exposing their APIs through LUXHUB support Berlin Group or STET API standards. Check out our API Catalog to see which standard is supported by each ASPSP.

Explore our APIs

Just have a look at our Providers Catalog to find the APIs you would like to consume. You can use one or several at the same time.

APIs can have different states:

Coming soon: API specification is available, but you cannot test the API yet;
Prototyped: API is available for testing in Sandbox, but you cannot use it in Production yet;
Published: API is available in both Sandbox and Production;
Deprecated: you can continue using the API if you were using it, but you should consider migrating to the next version. You won’t be able to create new applications using this API.

For each API, you can find in our Providers Catalog a summary of its documentation (as well as the specification Swagger file once you are registered), and the details of the host where the API and the authorization endpoint are exposed.

How to register

To get full access to the banks’ APIs in Sandbox, you need to be an authorized third-party provider (*) or a payment service provider that has applied for the relevant authorization. Register by filling in the required form and providing us information about you and your company so we can verify your identity and make sure that your company is entitled to get access to the PSD2 Sandbox APIs.

We will send you a registration code once the verification is completed. The registration code will allow you to create an account on the LUXHUB Developer Portal, which will grant you access to the APIs exposed by our customers.

(*)Account Information Service Provider (AISP), Payment Initiation Service Provider (PISP) or Card-Based Payment Instrument Issuer (CBPII) in the context of PSD2 Directive (EU) 2015/2366.

Organizational concepts

Within LUXHUB Developer Portal, an organization is a group of registered users belonging to the same company. Each member of an organization can share applications with other members, modify application names, view monitoring results of the shared applications and access application security credentials.

All developers of the same company registered to the LUXHUB Developer Portal will belong to the same organization.

Third party service providers can register to the LUXHUB platform to gain access to the ASPSP APIs published here via either the LUXHUB Developer Portal or LUXHUB Third Party Management API.

Developer portal based onboarding

As said above, access to onboard to the Developer Portal is open to authorized third-party provider or a payment service provider that has applied for the relevant authorization. According to their status, a process is in place that each TPP has to follow to onboard.

If a TPP is already registered and has access to the Sandbox environment, it can upgrade its access so it can use Sandbox and Production environments. The TPP can do so only by proving that he has the necessary credentials to operate in production in the PSD2 context, i.e. he is in possession of QWAC and QSEALC certificates issued by a PSD2 QTSP. Therefore, the promotion process is based, in portal or API form, on presenting such a proof, i.e. signing a challenge posted by LUXHUB so the signing key can be verified as qualified and belonging to a regulated entity.

Once promotion is done, all users from the corresponding TPP organization will have access to both environments. Here are the steps for portal based promotion:

Follow this link (/profile-menu/organization)

Copy the challenge;

Sign the challenge with your private key as per the QSEALC eIDAS certificate

Paste the signed challenge encoded in base64;

Submit its certificate (public key) in order to allow us to verify the signature.

Once the signed challenge and signing certificate are submitted and the signature verification is passed, the TPP's organization will be promoted to Production and will have access to both Sandbox and Production environments.

Warning: if getting your challenge signed with your private key requires too much time and could induce a session timeout, you should first copy the UUID that we provide with the challenge. Then, under the section where you paste your signed challenge, you should paste also the UUID linked to this challenge so they can be sync-ed.

Onboarding via API

A TPP can use the Third Party Management API to register itself to use the LUXHUB Platform for whatever API it chooses, manage its organization and developers.

The flow via the API is quite similar to the one proposed via the portal with few different elements. The endpoints in this API are tagged either Registration or Promotion and their usage is described below.

Note: The functionality works as described below for this current version of the API but it might be changed in the future to allow further flexibility in usage and enhance the user experience. As well, several other Platform level APIs will be published in the future to provide more LUXHUB specific services to third parties.

Registration via API

The TPP can register/onboard to the platform via OAuth2 Dynamic Client Registration protocol directly to the production environment. To this purpose the /register endpoint is protected by MTLS authentication, so the TPP will have to use its QWAC eIDAS certificate to access it. As a result, the TPP will have a registered application, with respective (client_id, client_secret) combination, and his organization will be created in the Production environment directly in qualified status, which allows usage of PSD2 APIs in Production.

The TPP has to obtain an OAuth2 access_token, via the Client Credentials Grant flow using the (client_id, client_secret) combination from step above, and use this to, optionally, access other endpoints. These endpoints allow finer grained control via the API, i.e. managing the list of APIs to register to the TPP organization, managing organizations and users, etc. Basically, pretty much everything that can be done in this direction via the portal can be done via the API as well.

All endpoints in the Third Party Management API are protected via MTLS and all, except for /register endpoint, are authorized via:

  • Client Credentials Grant or,
  • for those involving the user, Basic Authorization (user/password). This means Promotion tagged endpoints, which involve Developer Portal registration, as well as those not tagged Promotion if and only if the /register/user endpoint was used beforehand to create an user via the API.

 

Warning: if a TPP is already registered via the Developer Portal it will have to promote its organization via the portal. Otherwise, using the API will mean that a secondary organization will be created for the same TPP.

Promotion via API

A TPP can promote its organization from pending to qualified status, i.e. in order to get Production access to PSD2 APIs, via the Developer Portal, as described above, or via API - using endpoints tagged as Promotion in the Third Party Management API.

Once the TPP decides that he wants to promote his organization, i.e. so it can use the PSD2 APIs published by providers in Production, it will have to use the /promotion/challenge to obtain a challenge form the LUXHUB Platform, sign it with his QSEALC eIDAS certificate and return it via a call to the /promotion/organization endpoint. After the TPP signature is validated and verified its respective organization is moved to qualified status, so the TPP can start using the PSD2 APIs in Production.

In this iteration of the LUXHUB Platform, the Migration endpoints are to be used only by TPPs already registered via the Developer Portal to promote their organizations. The TPPs using the Registration endpoints will have their organization created directly in the qualified status, hence with access to Production environment.

The endpoints tagged Migration are also secured via OAuth2 Client Credentials Grant flow, using the token obtained above.

How to access an API

Have you found an API you would like to implement? The following steps will teach you what you need to do to access the API.

1. Security

eIDAS Certificates considerations

The eIDAS certificates referred to in this guide are SSL certificates with dedicated protection profiles that allows to be used in the PSD2 context. To achieve the PSD2 security requirements, banks and PSD2 service providers will use Qualified Certificates for Websites and Qualified Certificates for Electronic Seals. Those certificates will be issued by Qualified Trust Service Providers (QTSPs) based on the new technical standard, ETSI TS 119 495, which was published in May 2018. Qualified Certificates enable identification and verification of the payment institution by a third party. Identification will be based on the legal name of an organization, registration number and its main role(s) in the payments space.

There are two types of such certificates:

QWAC (Qualified Website Authentication Certificate): used as Client Certificates in MA-TLS

QSeal (Qualified Certificate for Seals) : used to sign requests using http-signature

All PSD2 APIs require both types of certificates, QWAC to access the API and QSeal for http-signature, i.e. message signing.

LUXHUB implementation of certificates

Our PSD2 APIs are protected by Mutual TLS protocols based on eIDAS Certificates, as required by the PSD2 European Directive. This means that if you want to access one of our PSD2 APIs, you need to use an eIDAS TLS Client Certificate for your requests. If you don’t have such a certificate, you can download a mock eIDAS Certificate from our Developer Portal (Log in, then go to Applications -> select Application -> Download QWAC Certificate). Our mock certificate nevertheless only allow access to Sandbox APIs. To access Production, you need to get your own PSD2 eIDAS Certificate from a Qualified Trust Service Provider.

At the application level, the PSD2 APIs require message signing following http-signature specification, with the signing QSeal certificate (different from the one used as client Certificate). To access the Sandbox, we also provide you such a mock certificate for message signing. The "download QSeal certificate button" is located just next to the one for downloading a QWAC Certificate.

2. Access Management

Our APIs use OAuth2 as authorization mechanism for access management to the endpoints. The main points related to the implementation of OAuth2 in the context of PSD2 APIs are presented below.

API Authorization

Berlin Group specifications do not impose any specific authorization framework to be used, whereas STET specifications specifically mentioned OAuth2 as authorization mechanism. Therefore, LUXHUB decided to employ state of the art authorization by protecting all endpoints via OAuth2 authorization. Please consult RFC 6749 for more details about this authorization framework.

Authorization Code Grant

The most used flow is Authorization Code grant, which allows PSU to authorize (give consent) to its resources in a secure manner. The authorization code provides a few important security benefits, such as the ability to authenticate the client, as well as the transmission of the access token directly to the client without passing it through the resource owner’s user-agent and potentially exposing it to others, including the resource owner. [https://tools.ietf.org/html/rfc6749#page-24]

Client Credentials Grant

There are few endpoints where the Client Credentials flow is also allowed, in which cases the respective resources to which it is granted access are considered completely detached from the PSU until they are authorized by him - they are considered to belong to the TPP up to this moment.

Client credentials are used as an authorization grant typically when the client is acting on its own behalf (the client is also the resource owner) or is requesting access to protected resources based on an authorization previously arranged with the authorization server. [https://tools.ietf.org/html/rfc6749#page-40]

Proof Key for Code Exchange

As an additional security measure to protect the Authorization Code Grant in OAuth2 flow, LUXHUB is implementing Proof Key for Code Exchange (PKCE) as described in [https://tools.ietf.org/html/rfc7636]. This mitigates the threat of having the authorization code intercepted. The technique involves the client first creating a secret, and then using that secret again when exchanging the authorization code for an access token. This way if the code is intercepted, it will not be useful since the token request relies on the initial secret.

Registering your application for API access

To access an API you need to obtain your OAuth2 credentials (client_id and client_secret) and use them to get tokens within the scope needed for the endpoint you are trying to access. There are two options to do so:

· Dynamic registration based on eIDAS Certificate

For all the PSD2 APIs, we provide OAuth2.0 Dynamic Client Registration endpoints. You just need to call the /register endpoint of the Third Party Management API, using your QWAC eIDAS certificate. In the request, you will have to provide the required redirect URI to your application, where the customer will be redirected after authentication. In the response, you will receive the credentials that you will need to use in order to get details about the API exposed on the LUXHUB platform and register access for your application to the ones that you deem interesting.

This Dynamic Client Registration for TPPs will be available, for now, only in Production environment. Details about how it is to be used are available in Onboarding via API paragraph in this document.

· Use our Developer Portal

If you don’t have an eIDAS Certificate yet or if you want to create more than one set of OAuth Credentials, you can use the Developer Portal for authorizing your application. If you don’t have an account yet, you need to register first (see How to register section above). Once you are logged in, you can go to the Applications section and create a new application.

Credentials management and API access is managed at application level, i.e. each application will receive a pair of (client_id, client_secret) credentials and will have to register the APIs to which it requires access.

You can change the APIs supported by your application at any time, but keep in mind that you have to do two important - immutable - choices:

Environment: You cannot mix Production APIs with Sandbox APIs

Category: As of now ONLY applications using PSD2 API can be created, in the near future non-PSD2 APIs will be offered as well. The current documentation will be updated to reflect this change once available.

When creating an application you also have to provide the redirect URLs of your application. This is where your application’s users (named Payment Service User - PSU - in PSD2 lingo) will be redirected after doing strong customer authentication (SCA) in the bank realm. Depending on your implementation, you may implement several redirect URLs (for example different redirect URls for account information consent approval and for payment authorization).

The redirect URLs defined here will have to match the URLs provided when the application calls/api/oauth/authorize endpoint (details in sections below).

Finally you will have to upload an X.509 Certificate, which will help us generate a PSD2-compliant OAuth2 client_id. In Production, you have to use your own eIDAS Certificate granted by a QTSP - the same certificate used to call the bank’s API. In Sandbox, we will provide you with a mock certificate that you can use. You can download your certificate from the Applications page. Binding a real eIDAS certificate to your application via the portal is also supported in Sandbox, in which case the said certificate needs to be uploaded.

3. Message signing

STET standard

STET specification enforces message signing, based on http-signature protocol as defined in [https://tools.ietf.org/html/draft-cavage-http-signatures-10], for requests and makes it optional for responses. Please see chapter 3.5 Applicative authentication in STET PSD2 API Documentation Part 1: Framework for details on how the message signing should be implemented.

The electronic signature of the TPP has to be based on a qualified certificate for electronic seals. This qualified certificate has to be issued by a qualified trust service provider according to the eIDAS regulation. The content of the certificate has to be compliant with the requirements of EBA RTS on SCA and CSC. The certificate of the TPP has to indicate all roles the TPP is authorized to use. The public key needed for signature verification is requested to be an URL aiming to provide the relevant Qualified Certificate.

As per the STET standard Documentation Framework, chapter 3.5, the http-signature should be computed in the following way:

1. Compute a SHA256 digest of the HTTP body and adding this digest as an extra HTTP header.

2. Use a specific Qualified Certificate (QSealC), respecting the ETSI/TS119495 Technical Specification, in order to apply a RSA-SHA256 signature on:

  • all the following headers that are present within the HTTP request sent by the TPP, including the previously computed digest:
    • Date (if available)
    • Content-Type
    • Content-Length (when there is a payload)
    • X-Request-Id
    • All available "PSU"-prefixed Headers
  • all the following headers that are present within the HTTP response given by the ASPSP, including the previously computed digest
    • Date (if available)
    • Content-Type
    • Content-Length (when there is a payload)
    • X-Request-Id
    • on the specific “(request-target)” field which is specified by the IETF draft-paper

3. Add this signature within an extra HTTP header embedding

  • The key identifier which must specify the way to get the relevant qualified certifi cate. It is requested that this identifier is an URL aiming to provide the relevant Qualified Certificate.
  • The algorithm that has been used
  • The list of headers that have been signed
  • The signature itself.
  • In order to assure an easy discrimination of the certificate among others, it is requested that the last part of the URL to the certificate be suffixed by an underscore followed by the fingerprint of the certificate. E.g.: https://path.to/myQsealCertificate_612b4c7d103074b29e4c1ece1ef40bc575c0a87e

Request example

GET /accounts
Headers:
Date: Mon, 14 Jan 2019 15:02:24 +0100
PSU-Date: 2017-06-08T09:33:55.954+02:00
Accept: application/hal+json; charset=utf-8
PSU-GEO-Location: GEO:52.506931,13.144558
Digest:
X-Request-ID: GGF3YUD3BDJK
PSU-Referer: http://en.wikipedia.org/wiki/Main_Page
PSU-IP-Port: 12345
PSU-Accept: text/plain
Authorization: Bearer 1234567890AZERTYUIOP
PSU-Accept-Charset: utf-8
PSU-Accept-Encoding: gzip, deflate
PSU-IP-Address: 10.10.10.10
PSU-User-Agent: Mozilla
PSU-HTTP-Method: POST
PSU-Accept-Language: en-US
Content-Type: application/json
User-Agent: Swagger-Codegen/1.0.0/java
Signature: keyId="https://path.to/myQsealCertificate_612b4c7d103074b29e4c1ece1ef40bc575c0a87e",algorithm="rsasha256",headers="date psu-date psu-geo-location digest x-request-id psu-referer psu-ip-port psu-accept psu-accept-charset psuaccept-encoding psu-ip-address psu-user-agent psu-http-method psu-accept-language content-type (requesttarget)",signature="ny8dUFiW9B0NBhylZa+NQf3tHVkjtMRvExfbnXps5NkN+dVDqetfp5MBmmwXIS4/eNNYxXp4m/1ct5AdDulCNyu9 PeVDii2a2Nv2NXE5YGN8SF/R/io4tmIpoYTDUxPqR6IrhUGB2Jcso1ibx0bwGXgFjp9EPniAFaLBBAipasY="

 

Berlin Group standard

Berlin Group specification does not require ASPSP to enforce message signing. However, LUXHUB considered this as best practice that should be enforced for all communication.

Therefore, all requests sent by TPPs to an ASPSP hosted on LUXHUB platform will have to be signed at application level using the http-signature protocol as defined in [https://tools.ietf.org/html/draft-cavage-http-signatures-10].

The electronic signature of the TPP has to be based on a qualified certificate for electronic seals. This qualified certificate has to be issued by a qualified trust service provider according to the eIDAS regulation. The content of the certificate has to be compliant with the requirements of EBA RTS on SCA and CSC. The certificate of the TPP has to indicate all roles the TPP is authorized to use. The corresponding public key will be present as a header in all the requests made towards the API.

For more details on this subject please see Section 4.2 in Berlin Group NextGenPSD2 XS2A Framework Implementation Guidelines.

For signing your requests in the case of using mock QSeal certificates downloaded from LUXHUB Developer Portal, please use the certificate named QSealC-cert.pem and the key named QSealC-key.pem.

Signing implementation details

For Berlin Group http-signing details, please refer chapter “12.2 Requirements on the "Signature" Header” of the XS2A Implementation Guide version 1.3. It describes how signature header should be interpreted.

LUXHUB implementation of the http-signature is using RFC2253 for formatting the distinguished name of the CA that issued the certificate used for signing - as part of keyId element of signature header.

For example, considering Java as implementation language, and reffering X500Principal class documentation, the correct code for implementing the keyid parameter should be:

 

String keyId = "SN=" + cert.getSerialNumber().toString(16) + ", CA=" + cert.getIssuerX500Principal().getName(); //default behavior
// or
String keyId = "SN=" + cert.getSerialNumber().toString(16) + ", CA=" + cert.getIssuerX500Principal().getName(X500Principal.RFC2253);

 

Request example - as per chapter “12.2 Requirements on the "Signature" Header” of the XS2A Implementation Guide version 1.3

  • assume the following payment initiation request :
POST https://api.testbank.com/v1/payments/sepa-credit-transfers
        Content-Type: application/json
        X-Request-ID: 99391c7e-ad88-49ec-a2ad-99ddcb1f7721
        PSU-IP-Address: 192.168.8.78
        PSU-ID: PSU-1234
        PSU-User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
        TPP-Redirect-URI: https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&code_Cchallenge_Mmethod="S2
                                56"
        Date: Sun, 06 Aug 2017 15:02:37 GMT

        {
          "instructedAmount":
          {
            "currency": "EUR",
            "amount": "123"
          },
          "debtorAccount":
          {
            "iban": "DE2310010010123456789"
          },
          "creditor":
            {
              "name": "Merchant123"
            },
          "creditorAccount":
            {
              "iban": "DE23100120020123456789"
            },
          "remittanceInformationUnstructured": "Ref Number Merchant"
        }
  • so the base64 encoded body will be:
eyAgICANCiAgICJpbnN0cnVjdGVkQW1vdW50IjogeyJjdXJyZW5jeSI6ICJFVVIiLCAiYW1vdW50IjogIjEyMyJ9LA0KICAgImRlYnRvckFjY291bnQiOiB7ImliYW4iOiAiREUyMzEwMDEwMDEwMTIzNDU2Nzg5In0sDQogICAiY3JlZGl0b3IiOiB7Im5hbWUiOiAiTWVyY2hhbnQxMjMifSwNCiAgICJjcmVkaXRvckFjY291bnQiOiB7ImliYW4iOiAiREUyMzEwMDEyMDAyMDEyMzQ1Njc4OSJ9LA0KICAgInJlbWl0dGFuY2VJbmZvcm1hdGlvblVuc3RydWN0dXJlZCI6ICJSZWYgTnVtYmVyIE1lcmNoYW50Ig0KfQ==
  • while the SHA-256 of the body is, in base64:
F9li3V7yu8S/QKVOhWiiiqJBhGMVId8UGZ4sBRVPkok=
  • therefore using rsa-sha256 signing algorithm the signed request of the TPP will be:
POST https://api.testbank.com/v1/payments/sepa-credit-transfers
        Content-Type: application/json
        X-Request-ID: 99391c7e-ad88-49ec-a2ad-99ddcb1f7721
        PSU-IP-Address: 192.168.8.78
        PSU-ID: PSU-1234
        PSU-User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
        TPP-Redirect-URI: https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&code_Cchallenge_Mmethod="S256"
        Date: Sun, 06 Aug 2017 15:02:37 GMT
        Digest: SHA256=ZuYiOtZkVxhjWmwTO5lOpsPevUNMezvk6dfb6fVhebM=
        Signature: keyId="SN=9FA1,CA=D-TRUST%20CA%202-1%202015,O=DTrust%20GmbH,C=DE",algorithm="rsa-sha256",headers="digest x-request-id psu-id tpp-redirect-uri date",signature="Base64(RSA-SHA256(signing string))"
        TPP-Signature-Certificate: <TPP's_eIDAS_Certificate>

        {
          "instructedAmount": {"currency": "EUR", "amount": "123"},
          "debtorAccount": { "iban": "DE2310010010123456789"},
          "creditor": { "name": "Merchant123"},
          "creditorAccount": {"iban": "DE23100120020123456789"},
          "remittanceInformationUnstructured": "Ref Number Merchant"
        }

Note: official Berlin Group example is wrong related to the case of "headers" component of keyid which needs to be in lower case only; so each header name needs to be normalized as such.

  • and signing string is:
digest: SHA-256=ZuYiOtZkVxhjWmwTO5lOpsPevUNMezvk6dfb6fVhebM=
x-request-id: 99391c7e-ad88-49ec-a2ad-99ddcb1f7721
psu-id: PSU-1234
tpp-redirect-uri: https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&code_Cchallenge_Mmethod="S256"
date: Sun, 06 Aug 2017 15:02:37 GMT

 

http-signature code snippets

Below code snippets are provided "as-is" with no express guarantee or licence and free of charge, as a help to third parties that would like to connect to the APIs available on LUXHUB platform. They are known to work at the moment of being presented in this guide. Each third party using these should take it's own due diligence if they plan to use them in production applications.

1. Java
- the Java code below can be used as well from Scala and Groovy

 

//berlin group version
import org.tomitribe.auth.signatures.PEM;
import org.tomitribe.auth.signatures.Signature;
import org.tomitribe.auth.signatures.Signer;...

public class HttpSignatureCalculatorBG implements SignatureCalculator {
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM YYYY HH:mm:ss");
private static final List<String> ALLOWED_HEADERS = Arrays.asList("X-Request-ID", "Date", "Digest");

private String certPath;
private String keyPath;

public HttpSignatureCalculatorBG(String certPath, String keyPath) {
this.certPath = certPath;
this.keyPath = keyPath;
}

@Override
public void sign(Request request) throws Exception {

String date = DATE_FORMAT.format(new Date()) + " GMT";

request.getHeaders().set("Date", date);

if(request.getBody() != null) {
String requestBody = new String(request.getBody().getBytes());
request.getHeaders().set("Digest", generateDigest(requestBody));
} else {
request.getHeaders().set("Digest", generateDigest(""));
}

BufferedReader reader = new BufferedReader( new InputStreamReader(loadFile(certPath) ) );
String certificate = reader.lines().collect( Collectors.joining( System.lineSeparator() ) );

request.getHeaders().set("TPP-Signature-Certificate", formatCertificate(certificate));
request.getHeaders().set("Signature", generateSignature(request));

}

private String formatCertificate(String certificate) {
return certificate.replaceAll("\n", "")
.replaceAll("\r", "")
.replace("-----BEGIN CERTIFICATE-----", "")
.replace("-----END CERTIFICATE-----", "");
}

private String generateDigest(String payload) {
try {
byte[] digest = MessageDigest.getInstance("SHA-256").digest(payload.getBytes());
return "SHA-256=" + new String(Base64.getEncoder().encode(digest));
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Wrong algorithm", e);
}
}

private String generateSignature(Request request) throws CertificateException, InvalidKeySpecException, IOException {

Map<String, String> headersMap = new HashMap<String, String>();
List<String> signatureHeaders = new ArrayList<String>();

request.getHeaders().forEach(entry -> {
if(!ALLOWED_HEADERS.contains(entry.getKey())) {
return;
}
headersMap.put(entry.getKey(), entry.getValue());
signatureHeaders.add(entry.getKey());
});

CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(loadFile(certPath));

String keyId = String.format("SN=%s,CA=%s", cert.getSerialNumber(), cert.getIssuerDN());
String algorithm = "rsa-sha256";

Signature signature = new Signature(keyId, algorithm, null, signatureHeaders);

PrivateKey privateKey = PEM.readPrivateKey(loadFile(keyPath));

Signer signer = new Signer(privateKey, signature);

String method = "method";
String uri = "uri";

Signature signed = signer.sign(method, uri, headersMap);

return signed.toString();

}

private InputStream loadFile(String content) throws FileNotFoundException {
return new ByteArrayInputStream(content.getBytes());
}

}

// stet version
import org.tomitribe.auth.signatures.PEM;
import org.tomitribe.auth.signatures.Signature;
import org.tomitribe.auth.signatures.Signer;...

public class HttpSignatureCalculatorStet implements SignatureCalculator {

    private static final List<String> ALLOWED_HEADERS = Arrays.asList("X-Request-ID", "psu-date", "Digest",
            "PSU-IP-Address", "(request-target)", "content-length", "content-type");

    private String keyPath;
    private String certificateUrl;

    public HttpSignatureCalculatorStet(String keyPath, String certificateUrl) {
        this.keyPath = keyPath;
        this.certificateUrl = certificateUrl;
    }

    @Override
    public void sign(Request request) throws Exception {

        String date = String.valueOf(System.currentTimeMillis());
        String requestTarget = request.getMethod().toString() + " " + request.getUri().getPath();

        if (request.getBody() != null) {
            String requestBody = new String(request.getBody().getBytes());
            request.getHeaders().set("Digest", generateDigest(requestBody));
            request.getHeaders().set("content-length", requestBody.length());
        } else {
            request.getHeaders().set("Digest", generateDigest(""));
        }

        request.getHeaders().set("psu-date", date);
        request.getHeaders().set("PSU-IP-Address", InetAddress.getLocalHost().getHostAddress());
        request.getHeaders().set("(request-target)", requestTarget);

        request.getHeaders().set("Signature", generateSignature(request));

    }

    private String generateDigest(String payload) {
        try {
            byte[] digest = MessageDigest.getInstance("SHA-256").digest(payload.getBytes());
            return "SHA-256=" + new String(Base64.getEncoder().encode(digest));
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("Wrong algorithm", e);
        }
    }

    private String generateSignature(Request request)
            throws CertificateException, InvalidKeySpecException, IOException {

        Map<String, String> headersMap = new HashMap<String, String>();
        List<String> signatureHeaders = new ArrayList<String>();

        request.getHeaders().forEach(entry -> {
            if (!ALLOWED_HEADERS.contains(entry.getKey())) {
                return;
            }

            headersMap.put(entry.getKey(), entry.getValue());
            signatureHeaders.add(entry.getKey());
        });

        String algorithm = "rsa-sha256";

        Signature signature = new Signature(certificateUrl, algorithm, null, signatureHeaders);

        PrivateKey privateKey = PEM.readPrivateKey(loadFile(keyPath));

        Signer signer = new Signer(privateKey, signature);

        String method = request.getMethod().toString();
        String uri = request.getUri().getPath();

        Signature signed = signer.sign(method, uri, headersMap);

        return signed.toString();

    }

    private InputStream loadFile(String content) throws FileNotFoundException {
        return new ByteArrayInputStream(content.getBytes());
    }

}

 

2. Python

- definition of signing string functions:

#berlin group version
def set_bg_signature(self, http_signature_cert, http_signature_key):
    key_id = get_cert_key_id(http_signature_cert)
    algorithm = 'rsa-sha256'
    headers = 'x-request-id date digest'
    signing_string = 'x-request-id: ' + self.x_request_id + '\ndate: ' + self.date + '\ndigest: ' + self.digest
    signature = sign_string(http_signature_key, signing_string, 'sha256')
    signature_header = 'keyId="' + key_id + '",algorithm="' + algorithm + '",headers="' + headers + '",signature="' + signature + '"'
    self.signature = signature_header

#stet version
def set_stet_signature(self, http_signature_cert_url, method, url, query_params, http_signature_key, body=None):
    key_id = http_signature_cert_url
    algorithm = 'rsa-sha256'
    headers = 'psu-ip-address psu-date x-request-id (request-target) digest'
    #if body is empty
    headers = headers + ' content-length content-type'
    request_target = method.lower() + ' ' + url.replace(API_Test_Variables.env, '') + params_str
    signing_string = 'psu-ip-address: ' + self.psu_ip_address + '\npsu-date: ' + self.psu_date + '\nx-request-id: ' + self.x_request_id + '\n(request-target): ' + request_target + '\ndigest: ' + self.digest
    #if body is not empty
    signing_string = signing_string + '\ncontent-length: ' + str(len(json.dumps(body))) + '\ncontent-type: ' + headers['Content-Type']
    signature = sign_string(http_signature_key, signing_string, 'sha256')
    signature_header = 'keyId="' + key_id + '",algorithm="' + algorithm + '",headers="' + headers + '",signature="' + signature + '"'
    self.signature = signature_header

where the sign-string function is:

from OpenSSL import crypto
...
def sign_string(key, signing_string, algorithm):
    pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, open(key).read())
    signature = base64.b64encode(crypto.sign(pkey, signing_string, algorithm))
    return signature

 

3. JavaScript

//berlin group version
const crypto = require('crypto');
generateBGSignature(headers, body) {
    let headerCert = QSEALCert.replace(/\r/g, '');
    headerCert = headerCert.replace(/\n/g, '');
    headerCert = headerCert.replace(/-----[^ ]+ CERTIFICATE-----/g, '');
    headers["TPP-Signature-Certificate"] = headerCert;

    const {
        Certificate
    } = require('@fidm/x509');
    const cert = Certificate.fromPEM(QSEALCert);

    const digest = "SHA-256=" + crypto.createHash('sha256').update(body).digest('base64');
    let signingString = "digest: " + digest + "\n" +
        "x-request-id: " + headers["X-Request-ID"] + "\n" +
        "date: " + headers["Date"];

    let signatureHeaders = "Digest X-Request-ID Date";

    if (typeof headers["PSU-ID"] !== "undefined") {
        signingString += "\nPSU-ID: " + headers["PSU-ID"];
        signatureHeaders += " PSU-ID";
    }

    if (typeof headers["PSU-Corporate-ID"] !== "undefined") {
        signingString += "\nPSU-Corporate-ID: " + headers["PSU-Corporate-ID"];
        signatureHeaders += " PSU-Corporate-ID";
    }

    if (typeof headers["TPP-Redirect-URI"] !== "undefined") {
        signingString += "\nTPP-Redirect-URI: " + headers["TPP-Redirect-URI"];
        signatureHeaders += " TPP-Redirect-URI";
    }

    headers["Digest"] = digest;
    headers["Signature"] = "keyId=\"SN=" + cert.serialNumber +
        this.getCertificateIssuerValues(cert.issuer) +
        ", algorithm=\"rsa-sha256\"" +
        ", headers=\"" + signatureHeaders + "\"" +
        ", signature=\"" + crypto.createSign('SHA256').update(signingString).sign(QSEALKey, 'base64') + "\"";

    return headers;
}

//stet version
const crypto = require('crypto');
generateSTETSignature(headers, method, url, body) {
    headers["Digest"] = "SHA-256=" + crypto.createHash('sha256').update(body).digest('base64');

    let signingString = "(request-target): " + method.toLowerCase() + " " + url;
    let signatureHeaders = "(request-target)";

    for (let key in headers) {
        if (headers.hasOwnProperty(key)) {
            signingString += "\n" + key.toLowerCase() + ": " + headers[key];
            signatureHeaders += " " + key;
        }
    }

    headers["Signature"] = "keyId=\"" + my_url_to_certificate + "\"" +
        ", algorithm=\"rsa-sha256\"" +
        ", headers=\"" + signatureHeaders + "\"" +
        ", signature=\"" + crypto.createSign('SHA256').update(signingString).sign(QSEALKey, 'base64') + "\"";

    return headers;
}

 

 

 

Account Information Services (AIS)

All APIs are protected by OAuth2 authorization. To receive authorization & access tokens, the PSU has to give consent for the application to access his resources - in case of Account Information Services (AIS) these are: accounts, balances and transaction history. The PSU authentication is required to be a Strong Customer Authentication (SCA), i.e. at least two factors, and to be done in the ASPSP (bank) realm.

Depending on each bank, the application might have to implement a different consent management flow. Please refer to the API documentation to discover which consent flow is supported by the Bank you have chosen.

According to PSD2 directive, it is the TPP’s (this means “your”) responsibility to ensure that a PSU has all required information when he gives consent to an application to access his financial data.

Consent

To access a PSU’s account, you have to obtain the PSU’s consent beforehand, in order to access his accounts.

First, the PSU has to select a bank in your application. Once this is done, you have to show to the PSU a clear description of which data you would like to access from his bank.
This is what we call the scope of the consent.

Next, the PSU has to authorize this consent. This process depends on the API specification and consent model implemented by the PSU’s bank.
Below, you will find the required requests to be executed until you have a valid consent to access a PSU’s account data.

These requests are not directly covered by the PSD2 Specification, but they are derived from other standards such as OAuth2.
For the actual PSD2 requests, you can look at the API specifications and at the official documentation of the PSD2 API standards supported by the LUXHUB platform:

Berlin Group - current supported version is 1.3 20190215

STET - current supported version is 1.4.1.3

This diagram shows the requests to be performed in the case where an API requires Berlin Group’s Global Consent Model as well as the first part of Berlin Group’s Detailed Consent Model.
The requests shown in yellow are explained in detail below.

It is to be noted for Berlin Group specifications that the Consent resource management and lifecycle is fully separated by the lifecycle and management of the Authorisation resource. Basically the Authorisation resource is used for authorizing the Consent resource, hence the Authorisation resource endpoints need to be protected by Authorization Code Grant, which involves PSU authentication, whereas Consent resource can be protected by Client Credentials Grant only. This kind of approach will allow for the Consent resource, and its status, retrieval by the TPP even before authorization by the PSU. Similarly, as will be explained later, payment flows will benefit also on the TPP being able to retrieve Payment Initiation, and dependent entities like Authorisation, before authorization by the PSU. This way simple management of implicit and explicit authorisation flows can be achieved. Signing Baskets flows will also benefit from the same feature.

The flow presented below in details, and in the diagram above, is a reference flow suggested by LUXHUB and not the only possible one. It is designed to show most of the complexities of the scenarios possible during the PSD2 consent authorization for Berlin Group standard implementation.

For illustrating flows described in this document we will use cURL, which is an utility present on the majority of operating systems allowing for fast testing of the API authorization and endpoints. Some requests will not contain verbatim all parameters needed or not all parameters will be explained in details, for the sake of simplicity.

For all request, please use the appropiate QWAC and/or QSeal certificates, either real eIDAS ones provided by a QTSP or mock ones downloaded from LUXHUB Developer Portal. In the case of mock certificates, the following file naming is used:

QWAC: QWAC-cert.pem and respectively QWAC-key.pem

QSeal: QSeal-cert.pem and respectively QSeal-key.pem

For exhaustive details on what and how all parameters of a certain request should be set, as well as for exact definitions of the responses to be expected from the API, please consult the swagger documentation of each respective API.

 

1. Request a token according to the OAuth2 client credentials grant. This token allows you to create a consent resource.

  • example request:
curl \
             -H 'Authorization: Basic ' \
             -H 'Content-Type : application/x-www-form-urlencoded' \
             -H 'X-Request-ID: 12345678-1234-1234-1234-1234567890ab' \
             -d 'grant_type=client_credentials&scope=AIS' \
             -X POST 'https://<Token Endpoint>' \
             --cert QWAC-cert.pem --key QWAC-key.pem​

2. Create a consent resource which allows you to retrieve further information about the accounts of a PSU. Depending on the supported consent model by the ASPSP:

· global consent model: you can use this consent - once authorized - to retrieve the details of the PSU’s accounts.

· detailed consent model: you can use this consent - once authorized - to retrieve the list of the PSU’s accounts.
At this moment in its life cycle, the consent is not yet bound to a PSU. The access to this resource is controlled via the access token obtained in step 1.

example request for (detailed consent):

curl \
             -H 'Signature: ' \
             -H 'Authorization: Bearer ' \
             -H 'X-Request-ID: 12345678-1234-1234-1234-1234567890ab' \
             -H 'Content-Type: application/json' \
             -d '{"access":"availableAccounts":"allAccounts"},"recurringIndicator":false,"validUntil":"2019-03-14","frequencyPerDay":4,"combinedServiceIndicator":false}' \
             -X POST 'https://<Base URI>/consents' \
             --cert QWAC-cert.pem --key QWAC-key.pem

 

  • example request (global consent):
curl \
             -H 'Signature: ' \
             -H 'Authorization: Bearer ' \
             -H 'X-Request-ID: 12345678-1234-1234-1234-1234567890ab' \
             -H 'Content-Type: application/json' \
             -d '{"access":{"allPsd2":"allAccounts"},"recurringIndicator":false,"validUntil":"2019-03-14","frequencyPerDay":4,"combinedServiceIndicator":false}' \
             -X POST 'https://<Base URI>/consents' \
             --cert QWAC-cert.pem --key QWAC-key.pem

3. Redirect the PSU to the SCA for authentication and authorization of the consent. This step will bind the consent to a PSU. Further API calls will be authorized based on access token received as a result of OAuth2 Authorization Code Grant, i.e. after PSU authentication in bank system. After consent authorization, the respective consent resource is bound to the PSU that authorized it.

  • example request:
curl \
         -X GET 'https://<Authorization Endpoint>?response_type=code&scope=AIS:<consent_id>&client_id=<client_id>&redirect_uri=http%3A%2F%2F127.0.0.1%3A9003%2Fredirect&state=12345678-1234-1234-1234-1234567890ab&code_challenge=<code_challenge_pkcs>&code_challenge_method=S256'

 

 

4. Your application has to retrieve the OAuth2 authorization code. Strong Customer Authentication of the PSU is required as part of this step. After SCA, the PSU is redirected back to the TPP application.
Your application should use the parameters specified below.
You can try this redirection by pasting the GET request from step 3 into your browser (you have to configure your browser to use the Mock Certificate for its requests) while ensuring that your application is listening at the specified redirection URL.

 

  • example request:

    curl \
                 -X GET 'http://127.0.0.1:9003/redirect?code=<authorization_code>'​

 

 

5. Once you have received the authorization code, you can ask for OAuth2 access and refresh tokens to access the /accounts resource.

    • example request:

      curl \
                   -H 'Authorization: Basic ' \
                   -H 'Content-Type : application/x-www-form-urlencoded' \
                   -d 'grant_type=authorization_code&redirect_uri=http%3A%2F%2F127.0.0.1%3A9003%2Fredirect&code=&scope=AIS:<consent_id>' \-X POST 'https://<Token Endpoint>' \
                   --cert QWAC-cert.pem --key QWAC-key.pem​

     

    6. (detailed model only) You have a valid consent you can use to retrieve a list of accounts on the /accounts endpoint
    The list of accounts has to be shown to the PSU to allow him to select the accounts he wants to give consent to.
    • example request:

      curl \
                   -H 'Signature: ' \
                   -H 'Authorization: Bearer ' \
                   -H 'Consent-ID: ' \
                   -H 'X-Request-ID: 12345678-1234-1234-1234-1234567890ab' \
                   -X GET 'https://<Base URI>/accounts \--cert QWAC-cert.pem --key QWAC-key.pem​
  1.  

7. (detailed model only) Once the PSU has selected the accounts, you have to create a new consent resource which allows you to retrieve details for the chosen accounts of the PSU.

    • example request:

      curl \
                   -H 'Signature: ' \
                   -H 'Authorization: Bearer ' \
                   -H 'X-Request-ID: 12345678-1234-1234-1234-1234567890ab' \
                   -H 'Content-Type: application/json' \
                   -d '{"access":{"accounts":[{"iban":"FR7612345987650123456789014"}],"balances":[{"iban":"FR7612345987650123456789014"}],"transactions":[{}]},"recurringIndicator":true,"validUntil":"2019-03-14","frequencyPerDay":4,"combinedServiceIndicator":false}' \
                   -X POST 'https://<Base URI>/consents' \
                   --cert QWAC-cert.pem --key QWAC-key.pem​

     

8. (detailed model only) You have to repeat the request to /api/oauth/authorize as you did already in step 3, but using the consent ID obtained in step 7.

9. (detailed model only) You have to repeat the request to the redirect URL as you did already in step 4, but using the authorization code obtained in step 8.

10. (detailed model only) You have to repeat the request to /api/oauth/token as you did already in step 5, but using the authorization code obtained in step 8/9.

11. You are now ready to call the /accounts resource with all details according to the scope of the TPP’s consent.

  • example request:

    curl \
                 -H 'Signature: ' \
                 -H 'Authorization: Bearer ' \
                 -H 'Consent-ID: ' \
                 -H 'X-Request-ID: 12345678-1234-1234-1234-1234567890ab' \
                 -H 'Content-Type: application/json' \
                 -X GET 'https://<Base URI>/accounts' \
                 --cert QWAC-cert.pem --key QWAC-key.pem​

 

 

It is worth noting that the Berlin Group specifications provide separate endpoints for card accounts and related entities access - however, the authorization grants used are the same like in case of regular payment accounts.

STET: Full AISP Model and Mixed Model

This schema shows the requests to be performed in the case where an API requires the STET Full AISP. The requests shown in yellow are explained in detail below.

 

 

The flow presented below in details, and in the diagram above, is a reference flow suggested by LUXHUB and not the only possible one.

  1. Request an authorization code for the AIS API access. After calling below URL, the PSU will be redirected to SCA of the chosen bank.

    example request:

    curl \
    -X GET 'https://<Authorization Endpoint>?response_type=code&scope=aisp&client_id=<client_id>&redirect_uri=http%3A%2F%2F127.0.0.1%3A9003%2Fredirect&state=12345678-1234-1234-1234-1234567890ab&code_challenge=<code_challenge_pkcs>&code_challenge_method=S256'

 

2. Once the PSU has done SCA with the bank, he will be redirected to your redirect URL. The following request has to be served by your application.

 

example request:

 

curl\
 -X GET 'http://127.0.0.1:9003/redirect?code=<authorization_code>'​

 

3. Once you have received the authorization code, you can ask for access and refresh tokens.

      • example request:

        curl \
                     -H 'Authorization: Basic ' \
                     -H 'Content-Type : application/x-www-form-urlencoded' \
                     -d 'grant_type=authorization_code&redirect_uri=http%3A%2F%2F127.0.0.1%3A9003%2Fredirect&code=<authorization_code>&scope=aisp' \
                     -X POST 'https://<Token Endpoint>' \
                     --cert QWAC-cert.pem --key QWAC-key.pem​

 

4. You are now ready to call the /accounts resource with all details according to the scope of the TPP’s consent.

      • example request:

        curl \
                     -H 'Signature: ' \
                     -H 'Authorization: Bearer ' \
                     -H 'X-Request-ID: 123456-1234-1234-1234567890ab' \
                     -X GET 'https://<Base URI>/accounts' \
                     --cert QWAC-cert.pem --key QWAC-key.pem​

 

5. (mixed model only) Finally, if the selected bank implements the mixed consent model, you have to inform the bank about the accounts and scopes you would like to access.

      • example request:

        curl \
                     -H 'Signature: ' \
                     -H 'Authorization: Bearer ' \
                     -H 'Content-Type : application/json' \
                     -H 'X-Request-ID: 123456-1234-1234-1234567890ab' \
                     -X PUT 'https://<Base URI>/consents' \
                     -d '{"balances": [{"Iban": "YY64COJH41059545330222956960771455"}],"transactions": [{"Iban": "YY64COJH41059545330222956960771455"}],"trustedBeneficiaries": true,"psuIdentity": true}' \
                     --cert QWAC-cert.pem --key QWAC-key.pem​

 

Payment Initiation Services (PIS)

The APIs we are exposing for PISP - as for AISP - use the OAuth2 for authorization. The PSU identification, authentication and the signing of payment requests is based on the Strong Customer Authentication (SCA) and it is done in the realm of the bank.

Depending on the bank you would like to integrate with, you have to implement Berlin Group or STET PSD2 API specification.

Below we will show you an example of both types for a simple Payment Initiation Request. For Berlin Group standard, the payment initiation endpoints are separated according to the type of payment product and payment service. As well, additional flows are supported for this standard, namely multiple SCA and signing baskets. These specifics flows are currently out of scope of the present document, but are fully supported by LUXHUB platform. Please refer Berlin Group PSD2 API official documentation for support in this direction.

Berlin Group

This diagram shows the requests to be performed in the case of an API of a bank which uses the Berlin Group specification for Payment initiation.
The requests shown in yellow are explained in details below.

It is to be noted for Berlin Group specifications that the Payment Initiation resource management and life cycle is fully separated from the life cycle and management of the Authorisation resource. Basically the Authorisation resource is used for authorizing the Payment Initiation resource, hence the Authorisation resource endpoints need to be protected by Authorization Code Grant, which involve PSU authentication, whereas Payment Initiation resource can be protected by Client Credentials Grant only. This kind of approach will allow for the Payment Initiation resource, and its status, retrieval by the TPP even before authorization by the PSU. The payment flow will allow to the TPP to retrieve Payment Initiation resource, and dependent entities like Authorisation, before authorization by the PSU. This way, simple management of implicit and explicit authorisation flows can be achieved. Signing Baskets flows will also benefit from the same feature.

Another benefit of this approach is a unified handling of payment flows between Berlin Group specifications and STET, which actually employs exactly the model mentioned above from an authorization perspective, while maintaining a simplified resource representation that does not involve the additional Authorisation resource.

In the example requests below, we will use the sepa-credit-transfer simple payments for illustrating the requests to be made by a TPP for payment initiation.

1. To start, you have to obtain an access token using the client credentials flow. This access token is not related to a PSU’s resources.
  • example request:
curl \
     -H 'Authorization: Basic ' \
     -H 'Content-Type : application/x-www-form-urlencoded' \
     -d 'grant_type=client_credentials&scope=PIS:payment' \
     -X POST 'https://<Token Endpoint>' \
     --cert QWAC-cert.pem --key QWAC-key.pem​

2. The next step is to call the products endpoint to create a new payment resource.

  • example request:

    curl \
         -H 'PSU-IP-Address: "123.123.123.123"' \
         -H 'TPP-Redirect-Preferred: true' \
         -H 'Signature: ' \
         -H 'Authorization: Bearer ' \
         -H 'X-Request-ID: 12345678-1234-1234-1234-1234567890ab' \
         -H 'Content-Type: application/json' \
         -d '{"endToEndIdentification":"123456-1234-1234-1234567890ab", "debtorAccount":{"iban":"FR7612345987650123456789014","currency":"EUR"}, "instructedAmount":{"currency":"EUR","amount":"30"}, "creditorAccount":{"iban":"FR1420041010050500013M02606","currency":"EUR"}, "creditorName":"SFR","creditorAddress":{"street":"rue blue","buildingNumber":"89","city":"Paris","postalCode":"75000","country":"FR"}, "remittanceInformationUnstructured":"AwesomeTPP"}' \
         -X POST 'https://<Base URI>/payments/sepa-credit-transfers' \
         --cert QWAC-cert.pem --key QWAC-key.pem​

 

3. In the following step, you will have to create an authorization resource for this payment.
  • example request:

    curl \
         -H 'Signature: ' \
         -H 'Authorization: Bearer ' \
         -H 'X-Request-ID: 12345678-1234-1234-1234-1234567890ab' \
         -H 'Content-Type: application/json' \
         -X POST 'https://<Base URI>/payments/<payment_id>/authorisations' \
         --cert QWAC-cert.pem --key QWAC-key.pem​

 

4. This current step is described from the point of view of a TPP accessing the API of a bank that implemented Berlin Group standard for payments using the concept of “explicit authorisation”. In this concept the authorisation resource has to be created explicitly by the TPP, i.e. by calling the POST endpoint of this resource. This model is more generic and allows as well for multiple SCA flow.

5. There is another model of payment accepted by Berlin Group specification, the so called “implicit authorisation”. In this model, the authorisation resource is created implicitly by the bank together with the payment initiation resource, so there is no need to explicitly call the POST /authorisations endpoint. This model can be applied only for the single SCA flow, where the payment is authorized by a single PSU.

  • Additional details about these two models can be found in the official documentation of Berlin Group.

6. The next step will actually authorize the payment authorisation resource. This is achieved by redirecting the PSU to his bank for SCA.
  • example request:

    curl \
         -X GET 'https://<Authorization Endpoint>?response_type=code&scope=PIS:<consent_id>&client_id=<client_id>&redirect_uri=http%3A%2F%2F127.0.0.1%3A9003%2Fredirect/_payment&state=12345678-1234-1234-1234-1234567890ab&resource=payments/<payment_authorization_id>/ '

 

7. Your application has to retrieve the OAuth2 authorization code. This is achieved by the bank’s SCA redirecting the PSU to the redirect URL defined in your request.
Your application should accept the parameters specified below.
You can try this redirection by pasting the GET request from step 3 into your browser (you have to configure your browser to use the Mock Certificate) while ensuring that your application is listening at the specified redirection URL.
  • example request:

    curl \
         -X GET 'http://127.0.0.1:9003/redirect?code=<authorization_code>'​

 

8. Once you have received the authorization code, you can ask for OAuth2 access and refresh tokens to access the status of the payment initiation.
  • example request:

    curl \
         -H 'Authorization: Basic ' \
         -H 'Content-Type : application/x-www-form-urlencoded' \
         -d 'grant_type=authorization_code&redirect_uri=http%3A%2F%2F127.0.0.1%3A9003%2Fredirect_payment&code=<authorization_code>&scope=PIS:<consent_id>' \
         -X POST 'https://<Token Endpoint>' \
         --cert QWAC-cert.pem --key QWAC-key.pem​

 

9. Your payment initiation request is now authorized and you can check its status by calling the following request
  • example request:

    curl \
         -H 'Signature: ' \
         -H 'Authorization: Bearer ' \
         -H 'X-Request-ID: 12345678-1234-1234-1234-1234567890ab' \
         -H 'Content-Type: application/json' \
         -X GET 'https://<Base URI>/payments/ ' \
         --cert QWAC-cert.pem --key QWAC-key.pem​
Multiple SCA flows

Berlin Group specification for APIs allows supporting multiple SCA for payment initiation (and consent authorization), based on the introduction of an additional entity called Authorisation. There will be such an entity created for each SCA that needs to be performed; the rules for how many SCA needs to be done are specific - per ASPSP, account, etc. - therefore there is a need to communicate to the TPP when it needs to perform additional authorisations. Below flow describes the approach a TPP needs to use in order to implement multi SCA.

It is of note, that multiple SCA can be implemented only by using explicit authorisation resource creation and, for now, only available only from ASPSP implementing Berlin Group API standard.

There is of note an extension to the Berlin Group standard API that is proposed in the diagram below: AUTHORISATION_FINALIZED value can be added by implementing ASPSP to the list of valid values returned after SCA, i.e. when last authorisation resource is signed/authorized by PSU. As such it will be a clear indication for TPP that authorization process is finished; otherwise the TPP has to query the transactionStatus of the payment to know if additional authorisations are required or the existing authorisation have to still be approved by the PSU.

 

 

Note: also see Berlin Group Implementation Guidelines 1.3 sections 5.1.9, 5.8, 6.4.4, 8.3. Most of the banks will have multi SCA implemented for payment initiation services only.

 

STET

Below diagram shows the requests to be performed in the case of an API which uses STET specification for Payment Initiation.
The requests shown in yellow are explained in detail below.

 

1. To start, you have to get an access token based on your OAuth2.0 credentials.
  • example request:

    curl \
         -H 'Authorization: Basic ' \
         -H 'Content-Type : application/x-www-form-urlencoded' \
         -d 'grant_type=client_credentials&scope=pisp' \
         -X POST 'https://<Token Endpoint>' \
         --cert QWAC-cert.pem --key QWAC-key.pem​

 

2. Then you have to create a payment resource
  • example request:

    curl -i \
         -H 'Signature: ' \
         -H 'Authorization: Bearer ' \
         -H 'X-Request-ID: 12345678-1234-1234-1234-1234567890ab' \
         -H 'Content-Type: application/json' \
         -d '{"paymentInformationId":"0f49608cd17a49048cc808dfa1047572", "creationDateTime":"2019-01-18T17:07:43.455Z", "numberOfTransactions":1, "initiatingParty":{ "name":"AwesomeTPP","postalAddress":{"country":"LU","addressLine":[null,null]}, "organisationId":{"identification":"12LU5","schemeName":"COID","issuer":"ACPR"} }, "paymentTypeInformation":{"serviceLevel":"SEPA","localInstrument":"INST","categoryPurpose":"DVPM"}, "debtor":{"name":"John Smith"}, "debtorAccount":{"iban":"YY64COJH41059545330222956960771321"}, "ultimateCreditor":{ "name":"myMerchant","postalAddress":{"country":"FR","addressLine":["18 rue de la DSP2","75008 PARIS"]}, "organisationId":{"identification":"852126789","schemeName":"SIREN","issuer":"FR"},"privateId":null }, "paymentInformationStatus":"RCVD","creditTransferTransaction": [{ "paymentId":{"instructionId":"01ab09d3e59e4e2a95ddb83e4d7a0dbe","endToEndId":"5f429404c96843e2a791cd8a5150b6a0"}, "instructedAmount":{"currency":"EUR","amount":"1"}, "remittanceInformation":["Fake remittance information."] }], "supplementaryData":{"acceptedAuthenticationApproach":["REDIRECT"] }}' \
         -X POST 'https://<Base URI>/payment-requests' \
         --cert QWAC-cert.pem --key QWAC-key.pem​

 

3. You have to authorize (and authenticate) the PSU. After calling this URL, the PSU will be redirected to SCA of the chosen bank.
  • example request:

    curl \
         -X GET 'https://<Authorization Endpoint>?response_type=code&scope=pisp&client_id=<client_id>&redirect_uri=http%3A%2F%2F127.0.0.1%3A9003%2Fredirect_payment&state=12345678-1234-1234-1234-1234567890ab&resource= &code_challenge=<code_challenge_pkcs>&code_challenge_method=S256

 

4. Once the PSU has performed SCA for payment authorization, he will be redirected to your redirect URL. The following request has to be served by your application.
  • example request:

    curl\
         -X GET 'http://127.0.0.1:9003/redirect?code=<authorization_code>'​

 

5. Once you have received the authorization code, you can ask for access and refresh tokens.
  • example request:

    curl \
         -H 'Authorization: Basic ' \
         -H 'Content-Type : application/x-www-form-urlencoded' \
         -d 'grant_type=authorization_code&redirect_uri=http%3A%2F%2F127.0.0.1%3A9003%2Fredirect_payment&code=<authorization_code>&scope=pisp' \
         -X POST 'https://<Token Endpoint>' \
         --cert QWAC-cert.pem --key QWAC-key.pem​

 

6. Finally, you can retrieve the status of your payment initiation
  • example request:

    curl \
         -H 'Signature: ' \
         -H 'Authorization: Bearer ' \
         -H 'Content-Type : application/json' \
         -H 'X-Request-ID: 123456-1234-1234-1234567890ab' \
         -X GET 'https://<Base URI>/payment-requests/ ' \
         --cert QWAC-cert.pem --key QWAC-key.pem​

 

Funds Confirmation Services (CBPII)

All APIs are protected by OAuth2 authorization. To receive authorization & access tokens, the PSU has to give consent for the application to access his resources - in case of funds confirmation services these are: accounts funds confirmation. The TPPs that are allowed to provide funds confirmation services according to PSD are called Card Based Payment Instrument Issuer and this particular flow is used normally for card accounts, but not only, to confirm funds before payment.

Consent

According to PSD2 directive, the PSU has to give his consent for a TPP to be able to access funds confirmation services API. However, it is not exactly specified how this should be done. It is further assumed, based on current industry practices, that a valid way of giving consent might be as out of band, i.e. based on an agreement between TPP, PSU and bank outside of the scope of the API.

STET specification

Taking into account the generic consideration above regarding consent, the STET specification allows funds confirmation consent to be given in two distinct ways:

  • via an out of band agreement between TPP, PSU and bank, where the bank has a record of the PSU consent for the respective TPP and based on this a Client Credentials OAuth2 flow is allowed. It is entirely up to the bank to verify such an out-of-band consent exists and if it is authorized by the respective PSU for the respective TPP. If this option is used, LUXHUB will allow funds confirmation endpoint access for ALL registered TPPs via Client Credentials Grant and it is up to the ASPSP to restricted according to the existing authorized consent.
  • via dedicated API authorization scope, i.e. a dedicated OAuth2 authorize request has to be made by the TPP, as part of the Authorization Code grant. The scope requested should be "cbpii". In this approach the PSU will be able to authorize the consent for funds confirmation using SCA, in a similar flow as in the case of account information services consent. Please note that the CBPII consent is not to be mixed with AISP consent, neither business wise or technically, as in OAuth2 scopes.

LUXHUB supports both approaches described above; please refer below diagram for details.

Berlin Group specification

Berlin Group specification considers, in its current - 1.3 - implementation, the consent for funds confirmation services as out of scope for the XS2A API. Therefore, the only approach supported is the one based on the Client Credentials flow and this is not part of the Berlin Group specifications.

However, recognizing the market's need, LUXHUB is also supporting an approach similar with the one supported by STET specification, based on dedicated scope for funds confirmation services consent and Authorization Code grant. The scope requested should be "PIIS". In this approach, the PSU will be able to authorize the consent for funds confirmation using SCA, within a similar flow as in the case of account information services consent. Please note that the CBPII consent is not to be mixed with AISP consent, neither business-wise or technically, as in OAuth2 scopes.

Please refer above diagram for details; it is of note that the technical scope used is named "PIIS" in case of Berlin Group implementation as opposed to "cbpii" in case of STET.

Furthermore, please note that with this type of flow only consent for funds confirmation for ALL eligible accounts is possible and not specific account consent. Same is valid for STET standard as described above.

Berlin Group v2 API

Recently Berlin Group published the so called "Extended value-added services" documentation, among them a proposal of handling consent for funds confirmation via a dedicated API. However the API specification for this is still under review and no reliable version was published.

As such, very few of LUXHUB API providers have implemented this version 2 API form Berlin Group. Please refer to the documentation above for detials of the functionality. In a nutshell, we are talking about a flow very similar with the one for Accounts Information Services consent but with consent request data structure closer to the payment initiation, i.e. the actual account, for which funds confirmation consent request is received, is included in the request body. There is, of course, a dedicated OAuth2 scope for this purpose, i.e. PIS for cleint Credentials Grant to obtain a consent and, respectively, PIIS: for the Authorization code grant for consent authorization - where the consent identifier is obtained as a result of the POST /consents/funds-confirmation request before.

API specifications standards additions

BIC query parameter

LUXHUB is a multi-tenant platform and among the ASPSP customers hosted on the platform there are banks that have multiple entities published under the same API. In order to accommodate such scenario, an additional query parameter Bank Identification Code (BIC) is added to all endpoints.

The value used for this parameter needs to match exactly the BIC code of the entity your application is trying to access. Furthermore, you need to have a valid PSD2 passport for the country where the respective entity resides. Please see the API page in the portal for more details.

Extended transaction history

As required by the EBA RTS on SCA and CSC, there should be additional SCA performed whenever a transactions history for more than 90 days is requested by a TPP. Berlin Group specifications do not provide dedicated handling of this scenario, whereas STET standard describes it in details and proposes a solution.

LUXHUB decided to implement the same solution as for the STET standard for the Berlin Group standard and offer it as a value addition to its customers.

The AISP will be able to ask for an extended transaction history with the very first access token retrieved after a token request. In this case, a single SCA will be required and used to get the token and to ask for an extended transaction history. Any further extended transaction history request will be considered as out of scope of this SCA.
If the access token scope cannot cover the request (case of extended transaction history request for instance):

  • The request will be rejected with HTTP403 with an error equal to insufficient_scope.
  • The refresh token will be revoked so the request could be replayed once a new token, having the right scope, would have been requested and provided.
  • The new refresh token will be valid up to 90 days.

The additional scope to be used for this case in Berlin Group implementations is AIS_EXT - this is additional scope and not replacing the regular one. For STET implementation this same scope is named extended_transaction_history.

Please see diagram below for details of the flow.

Sandbox environment considerations

The Sandbox environment offered by the LUXHUB Platform aims to duplicate, as much as deemed possible, the behavior of each ASPSP API in a real Production environment.

The data exposed in each Sandbox is a synthetic representation of the real data available in the respective ASPSP Production environment. As well, for all practical purposes, the journey of a PSU that connects to a TPP app and gives its consent to it to access its account information and/or to initiate payments, will be verbatim to the actual Production implementation, however small discrepancies might appear in the flows and/or data available. All these can be reported and fixed during the operation of the Sandbox.

The LUXHUB Platform Sandbox environment is based on static data. Data is normally static, i.e. it will not be modified as result of Sandbox usage. For example, a payment initiation submitted to an account will appear in transaction history but will not modify the account balance. According to the respective bank implementation of the API it might be that not all the fields needed in the created transaction will be present - this is because the values of such fields might be based on internal reference processes not available to the Sandbox application, but available in real scenarios to the bank’s core banking system. However, all mandatory fields will be present and taken care of in all scenarios.

The PSU identification credentials available for the Sandbox are not necessarily bank specific but generic only. By default, there are three accounts for testing, all with a default password and OTP. The mock authentication for each of these PSUs will work independent of the password used, but it will fail in case any other OTP aside from 123456 is used.

The Sandbox environment is continuously available for testing by the TPPs. Support is available to the TPP during business hours.

Data cleanup will be performed during a “nightly build” process on the Sandbox, i.e. all resources created and/or updated during the day by TPPs, including consent resources, will be reset to initial state.

Sandbox environment is providing verification of the connection to TPP from transport and application layer point of view.
– Mutual TLS will be insured for all connected TPPs as well as checks on the role of the TPP
– Validation of syntax of the request/response conversations will be done against the API specification provided by the respective standard
– Error reporting is done as specified in the documentation associated with each API standard published by the ASPSP
– Correctness of flows is verified as well and related documentation made available

Minimal test scenarios recommendations

A TPP connected to an ASPSP's Sandbox in LUXHUB environment is recommended to test all services and payment products available, in positive and negative test scenarios. Due to the fact that Sandbox data is static, below we suggest an approach on how to test positive and negative scenarios based on dedicated data.

Disclaimer

· Please note that the recommendations below are not exhaustive nor do they try to substitute in any way on the TPP being able to test functionality the way they see it fit. It is merely a suggestion of minimum scenarios that will provide a sanity check of the Sandbox.

· TPPs are encouraged to adjust their test flows and procedures to the exact functionality offered by each specific bank API, according to the PSD2 standard API specification implemented, functionality provided and details of implementation. To this purpose please consult the API technical documentation available via LUXHUB Developer Portal and/or get in touch with our Support Team that it is at your disposal to answer questions and provide clarifications.

· LUXHUB cannot guarantee that below recommendations are followed to the letter by each bank, however, these same recommendations were made to each bank related to the data provided in the Sandbox. If you encounter issues in following along the lines of the recommendations below, please get in touch with our Support Team which is at your disposal to answer questions and provide clarifications.

TPP role and certificate scenarios
  1. The TPP should be able to register his application on LUXHUB developer portal with either an eIDAS certificate, provided to him by a QTSP, or with a mock certificate generated on the portal itself.

  2. The Sandbox will check that: correct certificate is provided by TPP, correct role is used while addressing an AIS, PIS or PIIS/CBPII service.

  3. In the current version of the Sandbox, the generated certificates and related resources - organizations, applications, etc. - are not deleted as part of the nightly cleanup process so they can be reused.

Request/response validation
  1. TPP requests against each API endpoint will be validated against the proposed interface of the respective API method as in Production, i.e. against the published API specification for each ASPSP.
  2. ASPSP responses should be validated both by the Sandbox and the TPP against that same API specifications published by the ASPSP.

  3. The validations referred above are purely technical, i.e. it will deal with data format and structure and not with its content. This type of business validation will be described later in this document.

  4. The endpoints made available and their respective data structures are specific to each ASPSP and under its responsibility.
  1. The consent management in LUXHUB Sandbox is dynamic, i.e. consents are managed like resources themselves. Therefore, the end to end production flows can be implemented and consent management can also be tested as such in the Sandbox.
  2. Consent creation, of the type supported by each ASPSP according to its exposed standard, should be possible.
  3. Consent verification should be possible in the Sandbox, when used to access resources involved. Appropriate error handling, as per standard specifications, needs to be in place for invalid consents.
  4. Consent lifecycle should be implemented as per respective specification.
Accounts recommendations
  1. The Sandbox environment should be able to simulate different behaviors of the API based on the account data configured. There should be a sufficient number of accounts available to allow all scenarios defined by each ASPSP.
  2. ASPSP should offer accounts, at least with IBAN or BBAN identification, with different transaction history and balances. The consent management should be applicable to the accounts defined by the ASPSP.

  3. There should be at least one account configured for high volume of data, i.e. a big volume of transactions should be available for at least one account. For this account, TPP can test pagination option offered by ASPSP. What constitutes “high volume” as well as page size should be defined by each ASPSP. This recommendation is valid for accounts that do offer pagination and/or high volumes.

  4. There should be at least one blocked account, i.e. an account which, although consent might have been authorized, is blocked so access should not be granted. This can also be simulated by entering a wrong OTP code in the SCA page.
  5. There should be accounts that will allow testing the respective consent models supported by the ASPSP:

  6. For detailed consent there should be at least 2 accounts defined to allow the PSU to authorize consent only to one of them

  7. If consent is expired, revoked by TPP, revoked by PSU or its access frequency exceeded the access to the respective authorized accounts should not be allowed (similar for balances and transactions)

  8. The TPP should be able to perform a test to access accounts information with PSU present to verify access is allowed after exhaustion of allowed access when PSU is not present (frequency indicator in consent). In Sandbox environment this indicator cannot be changed, it is fixed at 4 times a day.

  9. There should be an account, or more, having all available balances according to ASPSP business implementation
  10. There should be an account giving access to each type of currency supported by the ASPSP
  11. There should be an account, or more, having transactions of each type supported by ASPSP
  12. There should be accounts with such balances that can allow different type of payments - while data is stateless in the Sandbox, it should make logical sense
  13. There should be enough accounts in the system to allow payment initiation for different scenarios
Card accounts recommendations

Berlin Group API standard has dedicated end-points for handling of card accounts. However, these are different just in the way they are presented to the API clients, their behavior and data is quite similar to other payment accounts exposed via the API.
Card accounts are normally the target for funds confirmation use cases as well, as it is common industry practice, for such accounts, to verify funds availability before payment initiation.
Therefore, the same recommendations as in the case of generic payment accounts apply.

Balances recommendations
  1. All type of balances supported by the ASPSP in its other online interfaces should be available in the Sandbox.
Transactions recommendations
  1. All type of transactions supported by the ASPSP in its other online interfaces should be available in the Sandbox.
  2. A big enough number of transactions should be supported to test pagination functionality if offered by ASPSP.
Funds confirmation recommendations
  1. TPP role conformity should be evaluated for access to funds information endpoints.
  2. It should be possible to test availability of funds in positive and negative scenarios.
Payment initiation recommendations
  1. It should be possible to make a payment initiation and authorize it via SCA. Cancellation of said payment should be possible if supported by the respective ASPSP. Negative scenarios should be possible as well, including successful SCA but not executed payment for business or technical reasons, i.e. daily limits exceeded, invalid amount, date, etc.

  2. It should be possible to make a payment initiation with SCA exemption. A dedicated account not requiring SCA for payment initiation can be setup for this purpose.

  3. Multiple SCA scenarios should be supported if supported in real Production by the respective bank. Usually there will be a dedicated account setup in the system that will require multiple SCA flow. If you encounter issues in working with this flow, please get in touch with our Support Team which is at your disposal to answer questions and provide clarifications.

  4. Signing basket scenarios should be supported if supported in real Production by the respective bank. If you encounter issues in working with this flow, please get in touch with our Support Team that it is at your disposal to answer questions and provide clarifications.

In case you need support, do not hesitate to check the Support page to find more information or to get in touch with us.

This website uses cookies. By continuing to use our website, you accept the use of these cookies.