Quick Start Guide PIS
This quick start guide for the Payment Initiation Service (PIS) will provide an overview of the process that initates a payment on behalf of the consumer.
API Structure
Open banking. by Klarna features two sets of API endpoints.
The XS2A API is used to configure and start the payment initiation process and retrieve information about it. All endpoints are authenticated.
Whenever consumer interaction is required the client-session based Auth API can be used to obtain and submit forms that the consumer has to fill out.
Starting the Session
In order to initiate a payment for a consumer an XS2A API session has to be created.
This can be done by executing a PUT
request towards https://api.openbanking.playground.klarna.com/xs2a/v1/sessions
.
PUT https://api.openbanking.playground.klarna.com/xs2a/v1/sessions HTTP/1.1
Content-Type: application/json;charset=utf-8
Authorization: Token <Token>
curl "https://api.openbanking.playground.klarna.com/xs2a/v1/sessions" -X "PUT" -H "Authorization: Token <token>"
Several optional configurations can be made by adding a JSON-payload to this request. If e.g. a bank should be preselected for the sessions the payload would look like this:
{
"selected_bank": {
"bank_code": "88888888",
"country_code": "de"
},
"psu": {
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36",
"ip_address": "123.123.123.123"
},
"redirect_return_url": "https://merchant.com/redirect-return"
}
While the selected_bank
property is optional, the properties psu
and redirect_return_url
have to be provided.
Once the session is created the request returns the flows that can be executed (among other details about the session).
{
"data": {
"session_id": "a14mjk9r6mjknfqschsnc43j915oa74t",
"self": "https://api.openbanking.playground.klarna.com/xs2a/v1/sessions/a14mjk9r6mjknfqschsnc43j915oa74t",
"flows": {
"balances": "https://api.openbanking.playground.klarna.com/xs2a/v1/sessions/a14mjk9r6mjknfqschsnc43j915oa74t/flows/balances",
"transfer": "https://api.openbanking.playground.klarna.com/xs2a/v1/sessions/a14mjk9r6mjknfqschsnc43j915oa74t/flows/transfer",
"account_details": "https://api.openbanking.playground.klarna.com/xs2a/v1/sessions/a14mjk9r6mjknfqschsnc43j915oa74t/flows/account-details",
"accounts": "https://api.openbanking.playground.klarna.com/xs2a/v1/sessions/a14mjk9r6mjknfqschsnc43j915oa74t/flows/accounts",
"transactions": "https://api.openbanking.playground.klarna.com/xs2a/v1/sessions/a14mjk9r6mjknfqschsnc43j915oa74t/flows/transactions"
}
}
}
Starting the Flow
To initiate a payment the transfer
flow has to be started in the previously created session.
This can be done by executing a PUT
request towards the URL provided in data.flow.transfer
.
PUT <data.flow.transfer> HTTP/1.1
Content-Type: application/json;charset=utf-8
Authorization: Token <Token>
curl "<data.flow.transfer>" -X "PUT" -H "Authorization: Token <token>"
The following is an example for the payload that is required for the request to be successful:
{
"type": "transfer",
"amount": {
"amount": 1234,
"currency": "EUR"
},
"reference": "Flowers",
"preferred_date": "2019-06-30",
"to": {
"iban": "DE53100700000648489390",
"holder_name": "John Doe",
"bic": "DEUTDEBBXXX",
}
}
The response of this request could look like this:
{
"data": {
"flow_id": "cb7ih6o9u0hp5v9nag3aeppqp562n49g",
"state": "CONSUMER_INPUT_NEEDED",
"self": "https://api.openbanking.playground.klarna.com/xs2a/v1/sessions/a14mjk9r6mjknfqschsnc43j915oa74t/flows/cb7ih6o9u0hp5v9nag3aeppqp562n49g",
"client_token":
"eyJhbGciOiJSUzI1NiJ9.eyJ4czJhX2FwcF91cmwiOiJodHRwczovL2F1dGgucGxheWdyb3VuZC5vcGVuYmFua2luZy5rbGFybmEuY29tL2NiN2loNm85dTBocDV2OW5hZzNhZXBwcXA1NjJuNDlnIiwid2l6YXJkX3VybCI6Imh0dHBzOi8vYXV0aGFwaS5wbGF5Z3JvdW5kLm9wZW5iYW5raW5nLmtsYXJuYS5jb20veHMyYS92MS93aXphcmQvY2I3aWg2bzl1MGhwNXY5bmFnM2FlcHBxcDU2Mm40OWciLCJ0cmFuc2xhdGlvbnNfdXJsIjoiaHR0cHM6Ly9hdXRoYXBpLnBsYXlncm91bmQub3BlbmJhbmtpbmcua2xhcm5hLmNvbS94czJhL3YxL3RyYW5zbGF0aW9ucy9jYjdpaDZvOXUwaHA1djluYWczYWVwcHFwNTYybjQ5ZyIsImZsb3dfdHlwZSI6InRyYW5zZmVyIn0.mLYn6OQsTL4expO70hVtqqQG1j250aGpHA2mkyrPIs1EuCi_BdwNrHG3jRMMHL2w-JDig-TvpLNSeFPRNhEeFQm7p19aFwoMkKnDvTUuGjuiRMcO3x1RIyyt72KfiOClv2VZXHvp9rszCfQcIdb1LA9vSHlhCDJUfshlFyLBlV2Prp1xPfbUeyYzE3p2RM1kokV5RzVaUvJK-deTTkLYa0qn_oS2ih-M7ydFZ4yX0X4t9EeQ_oK3tD0HmMUxTM57QPfPYb3_aR4N5H1qKaABIwETmCA0COP0dujp141pHPtAhK7o7NLE7lgtKVIV6u8i21VjePyFXfnpg-Z2Rv7XdA"
}
}
Handling Consumer Interaction
The state CONSUMER_INPUT_NEEDED
indicates that the consumer has to provide some sort of information or configuration to enable Open banking. by Klarna to proceed with the flow.
Information on the type of user interaction is contained within the JSON Web Token (JWT) in the client_token
porperty.
Once decoded the payload of the JWT is going to look like this:
{
"session_id_short": "4MJK9R6M",
"xs2a_app_url": "https://auth.openbanking.klarna.com/cb7ih6o9u0hp5v9nag3aeppqp562n49g",
"auth_url": "https://authapi.openbanking.playground.klarna.com/xs2a/v1/wizard/cb7ih6o9u0hp5v9nag3aeppqp562n49g",
"translations_url": "https://authapi.openbanking.playground.klarna.com/xs2a/v1/translations/cb7ih6o9u0hp5v9nag3aeppqp562n49g",
"flow_type": "transfer"
}
The actual data about the consumer interaction can then be obtained by executing a GET
request towards the URL provided in the auth_url
property.
GET <data.client_token.auth_url> HTTP/1.1
Content-Type: application/json;charset=utf-8
curl -X "GET" "<data.client_token.auth_url>"
In this example a one-time-password is required to authorize the payment. Therefore an XS2A Form has to be filled out by the consumer. Hence the response of the previous request could return the following JSON string:
{
"data": {
"state": "CONSUMER_INPUT_NEEDED",
"next": "https://authapi.openbanking.playground.klarna.com/xs2a/v1/wizard/1234567890123456789",
"result": {
"type": "form",
"category": "",
"message": "",
"configuration": {},
"form": {
"form_identifier": "e807915b-338a-4fc1-988c-089241efab43",
"elements": [
{
"content": "Your bank sent an OTP to your mobile phone.",
"type": "text",
"tags": [],
"version": "1.0.0"
},
{
"key": "OTP",
"type": "input",
"tags": [],
"version": "1.0.0",
"password": false,
"label": "OTP",
"placeholder": "",
"prefill_value": "",
"read_only": false,
"validator": {
"min_length": 6,
"max_length": 6,
"required": true,
"numeric": true,
"letters": {
"uppercase": false,
"lowercase": false
},
"symbols": false
}
}
],
"version": "1.0.0"
},
"key": "eyJhbGciOiJSUzI1NiJ9.eyJtb2R1bHVzIjoiZDEwOTA3YzBkZjc4ZmI5ZDMyNmI1NzkyZGExZDg2OGRkNGViZWRiMmM1ODI4M2UxNTQ2NzFmMmZhNWY2OThiYTI3NWJjZGE0YjA4YTMwOTM4MWNiYWRlOGVmMzRhMTJkM2RhOTM3YjlmMjI4N2EyYTZjY2YxYWU4YWI3ZGRhZTM3ZWNmNzRkM2JlYWQ1OTdhNmY0ZjkwNWZiMzczNGIxY2FhOTk0ZDlhMmNmYjZiZjUyYzAwNmU2MDNmN2E1NGFlODM0NmZiY2VhZmQzODQwMjRlOWI4ZjkyOWI3ODNiMDc0MTljZGNlNGZkMmJlYTcxMzNhYjk3N2M2OWQ1NjVhNzZjMGU4MWZiMDk0NGM3N2NkMGJjYmU4ZmQ4YzY5MmQ3MDNlMDY0ZGYwNzE0OGUxMDRlOWU3ZGY1NDgxZjIwMmRkMjBiODk5ZTEwM2FlYTZiMjM1YjkyZDc4Y2I3MGMyNTNjMjE0NzcyNWVhYmVhMWU4NDZlM2YwZDgzNmYzODZiNzc3YmMwNmYyZWQzZjVmNzllMzA1YjIzM2I3OWQ1OGI3OWVkYjUxOTU5ZGJmYjk4MjM0YmE1MDE3MWVjM2ZlZWMxOTU4Mjc2NDg1NzEyZjI3MDk1ZGNlZDQ4NDVlYjgyMzg4YWVlMmFjOWRlMDc2YzNiMjE1YzY3OTNjMDNmODkiLCJleHBvbmVudCI6IjEwMDAxIn0.zEfK3O9geI1h4l2SaxxUnbfR3pKy0a5CUs3Bfwxgmd56JCOIIMX-l7KhefELqAALekUwFXT0RnMFWmD4DY99p1gT3ETNeZEe_Spv1ednlrVpaMOi68-FT91l3tyoo-XzycCNeTJ1aGqrttZbuMzAOqLfMnwqd0PszzN7pPEDbwEoatWpX5UpTpYQNNDigZHZOHERqaYEP2Kq0YyZUBYxqPPr_Tvm6qGB4iRB6UG3DxIdCoHV_M3fmuHCP62qI9mHArLP96CC4PcAlGnqLUkRQyWgw5Kdfqj0j5LSNq4CHYlva_KcQAFjGYyBd0GwRUrFZPE6VUWda9Lt4dk0AVY1Pw",
"context": "authorization"
}
}
}
A rendering of the XS2A Form in the data.form
property in the JSON string above could look like this:
Form with OTP to confirm transfer
The data that was entered into the XS2A Form has to be transmitted to the URL provided in the data.state.next
property in the response above.
POST <data.state.next> HTTP/1.1
Content-Type: application/json;charset=utf-8
curl -X "POST" "<data.state.next>"
The payload of this POST
request has to be encrypted and should be structured like this:
{
"form_identifier": "e807915b-338a-4fc1-988c-089241efab43",
"data": [
{
"key": "OTP",
"value": "123456"
}
]
}
Evaluating the Flow's Result
Information about the flow can be obtained by executing a GET
request towards the URL provided in the data.self
property in the response of the request that started the flow.
GET <data.self> HTTP/1.1
Content-Type: application/json;charset=utf-8
Authorization: Token <Token>
curl "<data.self>" -H "Authorization: Token <token>"
The response for this request could look like this:
{
"data": {
"state": "FINISHED",
"result": {
"adjusted_reference": "Flowers",
"from": {
"id": "0",
"alias": "Giro account (Steven Sender)",
"iban": "DE06000000000023456789",
"account_number": "23456789",
"holder_name": "Steven Sender",
"bank_code": "",
"bic": "SFRTDE20XXX",
"transfer_type": "FULL",
"account_type": "DEFAULT"
},
"authentication_method": "MTAN",
"type": "transfer"
}
}
}
The value FINISHED
of the property data.state
indicates that the payment has been successfully initiated.
Furthermore information about the sender account will be provided.
Closing the Session
Once the payment has been initiated the session has to be closed by executing a DELETE
request towards the URL that is provided in the data.self
property in the response of the request that started the session.
DELETE <data.self> HTTP/1.1
Content-Type: application/json;charset=utf-8
Authorization: Token <Token>
curl "<data.self>" -X "DELETE" -H "Authorization: Token <token>"