Getting started

Initiate Payments

This guide shows how to integrate to the PIS API. It will contain information on how to initiate a domestic private account to account payment.

Variables and constants used in the guide

NameDescription
accessTokenAn access token with scope "paymentinitiation private".
TOKEN_URLThe token URL. For production, use https://auth.openbankingplatform.com/connect/token
bicWill contain the BIC of the bank that the user selected.
CLIENT_IDThe Client ID of the application you created in the Developer Portal.
CLIENT_SECRETThe secret that was generated when you created an application. If you did not save that value, you need to generate a new secret.
paymentAmountThe transaction amount
paymentCurrencyThe transaction currency
psuUserAgentThe User-Agent from the user's request.
psuIpAddressThe user's IP address
xRequestIDMost requests require the header X-Request-ID, which is a uuid. This will be a unique identifier of your request and will be useful in case you need support. Make sure to create a new GUID for every individual request. In this guide, we assume that you store this value in the variable xRequestID.

First we need to create a payment initiation. This is where we specify the details of the transaction.

Endpoint

POST /psd2/paymentinitiation/v1/payments/domestic
javascript

Request headers

Accept: "application/json",
Authorization: "Bearer " + accessToken,
Content-Type: "application/json",
PSU-IP-Address: psuIpAddress,
PSU-User-Agent: psuUserAgent,
X-BicFi: bic,
X-Request-ID: xRequestID,
javascript

Request body

{
    instructedAmount: {
        currency: paymentCurrency,
        amount: paymentAmount,
    },
    creditorName: creditor.name,
    creditorAccount: {
        iban: "SE123456789",
        currency: paymentCurrency
    },
    remittanceInformationUnstructured:
        "This text shows up on the transaction, both for creditor and debtor",
    debtorAccount: {
        bban: "123456789",
        currency: paymentCurrency
    },
}
javascript

Result

// The paymentID will be used in the coming requests
paymentID = response.body.paymentId
javascript

2. Start Payment Initiation Authorisation Process

Next we need to create an authorisation for the payment initiation we just created.

Endpoint

POST /psd2/paymentinitiation/v1/payments/domestic/${paymentID}/authorisations
javascript

Request headers

Accept: "application/json",
    Authorization: "Bearer " + accessToken,
    Content-Type: "application/json",
    PSU-IP-Address: psuIpAddress,
    X-BicFi: bic,
    X-Request-ID: xRequestID,
javascript

Result

// Contains the id that represents at least one of Mobilt BankID (mbid), Mobile BankID on this device (mbid_same_device),
// Mobile BankID on another device (mbid_animated_qr_image).
authenticationMethodID = response.body.scaMethods[0].authenticationMethodId;
// URL that will be used in the call to Update PSU Data for Payment Initiation (next step).
authoriseTransactionUri = response.body._links.authoriseTransaction.href;
//Resource identification of the related SCA, 
// will be used in the final step of the Redirect approach.
paymentAuthorisationID = response.body.authorisationId;
javascript

3. Update PSU Data for Payment Initiation

This request triggers the authorisation flow. If the ASPSP supports Animated QR, the response.body.scaMethods array will contain an element with mbid_animated_qr_image and/or mbid_animated_qr_token. When multiple choices are given, always use mbid_same_device (or mbid if mbid_same_device is not available) if authorisation is to be performed on the same device, and mbid_animated_qr_image or mbid_animated_qr_token when using different devices.

Endpoint

POST authoriseTransactionUri
javascript

Request headers

Accept: "application/json",
Authorization: "Bearer " + accessToken,
Content-Type: "application/json",
PSU-IP-Address: psuIpAddress,
X-BicFi: bic,
X-Request-ID: xRequestID,
javascript

Request body

{
    "authenticationMethodId": "authenticationMethodID"
}
json

Result

The first thing we need to check in the response is the SCA method used by the bank (Decoupled or Redirect), which will be extracted from the response headers.

// "DECOUPLED" or "REDIRECT"
scaApproach = response.headers.aspsp-sca-approach;
javascript

We are interested in different values from the response depending on if we got Decoupled or Redirect.

If the SCA flow is Decoupled, and you chose mbid in the previous step, you need to get the autoStartToken.

autoStartToken = response.body.challengeData.data[0]
javascript

You can the construct the full QR code like this:

bankIdLink = "https://app.bankid.com/?autostarttoken=" + autoStartToken + "&redirect=null"
javascript

If your users only uses desktops, the above is enough. You will display the QR code in the browser and poll the status of the Payment Initiation Authorisation to see when it's finalised. Make sure to re-render the image if the autoStartToken changes.

Use the bankIdLink to ask the user to open the link on its mobile device. You'll need to adjust the bankIdLink slightly if you want to support users on a smartphone. If you want the user to return to your website after using Mobilt BankID, redirectUriAfterDecoupledAuthentication should point to where you want the user to be redirect to. If you have an application, redirectUriAfterDecoupledAuthentication should be set to null, this will open the previously opened application once the Mobilt BankID session is done.

bankIdLink = "https://app.bankid.com/?autostarttoken=" + autoStartToken + "&redirect=" + redirectUriAfterDecoupledAuthentication
javascript

If the SCA flow is Decoupled, and you chose mbid_same_device, generate the bankIdLink in the same way as outlined for mbid and open the link.

If the SCA flow is Decoupled, and you chose mbid_animated_qr_image, you'll need to display the B64 encoded image animatedQRImage and instruct the user to scan it within the Mobilt BankID application. The image is only valid for 1 second, and must be refreshed by calling Get Payment Initiation Authorisation SCA Status repeatedly at least every second but no more often than every 500ms.

// Example value: "data:image/png;base64, + B64"
animatedQRImage = response.body.challengeData.image
javascript

If the SCA flow is Decoupled, and you chose mbid_animated_qr_token, you'll need to generate a QR image from animatedQRToken and instruct the user to scan it within the Mobilt BankID application. The image is only valid for 1 second, and must be refreshed by calling Get Consent Authorisation SCA Status repeatedly at least every second but no more often than every 500ms.

animatedQRToken = response.body.challengeData.data[0]
javascript

In case of Redirect approach, you need to extract the link to our auth server (which in turn will redirect to the bank's external authentication page) and replace the placeholders with the relevant values.

redirectLinkToBank = response.body._links.scaOAuth.href
javascript

Replace the following placeholders in redirectLinkToBank in the following way:

"[CLIENT_ID]" should be replaced by your CLIENT_ID.

"[TPP_REDIRECT_URI]" is the URI you want us to redirect to after we get confirmation from the bank that the user has authenticated. This URI has to be whitelisted for your application in the Developer Portal.

"[TPP_STATE]" is a convenience field for you to put in anything you want, for example something that identifies this session. It's important that you can identify the correct session after the PSU is redirected back again.

We now have what we need to let the user authorise the payment intiation. The flow will differ completely between Decoupled and Redirect, so the intructions will be separated.

4a. Decoupled

If using desktop, you use the bankIdLink or animatedQRToken to generate a QR code, or the ready-made PNG QR in animatedQRImage, that you present in your UI. Call the Get Payment Initiation Authorisation SCA Status endpoint until scaStatus is failed or finalised. If the data in response.body.challengeData.data[0] or response.body.challengeData.image changes, you must re-render the image displayed to the user. When the user has successfully authenticated and authorized the payment, the status of the Payment Initiation Authorisation will be finalised. To know the status of the Payment Initiation, you should poll the OPE API endpoint Get Payment Initiation Status.

GET /psd2/paymentinitiation/v1/payments/domestic/{paymentID}/authorisations/{paymentAuthorisationID}
javascript

Request headers

Accept: "application/json",
Authorization: "Bearer " + accessToken,
Content-Type: "application/json",
PSU-IP-Address: psuIpAddress
X-BicFi: bic,
X-Request-ID: xRequestID,
javascript

Result

scaStatus = response.body.scaStatus;
// if mbid or mbid_same_device
autoStartToken = response.body.challengeData.data[0];
// if mbid_animated_qr_image
animatedQRImage = response.body.challengeData.image;
// if mbid_animated_qr_token
animatedQRToken = response.body.challengeData.data[0];
javascript

4b. Redirect

First you need to route the user to redirectLinkToBank. When the user has authenticated, the bank will route the user back to the URI you replaced "[TPP_REDIRECT_URI]" with. Once there, you extract the URL parameters code and scope.

To finalise the payment, you make the following request:

Endpoint

POST TOKEN_URL
javascript

Request headers

Content-Type: "application/x-www-form-urlencoded",
X-PaymentId: paymentID,
X-PaymentAuthorisationId: paymentAuthorisationID,
javascript

Request body

{
    "client_id": "CLIENT_ID",
    "client_secret": "CLIENT_SECRET",
    "code": "code",
    "redirect_uri": "redirectURI", // Will be the same value as what you "replaced [TPP_REDIRECT_URI] with in the end of step 4.
    "scope": "scope",
    "grant_type": "authorization_code"
}
json

Result

accessToken = response.body.access_token
javascript

If you receive an access token it means that the request was successful.

5. Get Payment Initiation Status

The last thing to do is to check the status of the Payment Initiation.

GET /psd2/paymentinitiation/v1/payments/domestic/{paymentID}/status
javascript

Request headers

Accept: "application/json",
Authorization: "Bearer " + accessToken,
Content-Type: "application/json",
PSU-IP-Address: psuIpAddress,
PSU-User-Agent: psuUserAgent
javascript

Result

transactionStatus = response.body.transactionStatus
javascript

The Payment Initiation can have a number of different statutes. Read more about them in the Endpoint details. Currently we want to check if the payment was rejected. In that case, transactionStatus will have the value "RJCT". If not, then we are done.