Getting started
The ZTL API API is modular, with the following areas of functionality:
- Consent - Manages end-customer consent, a necessary condition for using both payment and account.
- Onboarding - Starting an onboarding with an end-customer organization, afterwards the customer can use ZTL Pay.
- Payment - Handles the process for domestic and cross-border invoice payments.
- Account - Access the accounts of a customer and see account numbers, balances and transactions.
Credentials
To call the API you need:
- A client secret. Provided by your ZTL contact person.
- An access token which you will get by implementing steps described in the authenticate with ZTL guide.
API Description
Api examples are provided along the description of each section. The below documentation only refers to v1.
We are currently working on a new version on our APIs, but only v1 is officially supported:
- V1 documentation used in Norway
- V2 documentation used in other countries than Norway. WARNING: This is just beta, reach out to us before trying this.
Error handling
Error handling follows HTTP error codes as closely as possible. In general meaning:
- 401 - No access
- 404 - Not found
- 400/403 - Invalid content
- 500 - Server Error
- 503 - Temporarily down
Environments
The following is a list of environments available when developing against or using our API:
Resource | Sandbox | Prod |
---|---|---|
API Endpoint | https://api.sandbox.ztlpay-test.io | https://api.ztlpay.io |
Authentication endpoint | https://oidc.sandbox.ztlpay-test.io | https://oidc.ztlpay.io |
Testing in Sandbox Environment
We refer to banks sandbox documentation for updated info about valid test data.
Bank | Documentation | Verified |
---|---|---|
DNB | https://developer.dnb.no/api-explorer | ✔️ |
Nordea | https://openbanking.direct.nordea.no/portal-sandbox/gettingstarted | ❌ |
While some banks above are not verified, some functionality works. Depending on your use-case you can test and see. Account services usually work for all of them.
Glossary for ZTL API
These are terms used in ZTL API:
General
- Onboarding - Part of ZTL API that starts onboarding with an end-customer organization, afterwards the customer can use ZTL Pay.
- Payment - Part of ZTL API that handles the process for domestic and cross-border invoice payments.
- PSU - Payment Service User.
- SCA - Strong Customer Authentication.
- Consent
- Part of ZTL API that manages end-customer consent, a necessary condition for using both payment and account.
- A consent provided by the PSU to the PSD2 TPP.
- Authorization - Where the PSD2 ASPSP authorizes access to an account service, with or without SCA.
- PAIN Xml - Payment Initiation Xml, part of a ZTL payment request (see iso20022.org and creating Pain.001).
- Account - Part of ZTL API that accesses the accounts of a customer to retrieve account numbers, balances and entries.
- IBAN - International Bank Account Number, standardized account number including country code and validation (see bank.codes/iban ).
PSD2 providers and services
- PSD2 - Payment Service Directive 2.
- AIS - Account Information Services. Getting information about accounts and entries.
- PIS - Payment Initiation Services. Initiating payments and checking information about it.
- TPP - Third Party Provider.
- AISP - A TPP in the role as an Account Information Service Provider.
- PISP - A TPP in the role as a Payment Initiation Service Provider.
- ASPSP - Account Service Payment Service Provider.
- NAAS - Nets Account Access Service, the background PSD2 provider we use. While we try to isolate this, you will sometimes see NAAS mentioned.
Authentication with ZTL
Access Token
Access tokens are used in token-based authentication which is what most of our API endpoints are hidden behind. This means you are required to obtain an access token to gain access to these endpoints
curl --request POST \
--url 'https://oidc.sandbox.ztlpay-test.io/connect/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=client_credentials' \
--data 'client_id=YOUR_CLIENT_ID' \
--data 'client_secret=YOUR_CLIENT_SECRET' \
--data 'scope=payments'
To get a new access token you need to provide your client with the following information:
- URL:
https://oidc.sandbox.ztlpay-test.io/connect/token
- Grant Type:
client_credentials
- Client ID:
<Your given ID here>
- Client Secret:
<Your given secret here>
- Scope:
payments
- Client Authentication:
In body
orHTTP Basic auth header
Using the Token
curl -X GET \
https://api.sandbox.ztlpay-test.io/api/ENDPOINT \
-H 'Authorization: Bearer ACCESS_TOKEN'
Access tokens should always be sent in the header of every HTTP request going to our API. Simply set the Authorization
header to Bearer ACCESS_TOKEN
to gain access to our endpoints
Trying to send a request without a correct or valid access token will result in most requests returning a 401
response code
External links
- For more information about the flow of the client credentials grant type visit the RFC page here
- If you want to inspect your access token to see what information it contains you can do so here
Onboarding
Starting an onboarding
# Start onboarding example request
curl -X POST \
https://api.sandbox.ztlpay-test.io/api/onboarding \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-d '{ "organizationId": "123456789", "webhookUrl": "https://example.com", "redirectUrl": "https://example.com" }'
Make a POST
request to https://api.sandbox.ztlpay-test.io/api/onboarding
to start an onboarding.
The request object must conform to the schema shown in the example.
Body description
Start onboarding example request body
{
"type": "object",
"description": "Start onboarding",
"definitions": {},
"required": ["organizationId"],
"properties": {
"organizationId": {
"$id": "#/properties/organizationId",
"type": "string",
"examples": ["12341234"],
"pattern": "^(.*)$"
},
"webhookUrl": {
"$id": "#/properties/webhookUrl",
"type": "string"
},
"redirectUrl": {
"$id": "#/properties/redirectUrl",
"type": "string"
}
}
}
- organizationId: The organization identifier of the company to be onboarded.
- webhookUrl: A webhook url to receive status updates from the onboarding process.
- redirectUrl: The user is redirected to this URL after the onboarding process has completed as well as if they cancel the onboarding process. Normally this will be the URL of the ERP the end-user originated from. Must start with a protocol (HTTP/HTTPS).
Response
- The flowId is the identifier for the unique onboarding process.
- The onboardingUrl shows the onboarding status https://onboarding.sandbox.ztlpay-test.io/onboarding/d293b808-8adf-4280-9449-8bf52d49795b
Please note that both http response code 200 and 201 are possible: - 200 returned if there is an existing onboarding process for the company from before (flowId of the initial onboarding is returned) - 201 is returned on a new onboarding process request
Start onboarding example response
{
"flowId": "d293b808-8adf-4280-9449-8bf52d49795b",
"onboardingUrl": "https://onboarding.sandbox.ztlpay-test.io/onboarding/d293b808-8adf-4280-9449-8bf52d49795b"
}
Onboarding Expiration and canceled flows:
An onboarding process is persisted for an undetermined time, but no less than 45 days. However, processes which are pending signing are forcefully expired after 45 days. Upon expiration the process is forcefully canceled. Canceled processes cannot be resumed. If the process is expired then feel free to initiate a new one.
Status API
curl -X GET \
https://api.sandbox.ztlpay-test.io/api/onboarding/d293b808-8adf-4280-9449-8bf52d49795b/status \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
The status of a onboarding can be retrieved via the status endpoint.
GET
request to https://api.sandbox.ztlpay-test.io/api/onboarding/d293b808-8adf-4280-9449-8bf52d49795b/status
:
It should be made clear that both a process and its tasks can have status indicators. These values conform to the kebab-casing convention (lowercase words separated by hyphens).
Tasks
A single task can have the following status codes:
completed
: The task has been completed.failed
: The task has failed during execution.pending
: The task has not been started and is pending.
Status
Example response, successful onboarding
{
"orgName": "Ztl",
"orgNo": "123456789",
"status": "completed",
"tasks": [],
"redirectUrl": "https://example.com",
"webhookStatuses": {
"complete": {
"status": "success",
"timestamp": "2021-12-16T18:19:55.267Z"
}
}
}
Example response, terminated by case worker
{
"orgName": "HVOSLEF AS",
"orgNo": "816812542",
"status": "archived",
"tasks": [],
"redirectUrl": "https://demo.dev-2.ztlpay.io/onboarding",
"webhookStatuses": {
"complete": {
"status": "not-provided",
"timestamp": "2021-12-29T15:23:10.989Z"
}
},
"archivedBy": {
"role": "case-worker"
}
}
Example response, onboarding terminated by user
{
"orgName": "Ztl",
"orgNo": "123456789",
"status": "archived",
"tasks": [],
"redirectUrl": "https://example.com",
"webhookStatuses": {
"complete": {
"status": "pending",
"timestamp": "2021-12-16T18:19:55.267Z"
}
},
"archivedBy": {
"role": "unknown"
}
}
A single process always has a status
, represented as an exact string. Please note that new values for status
may be added in the future. Possible statuses are listed below, divided into four conceptual categories:
Active processes
A process with any of these statuses is active/ongoing.
failed
: The process has one or more failed tasks. This is usually caused by a problem with an external service, but could also be an internal error. Caseworkers from ZTL will have to manually restart the failed task, which may allow the process to continue.manual-handling
: The company has been flagged for manual handling, and must be examined by a caseworker.pending
: The process has a pending user task, and is waiting for the end user.processing
: The process is in automatic processing. This status is short-lived, and is normally replaced within a few seconds.document-signed-awaiting-packaging
: The contract has been signed by all signers and is awaiting pades packaging by Signicat. This status is short-lived, and is normally replaced within a few seconds.document-signed-packaging-complete
: The contract has been signed and packaged successfully, and awaits further processing. This status is short-lived, and is normally replaced within a few seconds.initiating
: The process is starting. This status is short-lived, and is normally replaced within a few seconds.
Successful processes
completed
: The company has been successfully onboarded, and the process is complete.already-onboarded
: The company has already been onboarded.
Unsuccessful processes
A process with any of these statuses is considered declined or rejected, and cannot be restarted or otherwise progressed, even by a caseworker. The end user will have to start a new onboarding from scratch.
company-not-found
: Failed to get necessary information on this company, either from Brreg or Bisnode. This may be due to an incorrect orgnr. Note that if Brreg or Bisnode is offline or otherwise fails, afailed
status is triggered instead.declined-enk-no-vat
: A legacy status, indicating that the company was declined due to not being registered in VAT registry. After June 2022, companies are no longer rejected for this reason, but the status can still be returned for old onboardings.declined-risk-evaluation
: The company has been declined due to risk evaluation, either manually or automatically.declined-unsupported-company-type
: The company has been declined due to an unsupported company type (organisasjonsform).declined-missing-company-type
: The company has been declined due to a missing company type (organisasjonsform).signorder-expired
: The end user did not sign the contract within the required 45 days, causing the signing order in Signicat to expire.voluntary-credit-block-bisnode
: Company have credit block (kredittsperre) enabled, company should disable and try again
Canceled processes
archived
: The process has been archived, e.g. manually canceled by the end user or a caseworker. This is an irreversible operation, and can be done at any time, even after the onboarding is completed. These processes will also have anarchivedBy
field in the API response, see above.
webhookStatuses
POST payload for completed webhook
{
"eventType": "Completed",
"entityName": "Onboarding",
"entityId": "flowId",
"entity": {
"organizationId"
},
"timeStamp": "new Date().toISOString()"
}
webhookStatuses is a collection of "webhookResponse" objects which contains various information about a provided webhook. E.g: The "complete" webhook, depicted in the JSON example, contains a status of the provided webhook (success) and a timestamp of the webhookResponse objects instantiation. All webhookResponse objects have two properties: status, and timestamp.
A webhook's status is always one of the four values mentioned below:
success
: A POST request has been successfully made to the webhookURLpending
: Webhook URL has been received, but not yet been used in any POST requests.failed
: A POST request towards the webhookUrl has failed. This is highly indicative of an invalid webhookUrl.not-provided
: No webhook URL was received for this onboarding.
Note that an onboarding will still continue its flow, regardless of any webhookResponse object's status that is associated with the application. The list of possible webhook objects can potentially be expanded upon in the future, however there is currently only a "complete" webhook in use. Depicted below, is the POST requests payload for the "completed" webhook URL post request.
Final notes
Phone Numbers And E-mail Messages
We send real messages via sms and email to the specified recipient during an onboarding. This is true for development and test environments as well. Be aware that this could have unfortunate side effects should you use random people's contact information during testing.
Data quality in sandbox environment
The data quality in this environment may at times be lacking or outright missing due to missing data from our providers.
Known cases: 1. Data about Ultimate Beneficial Owners(UBO) is unavailable. Feel free to add your own UBO entries via the Know Your Customer(KYC) form.
Consent
Consent is given by the ERP's end-user (customer) by means of strong customer authentication (SCA).
ZTL Payment Solution requires the end-user to give consent for payment and AISP services. The end-user's bank is handling the actual strong customer authentication through BankID. During the process, a consent-token is generated and passed back to the ERP system. The consent should be stored by the ERP system and can be used as long as it is valid (180 days).
Even though the consent has an expiry date, it is not guaranteed to be valid until that date. Banks can cancel and re-issue consents for a multiple of reasons (usually security). So please make sure to handle expired consent errors in all APIs with an issuing of a new consent.
Creating a Consent Token
curl -X POST \
https://api.sandbox.ztlpay-test.io/api/consents \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: "123123123"' \
-H 'PSU-IP-Address: 192.158.1.38' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
-d '{ "accountOwnerSsn": "{SSN}",
"accountOwnerOrganizationReference": "{Organization Number}",
"callbackUrl": "https://www.google.com",
"debtorBankBic": "DNBANOKK",
"instructionId": "ca8c24ad-740b-464b-8cfb-85fe4ae2ca44"
}'
// Example consent response 200
{
"scaUrl": "https://lldkit7c6f.execute-api.eu-west-1.amazonaws.com/Prod/bankid/logon?blob=eyJ1c2VySWQiOiIzMTEyNTQ2NDM0NiIsInJlZGlyZWN0VXJsIjoiaHR0cHM6Ly9zYW5kYm94Lm5hYXMtdGVzdDEubmV0cy5ldS9kbmJhbm9ray9jb25zZW50L2FjY2VwdC80OTJjYWZhYzJiYzY0ODliOTRkM2M4YWVkZTNhNmYyMyIsIm1vZGUiOiJQcm9kIiwiY29uc2VudElkIjoiZGJhNjk2ZTgtMTA0NC00NzBiLTkyZGQtNmQwZTI2YTcwZTYzIiwicHNkMkVuZHBvaW50IjoiaHR0cHM6Ly9pbnRlcm5hbC1zYW5kYm94LXNoYXNsLWFsYi05MTg3MDk3MDUuZXUtd2VzdC0xLmVsYi5hbWF6b25hd3MuY29tL3YxL25ldGJhbmsvcHJveHkvYXV0aGVudGljYXRpb24vdXBkYXRlc3RhdHVzIiwiY29uc2VudEVuZHBvaW50IjoiaHR0cHM6Ly9pbnRlcm5hbC1zYW5kYm94LXNoYXNsLWFsYi05MTg3MDk3MDUuZXUtd2VzdC0xLmVsYi5hbWF6b25hd3MuY29tL3YxL25ldGJhbmsvcHJveHkvYXV0aG9yaXNhdGlvbi9kYmE2OTZlOC0xMDQ0LTQ3MGItOTJkZC02ZDBlMjZhNzBlNjMiLCJwYXltZW50SWQiOiJBSVN8ODMzNjIwMWYtYzY2OS00ZTFkLWIxOGEtNjhlN2EzZTc0MjY4IiwidHBwIjoiUFNETk8tVFNUTkNBLTQ1NTIzMzk3fE5ldHMgTm9yd2F5IiwidGltZXN0YW1wIjoiMTU3NjA3MDYxNzY0MiJ9&redirectUrl=https%3A%2F%2F%2Fdnbanokk%2Fconsent%2Faccept%2F492cafac2bc6489b94d3c8aede3a6f23&la",
"consentAuthorizationId": "492cafac2bc6489b94d3c8aede3a6f23",
"status": "AuthorizationRequired",
"reason": null,
"expirationDate": "2020-03-10T14:23:36.57+01:00"
}
ZTL Payment Solution exposes an endpoint that the ERP can use to let its users create consent-tokens:
Headers description
PSU-IP-Address
: The IP address of the end user. E.g: "192.158.1.38". This field will be mandatory starting 2022-05-02.Idempotency-Key
: Unique identifier for idempotency
Body description
The ERP has to provide four fields to ZTL, which will be used to create the token at the customer's bank:
accountOwnerSsn
: (Mandatory) The SSN of the account owner providing consent.accountOwnerOrganizationReference
: (Mandatory) The organization number/reference of the account owner.callbackUrl
: Redirect URLdebtorBankBic
: (Mandatory) The BIC of the payer's (debtor) bank. Example DNB -DNBANOKK
.instructionId
: An instruction ID that will be added as a query parameter in the SCA callback.
Strong Customer Authentication
The scaUrl
in the response will take the user to the banks's portal for Strong Customer Authentication. This will
allow the end user to create a consent-token using one of the methods suppored by the bank, e.g.:
- BankId
- BankId on Mobile
The SCA can be either decoupled or embedded, depending on the bank's implementation. The bank also controls how the created consent-token is sent back to the PSD2 aggregator.
Going to the scaUrl
in the current test-environment will direct the user to the PSD2 aggregators example SCA page, in
this case DNB. To authorise, simply press the button labeled with Fortsett
which will redirect the user
to the callback URL specified in the initial request. The naasConsentReference
will be added to the redirect URL as a
parameter and formatted similar to this:
https://www.google.com/?instructionId=HEyKZacHyZ&naasConsentReference=eb42984d-4d7d-4d50-829e-6b531501176b&sca=success
The ERP vendor has to store both the consentAuthorizationId
and the naasConsentReference
for further processing. naasConsentReference
will be used as the consentReference
in further API-calls.
For BankID testing you can find a table with valid personal identification numbers that you may use on this page (in Norwegian).
Checking Consent status
consentAuthorizationId
should be used to check the concent status.
curl -X GET \
https://api.sandbox.ztlpay-test.io/api/consents?consentAuthorizationId={consentAuthorizationId} \
-H 'Authorization: Bearer {access-token}'
Consent status response body
json
{ "authorizationType": "RECURRING_PIS,RECURRING_AIS,RECURRING_CAF", "consentState": { "aisExpired": false, "cafExpired": false, "pisExpired": false }, "expirationDate": "2020-05-07T10:03:55.479+02:00", "reason": null, "status": "Accepted" } ```
Note that the reason
field will be null most of the time unless a REJECTED status is fetched from the Aggregator.
Known status
for consents are :
- Accepted
- Rejected
- Expired
- AuthorizationRequired
Revoke Consent
Revoking the consent invalidates the consent and prevents further operations with the consent.
curl -X DELETE \
https://api.sandbox.ztlpay-test.io/api/consents \
-H 'Consent-Reference: {consentAuthorizationId}' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
Account Information
Retrieve the current available and booked balance for the account specified in the authorized query.
Account info
curl -X GET \
https://api.sandbox.ztlpay-test.io/api/accounts/info?consentAuthorizationId={consentAuthorizationId}&consentReference={consentReference} \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
Example response for account info
{
"accounts": [ {
"id": "12043175449",
"name": "Brukskonto",
"bic": "AABASESSXXX",
"bban": "12043175449",
"iban": "NO0812043175449",
"country": "NO",
"currency": "NOK",
"type": "Debit"
} ]
}
With query params: consentAuthorizationId
and consentReference
from Create Consent-step.
In Sandbox, the account_id 12043175449
may be used for querying balance.
Account balance
curl -X GET \
https://api.sandbox.ztlpay-test.io/api/accounts/{accountId}/balance?consentAuthorizationId={consentAuthorizationId}&consentReference={consentReference} \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
Example response for account balance
{
"id": "12043175449",
"name": "Brukskonto",
"bic": "AABASESSXXX",
"bban": "12043175449",
"iban": "NO0812043175449",
"country": "NO",
"currency": "NOK",
"type": "Debit",
"balance": {
"booked": {
"amount": "123.34",
"currency": "NOK"
},
"available": {
"amount": "123.34",
"currency": "NOK"
}
}
}
You can query single account balance.
Account entries
curl -X GET \
https://api.sandbox.ztlpay-test.io/api/accounts/{accountId}/entries?
consentAuthorizationId={consentAuthorizationId}&consentReference={consentReference}&fromDate={fromDate}&toDate={toDate} \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
Example Response for account entries
{
"account": {
"id": "12043175449",
"name": "Brukskonto",
"bic": "AABASESSXXX",
"bban": "12043175449",
"iban": "NO0812043175449",
"country": "NO",
"currency": "NOK",
"type": "Debit"
},
"accountEntries": [
{
"transactionId": "121111112",
"type" : "Debit",
"status" : "Booked",
"bookingDateTime" : "2020-10-06T22:00:00Z",
"valueDateTime" : "2020-10-06T22:00:00Z",
"postedAmount" : {
"amount" : 1.0,
"currency" : "NOK"
},
"debtor": {
"name": "Jan Johansen",
"bban": "",
"iban": ""
},
"creditor": {
"name": "Jan Johansen",
"bban": "",
"iban": ""
},
"purposeText": "Overføring Innland",
"remittanceInformation": {
"reference": "313123121231",
"unstructured": "Fra Avsender Pål Pålsson"
},
"endToEndId" : "100543943"
}
]
}
You can query single account entries.
The query params fromDate
and toDate
are optional and inclusive, and uses the ISO-8601 standard for date.
If omitted, the ASPSP may revert to a default date range. The maximum date range can be retrieved from the
Supported Banks-endpoint.
The amount in postedAmount
is always positive, and its the type
of the entry (i.e., debit or credit) that informs
whether the entry is for an outgoing- or incoming amount.
Clarification regarding header values
According to the official PSD2 regulations, the payment service user (PSU, end user) can fetch their own account information as often as they like without requiring a new SCA/authentication. However, other parties, such as ERP-systems, are limited to 4 retrievals every 24 hours when fetching account information on behalf of the PSU, without the PSU being present.
The presence of the PSU is determined by the information provided in the header fields
user_present
, PSU-Geo-Location
, PSU-IP-Address
and PSU-User-Agent
; these PSU values are all used when
signing requests. When the limit of unattended requests has been reached, further requests will receive an error-response with error code 429.
Payments
Overview
Domestic and international payments are supported.
In short, the payment process consist of the following logical steps:
1. User Input and Validation Using methods to validate payment transactions before initiating
2. Payment Initiation Create the payment job.
3. Status (Pre SCA) Exchange rate and total cost for international transactions should be displayed to end-user before the user goes to SCA signing.
4. SCA End user needs to sign SCA.
5. Status Transaction status updates are available through the status endpoint.
6. Settled The transaction end state, where the money has been transferred to the beneficiary account.
User Input/Validation
The payment content is validated before the payment process is created, and the payment process will not be created in case of validation errors.
The following error situations may occur:
Error | Description |
---|---|
400 urn:pain-invalid | Error validating pain xml content. Indicates syntax/validation error of xml, or content error |
400 urn:missing-header | Required header not set in request (See detail) |
Validation errors
If the payment request fails ZTL's internal validation, a list of validation errors is provided as a part of the response.
Validation error example
{
"code": "urn:max-length-exceeded",
"path": "CstmrCdtTrfInitn.PmtInf[0].Dbtr.PstlAdr",
"message": "Sender's address is too long",
"developerText": "Debtor address line 'Joveien 45 8036 Bodo Norway' with length 27 is longer than the maximum allowed length 20 for debtor agent DNBANOKK"
}
Most of the validation errors are assigned a distinct code for easier error handling and better end-user experience, following table describes possible error codes:
Code | Description |
---|---|
urn:number-of-transactions-invalid |
Provided total number of transactions in a payment job does not match actual number of transactions |
urn:urn:basket-size-exceeded |
Number of transactions in payment request exceeds debtor bank limitation |
urn:sum-mismatch |
Provided control sum of transaction amounts in a payment job does not match actual sum of transaction amounts |
urn:organization-account-mismatch |
Provided organization ID does not own the debtor account |
urn:end-to-end-id-not-unique |
One or several transaction endToEndIDs are used more than one time in the same payment job |
urn:country-missing |
Country code is missing from creditor/debtor (specified in 'path' field) postal address |
urn:max-length-exceeded |
The value in the field provided under 'path' in error object has exceeded maximum length |
urn:amount-invalid |
Transaction amount exceeds the limit for given currency |
urn:bic-missing |
BIC is missing from creditor/debtor (specified in 'path' field) account information |
urn:bic-unknown |
Provided BIC in creditor/debtor (specified in 'path' field) account information could not be matched to a bank or |
BIC could not be derived from the provided account number | |
urn:remittance-info-invalid |
Remittance info is of invalid format (bank specific) |
urn:invalid-characters |
The value in the field provided under 'path' in error object contains invalid characters |
urn:due-date-invalid |
Provided requested execution date is in the past |
urn:due-date-non-business-day |
Provided requested execution date is a non-banking day |
urn:creditor-info-invalid |
Creditor information doesn't match the requirements based on provided country/currency/bank |
urn:iban-invalid |
Provided creditor/debtor (specified in 'path' field) IBAN is invalid for given country |
urn:bban-invalid |
Provided creditor/debtor (specified in 'path' field) BBAN is invalid |
urn:iban-or-bban-missing |
Provided creditor/debtor (specified in 'path' field) account information is missing account number |
urn:kid-invalid |
Provided KID in not valid in MOD10/MOD11 or exceeds maximum length of 25 characters |
urn:invalid-remittance-info |
KID cannot be sent on international payments |
urn:bic-iban-mismatch |
Provided combination of BIC and IBAN is not valid |
urn:debtor-post-code-invalid |
Debtor address is set to Norway, but the given postal code is not a valid Norwegian postal code |
urn:creditor-post-code-invalid |
Creditor address is set to Norway, but the given postal code is not a valid Norwegian postal code |
urn:company-disabled-for-international-payments |
Company is not enabled for international payments. ZTL will contact user for such enablement |
urn:duplicate-payment |
Possible duplicate payment for Nordea. Re-initiation can be done after 1min. |
The following API gives useful information about supported currencies, banks and constraints, and should be used to ensure that valid transactions are created.
Supported currencies
Example of a non-complete response of supported currencies
[
{
"code": "EUR",
"currencyName": "Euro",
"isoScale": 2,
"symbol": "€"
},
{
"code": "ISK",
"currencyName": "Icelandic króna",
"isoScale": 0,
"symbol": "kr"
},
{
"code": "USD",
"currencyName": "United States dollar",
"isoScale": 2,
"symbol": "$"
}
]
Supported currencies shows a list of available currencies for international payments. It also provides currency-specific constraints, e.g. required creditor bank information/format. Note that the only currency supported for payments to Norwegian bank accounts is NOK.
Available due dates
Available due dates for a transaction are dependent on the currency and amount. It's recommended to get a list of available due dates before initiating payments, this is especially important for international transactions.
Available banks
Example of supported bank response
[ {
"bic" : "DNBANOKK",
"constraints" : {
"maxBasketSize" : "20",
"maxInformationFieldLength" : "35",
"maxUnstructuredRemittanceInfoLength" : "108",
"aisMaxDateRangeMonths" : "3",
"aisMaxDateRangeNote" : "supports up to 24 months if consent age < 1h",
"cancelSCARequired": "false",
"validInformationFieldCharactersPattern": "[A-ZÆØÅa-zæøå0-9\\D]"
},
"countryCode" : "NO",
"name" : "DNB"
}, {
"bic" : "HANDNOKK",
"constraints" : {
"maxBasketSize" : "10",
"maxInformationFieldLength" : "35",
"maxUnstructuredRemittanceInfoLength" : "128"
},
"countryCode" : "NO",
"name" : "Handelsbanken"
}, {
"bic" : "NDEANOKK",
"constraints" : {
"maxBasketSize" : "10",
"maxInformationFieldLength" : "30",
"maxUnstructuredRemittanceInfoLength" : "128",
"aisMaxDateRangeMonths" : "12"
},
"countryCode" : "NO",
"name" : "Nordea"
} ]
The Supported banks lists all supported bank. Due to variations in the PSD2 APIs of the different banks, initiating a payment with certain banks may be subject to some limitations. This service also shows the constraints per bank, it is strongly recommended using the constraint information to validate user input for both Payment- and AISP-services.
Initiate Payment
Example request for initiating payment
curl -X POST \
https://api.sandbox.ztlpay-test.io/api/payments \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'PSU-IP-Address: 192.158.1.38' \
-d '{
"organizationId": "123456789",
"countryCode": "NO",
"scaRedirectUrl": "https://example.com/callback",
"idempotencyKey": "123123123",
"consentReference": "1235667-aojsadpjsa-1021903-aojs",
"base64Pain001": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiP...mZJbml0bj4NCjwvRG9jdW1lbnQ+DQo="
}'
Example response body for initiated payment
{
"flowId": "c766e7e0-ea15-4b4a-9574-a27c763f6c17",
"statusUrl": "https://api.sandbox.ztlpay-test.io/api/payments/c766e7e0-ea15-4b4a-9574-a27c763f6c17/status"
}
A new payment with one or more transaction can be created by initiating payment. The successful response contains a link to the payment status, this url should be used for both retrieving updated payment status and to get payment information like SCA url and total cost.
Pain format
Initiate payment uses the Payment Initiation V03 format, xml schema.
Example Pain Xml with single transaction
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>20211217000000-228628058026</MsgId>
<CreDtTm>2021-12-17T15:18:25.925843</CreDtTm>
<NbOfTxs>1</NbOfTxs>
<CtrlSum>12.00</CtrlSum> <!-- Optional -->
<InitgPty></InitgPty> <!-- Can be left empty, but the element is required -->
</GrpHdr>
<PmtInf>
<PmtInfId>20211217000000-228628058026-1</PmtInfId>
<PmtMtd>TRF</PmtMtd> <!-- The value is ignored, but the element is required -->
<NbOfTxs>1</NbOfTxs> <!-- Optional -->
<CtrlSum>12.00</CtrlSum> <!-- Optional -->
<ReqdExctnDt>2021-12-17</ReqdExctnDt>
<Dbtr>
<Nm>Test company AS</Nm>
<PstlAdr>
<StrtNm>Solheimroa 4</StrtNm>
<PstCd>9883</PstCd>
<TwnNm>Evensen</TwnNm>
<Ctry>NO</Ctry>
</PstlAdr>
</Dbtr>
<DbtrAcct><Id><Othr><Id>12043175449</Id><SchmeNm><Cd>BBAN</Cd></SchmeNm></Othr></Id><Ccy>NOK</Ccy></DbtrAcct>
<DbtrAgt><FinInstnId><BIC>DNBANOKK</BIC></FinInstnId></DbtrAgt>
<CdtTrfTxInf>
<PmtId><EndToEndId>762362604515896127385745121302</EndToEndId></PmtId>
<Amt><InstdAmt Ccy="EUR">12.00</InstdAmt></Amt>
<CdtrAgt><FinInstnId><BIC>INGDDEFFXXX</BIC></FinInstnId></CdtrAgt>
<Cdtr><Nm>EUR Company Inc</Nm><PstlAdr><Ctry>NO</Ctry></PstlAdr></Cdtr>
<CdtrAcct><Id><IBAN>DE38500105175114522294</IBAN></Id></CdtrAcct>
<Purp><Cd>OTHR</Cd></Purp> <!-- Optional, defaults to <Cd>OTHR</Cd> -->
<RmtInf><Ustrd>Testing</Ustrd></RmtInf>
</CdtTrfTxInf>
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
Example Pain Xml with two international transactions, first with unstructured remittance info, second with KID
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>20220302000000-521206628232</MsgId>
<CreDtTm>2022-03-02T09:34:52.878633341</CreDtTm>
<NbOfTxs>2</NbOfTxs>
<CtrlSum>30.00</CtrlSum>
<InitgPty></InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>20220302000000-521206628232-1</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<NbOfTxs>1</NbOfTxs>
<CtrlSum>12.00</CtrlSum>
<ReqdExctnDt>2022-03-02</ReqdExctnDt>
<Dbtr>
<Nm>Test company AS</Nm>
<PstlAdr>
<StrtNm>Solheimroa 4</StrtNm>
<PstCd>9883</PstCd>
<TwnNm>Evensen</TwnNm>
<Ctry>NO</Ctry>
</PstlAdr>
</Dbtr>
<DbtrAcct><Id><Othr><Id>12041111111</Id><SchmeNm><Cd>BBAN</Cd></SchmeNm></Othr></Id><Ccy>NOK</Ccy></DbtrAcct>
<DbtrAgt><FinInstnId><BIC>DNBANOKK</BIC></FinInstnId></DbtrAgt>
<CdtTrfTxInf>
<PmtId><EndToEndId>564872588722933892663744621782</EndToEndId></PmtId>
<Amt><InstdAmt Ccy="DKK">12.00</InstdAmt></Amt>
<CdtrAgt><FinInstnId><BIC>CITIIE2X</BIC></FinInstnId></CdtrAgt>
<Cdtr><Nm>DKK Company Inc</Nm><PstlAdr><Ctry>NO</Ctry></PstlAdr></Cdtr>
<CdtrAcct><Id><IBAN>IE50CITI99005133791111</IBAN></Id></CdtrAcct>
<Purp><Cd>OTHR</Cd></Purp>
<RmtInf><Ustrd>Example Message</Ustrd></RmtInf>
</CdtTrfTxInf>
</PmtInf>
<PmtInf>
<PmtInfId>20220302000000-521206628232-2</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<NbOfTxs>1</NbOfTxs>
<CtrlSum>18.00</CtrlSum>
<ReqdExctnDt>2022-03-02</ReqdExctnDt>
<Dbtr>
<Nm>Test company AS</Nm>
<PstlAdr>
<StrtNm>Solheimroa 4</StrtNm>
<PstCd>9883</PstCd>
<TwnNm>Evensen</TwnNm>
<Ctry>NO</Ctry>
</PstlAdr>
</Dbtr>
<DbtrAcct><Id><Othr><Id>12041111111</Id><SchmeNm><Cd>BBAN</Cd></SchmeNm></Othr></Id></DbtrAcct>
<DbtrAgt><FinInstnId><BIC>DNBANOKK</BIC></FinInstnId></DbtrAgt>
<CdtTrfTxInf>
<PmtId><EndToEndId>259078089432288924472847479859</EndToEndId></PmtId>
<Amt><InstdAmt Ccy="EUR">12.00</InstdAmt></Amt>
<CdtrAgt><FinInstnId><BIC>CITIIE2X</BIC></FinInstnId></CdtrAgt>
<Cdtr><Nm>EUR Company Inc</Nm><PstlAdr><Ctry>NO</Ctry></PstlAdr></Cdtr>
<CdtrAcct><Id><IBAN>IE50CITI99005133791111</IBAN></Id></CdtrAcct>
<Purp><Cd>OTHR</Cd></Purp>
<RmtInf><Strd><CdtrRefInf><Tp><CdOrPrtry><Cd>SCOR</Cd></CdOrPrtry></Tp><Ref>123456789</Ref></CdtrRefInf></Strd></RmtInf>
</CdtTrfTxInf>
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
Example Pain Xml with single transaction from a currency account(Valutakonto)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>20211217000000-228628058026</MsgId>
<CreDtTm>2021-12-17T15:18:25.925843</CreDtTm>
<NbOfTxs>1</NbOfTxs>
<CtrlSum>12.00</CtrlSum> <!-- Optional -->
<InitgPty></InitgPty> <!-- Can be left empty, but the element is required -->
</GrpHdr>
<PmtInf>
<PmtInfId>20211217000000-228628058026-1</PmtInfId>
<PmtMtd>TRF</PmtMtd> <!-- The value is ignored, but the element is required -->
<NbOfTxs>1</NbOfTxs> <!-- Optional -->
<CtrlSum>12.00</CtrlSum> <!-- Optional -->
<ReqdExctnDt>2021-12-17</ReqdExctnDt>
<Dbtr>
<Nm>Test company AS</Nm>
<PstlAdr>
<StrtNm>Solheimroa 4</StrtNm>
<PstCd>9883</PstCd>
<TwnNm>Evensen</TwnNm>
<Ctry>NO</Ctry>
</PstlAdr>
</Dbtr>
<DbtrAcct><Id><Othr><Id>12043175449</Id><SchmeNm><Cd>BBAN</Cd></SchmeNm></Othr></Id><Ccy>EUR</Ccy></DbtrAcct>
<DbtrAgt><FinInstnId><BIC>DNBANOKK</BIC></FinInstnId></DbtrAgt>
<CdtTrfTxInf>
<PmtId><EndToEndId>762362604515896127385745121302</EndToEndId></PmtId>
<Amt><InstdAmt Ccy="EUR">12.00</InstdAmt></Amt>
<CdtrAgt><FinInstnId><BIC>INGDDEFFXXX</BIC></FinInstnId></CdtrAgt>
<Cdtr><Nm>EUR Company Inc</Nm><PstlAdr><Ctry>SE</Ctry></PstlAdr></Cdtr>
<CdtrAcct><Id><IBAN>DE38500105175114522294</IBAN></Id></CdtrAcct>
<Purp><Cd>OTHR</Cd></Purp> <!-- Optional, defaults to <Cd>OTHR</Cd> -->
<RmtInf><Ustrd>Testing</Ustrd></RmtInf>
</CdtTrfTxInf>
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
The following table shows an overview of the xml elements used by ZTL.
xml-element | required | description |
---|---|---|
GrpHdr.MsgId | Y | Must be unique max 35 char Id. May not contain special characters including ”å”, “ä” and “ö” |
GrpHdr.NbOfTxs | Y | Number of total transactions in the xml document |
GrpHdr.CreDtTm | Y | CreationDateTime. Assumed to be in timeZone "Europe/Oslo" unless it has an offset |
GrpHdr.CtrlSum | N | Total of individual amounts included in the xml document, irrespective of the currency. Required. |
GrpHdr.InitgPty | Y | InitiatingParty. Required xml element, but may be empty |
GrpHdr.InitgPty.Nm | N | InitiatingParty Name |
PmtInf | Y | There should be one PmtInf per transaction in a basket of payments |
PmtInf.PmtInfId | Y | TransactionId. Must be unique max 35 char Id. May not contain special characters including ”å”, “ä” and “ö” |
PmtInf.PmtMtd | Y* | The element is required by xml standard, but not used in the payment solution. Accepted values:
|
PmtInf.NbOfTxs | N | Number of total transactions in the group - should be 1. Optional, but validated if present. |
PmtInf.CtrlSum | N | Total of all individual amounts included in the group, irrespective of the currency. Optional, but validated if present. Value should be equal to InstdAmt since we only allow one transaction per PmtInf |
PmtInf.ReqdExctnDt | Y | Transaction Due date (from debtor account) |
PmtInf.Dbtr.Nm | Y | Debtor Name |
PmtInf.Dbtr.PstlAdr | Y | Debtor Address. Required for both domestic and international transactions. Minimum required fields: StrtNm, PstCd, TwnNm, Ctry |
PmtInf.DbtrAcct.* | Y | Debtor account number. Can be BBAN or IBAN.
|
PmtInf.DbtrAgt.FinInstnId.BIC | Y | Debtor bank BIC. |
PmtInf.CdtTrfTxInf.* | Y | CreditTransferTransactionInformation - There should only be one CdtTrfTxInf per PmtInf |
PmtInf.CdtTrfTxInf.PmtId.EndToEndId | Y | Unique identification assigned by the initiating party to unambiguously identify the transaction. This identification is passed on, unchanged, throughout the entire end-to-end chain. Max 35char. |
PmtInf.CdtTrfTxInf.Amt.InstdAmt | Y | Amount of money to be moved between the debtor and creditor, before deduction of charges, expressed in the currency as ordered by the initiating party. Ccy - Currency eg "NOK", "EUR", "SEK", "DKK" |
PmtInf.CdtTrfTxInf.CdtrAgt.FinInstnId.BIC | N* | Creditor Bank BIC. Not mandatory for Norwegian domestic transactions - but recommended to include this information. Mandatory for international payments. |
PmtInf.CdtTrfTxInf.CdtrAgt.FinInstnId.ClrSysMmbId.MmbId | N | Creditor Bank Clearing Code. Mandatory in some countries. |
PmtInf.CdtTrfTxInf.Cdtr.* | Y | Creditor Identification. Note that Cdtr.Nm is required for AML reasons. |
PmtInf.CdtTrfTxInf.Cdtr.PstlAdr.* | Y | Creditor Address. Required for both domestic and international transactions. Minimum required fields: StrtNm, Ctry for international transaction and valid PstCd for Norwegian creditors. |
PmtInf.CdtTrfTxInf.CdtrAcct.* | Y | Creditor account number. Can be BBAN or IBAN.
|
PmtInf.CdtTrfTxInf.Purp.Cd | N | PurposeCode - Uses default OTHR if not present. Accepted values:
|
PmtInf.CdtTrfTxInf.RmtInf | N | Remittance info. If present, only one of the following element should be set:
|
Pain validation and errors lists
If pain content validation fails, 400 error are returned with type=urn:pain-invalid
with a list of validations errors with path to exactly the error is.
An example of a pain file with a parse error, Empty ID tag. The parser may report more than one error related to an item (same line and column number).
{
"type" : "urn:pain-invalid",
"title" : "Invalid Pain",
"detail" : "There are 2 errors",
"detail2" : "",
"errors" : [ {
"type" : "parse",
"errors" : [ {
"message" : "cvc-minLength-valid: Value '' with length = '0' is not facet-valid with respect to minLength '1' for type 'Max35Text'.",
"line" : 21,
"column" : 38
}, {
"message" : "cvc-type.3.1.3: The value '' of element 'Id' is not valid.",
"line" : 21,
"column" : 38
} ]
} ],
"traceId" : "803ec5f6-3382-4230-88c0-fa19e4499720"
}
An example of a pain file with a transaction error, Invalid IBAN.
{
"type" : "urn:pain-invalid",
"title" : "Invalid Pain",
"detail" : "There are 1 errors",
"detail2" : "",
"errors" : [ {
"type" : "transaction",
"endToEndId" : "197102754228134604603270233998",
"errors" : [ {
"message" : "IBAN failed checksum validation",
"path" : "cstmrCdtTrfInitn.paymentInformation[0].creditTransferTx[0].cdtrAcct.id.iban"
} ]
} ],
"traceId" : "b3515c0f-a474-470c-a58f-0d2478f3268f"
}
An example of pain xml with transaction error with missing mandatory elements.
{
"type": "urn:pain-invalid",
"title": "Invalid Pain",
"detail": "There are 2 errors",
"detail2": "",
"errors": [
{
"type": "transaction",
"endToEndId": "966700472476787235510074468580",
"errors": [
{
"code": "urn:creditor-name-missing",
"message": "Creditor name is required",
"path": "cstmrCdtTrfInitn.paymentInformation[0].creditTransferTx[0].creditor.name"
}
]
}
],
"traceId": "03f55df4-d7d7-416a-add6-0f427eec7f92"
}
An example of a pain file with a general error, Sum mismatch.
{
"type" : "urn:pain-invalid",
"title" : "Invalid Pain",
"detail" : "There are 1 errors",
"detail2" : "",
"errors" : [ {
"type" : "general",
"errors" : [
{
"code": "urn:sum-mismatch",
"message" : "Control sum and total sum of transactions mismatch. Expected total amount of 100 but was 12.00",
"path" : "cstmrCdtTrfInitn"
} ]
} ],
"traceId" : "4fdce039-f9f1-4fc2-b91a-88580bc3bbb8"
}
Payments from currency account(Valutakonto) and NOK payments abroad
Additional requirements in pain xml for such payments are:
- Debtor account currency must be provided
- Creditor address must EITHER be fully provided including street name, building number, postal code, town and country OR only include country, nothing else
International payments
Additional requirements in pain xml for international payments are:
- The name and address of the creditor are required for international payments.
- IBAN and BIC are required for most currencies, constrains per currencies are available
- All payments to India (with INR) require a payment reason and a purpose code provided in the unstructured remittance info. Those should be placed at line 1 and 2 of remittance info accordingly
An example of a remittance info with payment reason and purpose code (INR/India specific)
<RmtInf>
<Ustrd>Payment reason</Ustrd> <!-- Payment reason described in free text, max 35 characters -->
<Ustrd>P0802</Ustrd> <!-- Payment purpose code, note that the Miscellaneous Purpose Code (P1099) is not permitted -->
</RmtInf>
Status (Pre SCA)
The initiate payment response contains a statusUrl. This Url is used in the remaining payment steps, but the response and statuses will differ depending on the payment status.
Payment status right the transaction has been created
curl -X GET \
https://api.sandbox.ztlpay-test.io/api/payments/c766e7e0-ea15-4b4a-9574-a27c763f6c17/status \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
Example response:
{
"flowId": "c766e7e0-ea15-4b4a-9574-a27c763f6c17",
"processStatus": "active",
"messageId": "1233-12211"
}
The payment transaction goes through a process, and is active as long as processStatus = active
. The response will change as the transaction process goes through its different stages.
Status with total cost related to currency exchange.
{
"scaUrl" : "https://***",
"processStatus" : "active",
"scaStatus" : "AuthorizationRequired",
"transactions" : [ {
"status" : "AuthorizationRequired",
"statusReason" : "Payment accepted at Aspsp and needs to be signed by PSU. Please initiate payment signing process",
"purposeCode" : "OTHR",
"commission" : {
"amount" : "0.25",
"currency" : "NOK"
},
"transactionAmount" : {
"amount" : "125.78",
"currency" : "NOK"
},
"exchangeRate" : "10.4818",
"displayExchangeRate" : "10.4818",
"exchangeRateExpiration" : "2022-08-05T14:48:14.133+02:00",
"fromCurrency" : "NOK",
"toCurrency" : "EUR",
"dueDate" : "2022-02-07"
} ]
}
International transactions contains useful information like exchangeRate
, displayExchangeRate
, transactionAmount
, commission
and exchangeRateExpiration
. It's recommended to show the displayExchangeRate and transactionAmount to the customer
before SCA signing.
Signing (SCA) international transactions includes a currency exchange commitment. It's important to notice that there is a cancellation cost for the customer if the transaction are cancelled or not completed due to insufficient fund after the transaction has been signed. We recommend that either of the following messages should be displayed and confirmed by the user prior to signing international transactions:
- You are now about to enter into a legally binding currency agreement. Cancelling this agreement will incur a cancellation cost - both a fee and any currency exchange loss.
- Du holder nå på å inngå en bindende valutaavtale. Kansellering av denne avtalen vil medføre en kanselleringskostnad - både en avgift og et evt valutatap.
SCA
Normally the payment is ready to be signed when the status response contains scaUrl and scaStatus AuthorizationRequired
.
Regular SCA flow:
{
"scaStatus": "AuthorizationRequired",
"scaUrl": "[sca-url]"
}
At this point, the scaUrl should be presented to the user. After the SCA has been successfully completed, the customer
will be redirected to the scaRedirectUrl
specified in the payment initiation request.
You can then get the status of the payment after the redirect to check the SCA status.
Note: The SCA link that the customer is redirected to initially should also be available in ERP interface on the payments line. That way the customer can click and re-enter the SCA flow to complete the signing. If the customer doesn't do this the flow will automatically expire after some time.
Note that there are some cases in which the payment does not need signing (this is bank specific and can depend on transaction amount, receiver's bank, etc). In such cases no scaUrl or scaStatus will be provided, and the transaction goes to "AcceptedSettlementInProgress" status. Thus, the absence of the scaUrl in "get payment information" response does not necessarily mean that the payment initiation request has failed, here checking the 'processStatus' field (should be "active") can be used to determine if the initiation was successful.
Process flow and dealing with aborted SCA
Accepted SCA:
{
"flowId": "6613c5c0-642f-4ae7-98ee-a8ccdaa486bb",
"processStatus": "active",
"scaStatus": "Accepted",
"transactions": [
{
"endToEndID": "296edf186a374b0a89edfa8e9fd054c5",
"status": "AcceptedSettlementInProgress",
"transactionAmount": {
"amount": "1235",
"currency": "NOK"
},
"commission": {
"amount": "0",
"currency": "NOK"
},
"fromCurrency": "NOK",
"toCurrency": "NOK",
"exchangeRate": 1,
"paymentId": "0fsdfasdfsa3e4d46218b80179d5c19f796"
},
{
"endToEndID": "wd459dp10b7plasjdl202aas5",
"status": "Rejected",
"transactionAmount": {
"amount": "1234.14",
"currency": "NOK"
},
"commission": {
"amount": "14.14",
"currency": "NOK"
},
"fromCurrency": "NOK",
"toCurrency": "SEK",
"exchangeRate": 1.2345
},
{
"endToEndID": "iojadoiwajdijoa123",
"status": "Booked",
"transactionAmount": {
"amount": "1235",
"currency": "NOK"
},
"commission": {
"amount": "0",
"currency": "NOK"
},
"fromCurrency": "NOK",
"toCurrency": "NOK",
"exchangeRate": 1,
"paymentId": "0fsdfasfaserairpasr3484543p5wvretv"
}
],
"messageId": "1233-12211"
}
When the user is sent back to the partner system after completion (or cancelling) of SCA or just coming back after closing the browser window the system should make an API call to get an updated payment status. The SCA URL can be retried as long as the status is AuthorizationRequired. If the status is Rejected or Cancelled, the transaction has failed. In case where the status is AcceptedSettlementInProgress and AcceptedSettlementCompleted the system should just wait for the status Booked to be sure that the process is completed.
When SCA are completed, the scaStatus are changed to "Accepted"
If the user cancels the SCA signing, the scaStatus will be displayed as Rejected
, and transactions status AuthorizationRequired
(since the payment can still be signed from an online bank).
A transaction, where user cancels the SCA:
{
"flowId": "117b7ce7-a133-4dc6-a8d9-d964d18c8582",
"processStatus": "completed",
"scaStatus": "Rejected",
"transactions": [
{
"endToEndID": "296edf186a374b0a89edfa8e9fd054c5",
"status": "Cancelled",
"transactionAmount": {
"amount": "1235",
"currency": "NOK"
},
"commission": {
"amount": "0",
"currency": "NOK"
},
"fromCurrency": "NOK",
"toCurrency": "NOK",
"exchangeRate": 1,
"paymentId": "434wrw43wairpasr3484543p5wvretv"
},
{
"endToEndID": "wd459dp10b7plasjdl202aas5",
"status": "Cancelled",
"transactionAmount": {
"amount": "1234.14",
"currency": "NOK"
},
"commission": {
"amount": "14.14",
"currency": "NOK"
},
"fromCurrency": "NOK",
"toCurrency": "SEK",
"exchangeRate": 1.2345,
"paymentId": "2424lwl4iwl34w34484543p5wvretv"
},
{
"endToEndID": "iojadoiwajdijoa123",
"status": "Cancelled",
"transactionAmount": {
"amount": "1235",
"currency": "NOK"
},
"commission": {
"amount": "0",
"currency": "NOK"
},
"fromCurrency": "NOK",
"toCurrency": "NOK",
"exchangeRate": 1,
"paymentId": "2424iwl34w34484543p5wvretv"
}
],
"messageId": "1233-12211"
}
In order to avoid duplicate payments and confusion for the end users, all unsigned transactions will be automatically cancelled after 25 minutes from payment initiation.
The the scaStatus will be set to Timeout
, and transaction status to Cancelled
with status reason Payment was cancelled due to SCA timeout
. The scaUrl will be set to null.
Note that not all banks support automatic cancellation, in such cases the transactions will stay unchanged and will be available for signing even after 25 minutes.
Transaction with SCA timeout
{
"scaUrl": null,
"flowId": "cc408c13-28eb-4730-8b75-006f239e019e",
"processStatus": "completed",
"scaStatus": "Timeout",
"error": null,
"messageId": "25816052-88A4-49CE-994B-4EF149FE1C5F",
"transactions": [
{
"endToEndID": "553253946837740742720797270914",
"status": "Cancelled",
"purposeCode": "OTHR",
"statusReason": "Payment was cancelled due to SCA timeout",
"exchangeRate": 1,
"fromCurrency": "NOK",
"toCurrency": "NOK",
"dueDate": "2022-09-05",
"paymentId": "4ff415b2901e4737a2f262e2fa356037",
"errors": "null"
}
]
}
Notice that the processStatus is set to completed
when the SCA signing fails.
Payment Status
The transaction is added to the process after a successful SCA. Depending on the due date of the transaction, it might take several days before it will be completed. The transaction status will be updated during the process. The table below shows an overview of the possible statuses.
Status | Description |
---|---|
Imported |
ZTL has imported and parsed the transaction, but no further action has been taken. |
Initialized |
ZTL has initialized the transaction, but the status from the bank is still unknown. |
AuthorizationRequired |
The user needs to complete SCA and the URL for that is in the API response. |
Rejected |
The bank rejected the payment. |
Cancelled |
The payment has been cancelled or SCA has timed out. |
AcceptedSettlementInProgress |
The bank has accepted the transaction, and the payment is ongoing. |
AcceptedSettlementCompleted |
The payment has been completed by the bank. |
Booked |
The payment is successful and process completed at ZTL. |
RejectedFundsNotReceivedInTime |
ZTL has rejected the payment due to not having received the funds by the due date. This is often due to insufficient funds on the debtor account. Notice that unless the payment is also cancelled in/by the bank, the initial payment may still be processed and paid to ZTL at a later time. In this scenario, ZTL will not forward the funds to the creditor. Instead ZTL will return the funds to the debtor |
HeldByBank |
Transaction is temporary stopped by the bank. Customer should contact bank. This status is only shown for partners who have enabled this feature. |
UnknownStateInProgress |
Transaction is in an unknown state (last known status was Rejected ) and can be assumed to be in progress until status is updated. The status is a temporary status used for Evry banks. |
Errors
The status response will contain an error property if an error occurs during the transaction process. The errors can occur for both the payment job/basket, and for the individual transactions.
{
"flowId" : "56563af2-a199-44ec-92a1-d77eed2d8562",
"processStatus" : "completed",
"error" : {
"type" : "urn:quote-failed",
"title" : "Payment rejected",
"detail" : null,
"instance" : "/api/payments/56563af2-a199-44ec-92a1-d77eed2d8562/status"
},
"messageId" : "8A993B4B-8FE8-43AD-924C-DFFEB1D4A430",
"transactions" : [ {
"endToEndID" : "381973911341275448394637392141",
"status" : "Imported",
"purposeCode" : "OTHR",
"errors" : [ {
"type" : "urn:payment:invalid-due-date",
"detail" : "Due date can be maximum 8 business days ahead"
} ],
"fromCurrency" : "NOK",
"toCurrency" : "EUR",
"dueDate" : "2022-02-18"
} ]
}
PaymentJob Errors
Type | Description |
---|---|
urn:quote-failed |
May occur for international payments, if there are problems receiving quote for at least one transaction |
urn:company-disabled |
The company making the payment is disabled (i.e. is not allowed to make payments) |
urn:input-validation-failed |
The payment(s) fails validation (e.g account number, KID, address) |
urn:internal-server-error |
May occur for different reasons when there is a technical problem with the payment solution |
urn:unknown-error |
May occur for different reasons, such as when there is a problem with an external service |
urn:bank-downtime |
The bank service is down |
urn:exchange-provider-downtime |
The currency exchange provider is down |
urn:ocr-error |
The OCR (KID in Norway) rules for the creditor account are violated, e.g. missing KID, creditor account doesn't have an OCR agreement or a KID number that doesn't match the creditor account |
urn:payments-rejected |
May occur for different reasons when the bank rejects one of the payments during payment initiation requests |
urn:consent-expired |
Consent has expired |
Possible Transaction Errors
Type | Description |
---|---|
urn:payment:invalid-due-date |
May occur with payments with invalid due-date |
urn:payment:max-amount-exceeded-for-currency |
May occur if amount exceeds max amount for currency/date. There are limitations for max amount for forward transactions |
urn:payment:currency-invalid-due-date |
Provided currency does not support provided due date |
urn:payment:international-payment-service-closed |
International payment service is closed during weekend |
Settled
The transaction has been successfully completed once the status shows that it's booked:
{
"flowId": "117b7ce7-a133-4dc6-a8d9-d964d18c8582",
"processStatus": "completed",
"transactions": [
{
"status": "Booked"
}
],
"messageId": "1233-12211"
}
Cancel
curl -X POST https://api.sandbox.ztlpay-test.io/api/payments/cancel?consentReference=b0fc2430-3d87-470d-83f5-a4e94d540f6f \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 49ae0cfe-6b72-4310-81f5-ad4eef897fe3' \
-H 'PSU-IP-Address: 153.110.241.229' \
-H 'Authorization: Bearer ACCESS_TOKEN'
-d '{
"instructionId" : "testydsddff345",
"callbackUrl" : "https://www.example.com",
"paymentId" : "c04de502623d4e6f9e8b632278342859"
}'
201 Response
{
"statusReason": {
"reason": "Payment cancellation request is initiated. PSU needs to confirm the cancellation",
"status": "AuthorizationRequired",
"origin": "DNB at NAAS"
},
"scaUrl": "https://lldkit7c6f.execute-api.eu-west-1.amazonaws.com/Prod/bankid/logon?blob=eyJ1c2VySWQiOiIzMTEyNTQ2MTExOCIsInJlZGlyZWN0VXJsIjoiaHR0cHM6Ly9zYW5kYm94Lm5hYXMtdGVzdDEubmV0cy5ldS9kbmJhbm9ray9jYW5waXMvYWNjZXB0L2MwNGRlNTAyNjIzZDRlNmY5ZThiNjMyMjc4MzQyODU5IiwibW9kZSI6IlByb2QiLCJjb25zZW50SWQiOiJlMTdkYmMzMy1mNDM5LTQ0NmYtOWY3YS1jYTc0ZjcyNjExYzUiLCJhY2Nlc3NVcmwiOiJhcHBzL25icC9iZXRhbGluZyIsInBzZDJFbmRwb2ludCI6Imh0dHBzOi8vYXBpLmludGVybmFsMDEuc2FuZGJveC5hcHBzaGFyZWRzdmMudGVjaC0wMy5uZXQvdjEvbmV0YmFuay9wcm94eS9wYXltZW50L3NjYS9lMTdkYmMzMy1mNDM5LTQ0NmYtOWY3YS1jYTc0ZjcyNjExYzUiLCJjb25zZW50RW5kcG9pbnQiOiJodHRwczovL2FwaS5pbnRlcm5hbDAxLnNhbmRib3guYXBwc2hhcmVkc3ZjLnRlY2gtMDMubmV0L3YxL25ldGJhbmsvcHJveHkvYXV0aG9yaXNhdGlvbi9lMTdkYmMzMy1mNDM5LTQ0NmYtOWY3YS1jYTc0ZjcyNjExYzUiLCJwYXltZW50SWQiOiJTU0xfRE9NLTQ5MTQzMjk2MyIsInRwcCI6IlBTRE5PLVRTVE5DQS03ZmEwNjZiMXxOZXRzIFRQUCIsInRpbWVzdGFtcCI6IjE2MDM3ODQyNTYyMTcifQ==&redirectUrl=https%3A%2F%2Fsandbox.naas-test1.nets.eu%2Fdnbanokk%2Fcanpis%2Fdeny%2Fc04de502623d4e6f9e8b632278342859&la"
}
400 Response
{
"type": "urn:cancellation-not-supported-on-payment-date",
"title": "Cancellation not supported",
"detail": "Bank with bic 'NDEANOKK' does not support cancellation on the payment execution date"
}
Payments can be cancelled as long as they have not reached booked status. For cancellation, the PSD2 paymentId are used, obtained from the status endpoint.
Before the user signs the cancellation of an international transaction, either of the following messages should be displayed and confirmed by the user:
- Cancelling the legally binding currency agreement will incur a cancellation cost - both a fee and any currency exchange loss.
- Kansellering av den bindende valutaavtalen vil medføre en kanselleringskostnad - både en avgift samt et evt valutatap.
These messages must remain unaltered. If a translation is missing, please reach out to us.
Note: Nordea does not support cancelling payments on the same day as execution date, a 400 response with an error of type "urn:cancellation-not-supported-on-payment-date" will be sent in such cases.
Payroll
Initiating a payroll payment
curl -X POST \
https://api.sandbox.ztlpay-test.io/api/payroll \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'PSU-IP-Address: 192.158.1.38' \
-d '{
"base64Pain001": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiP...mZJbml0bj4NCjwvRG9jdW1lbnQ+DQo=",
"legalId": "NO89453784",
"consentReference": "8eerj3e-ds79jdc",
"callbackUrl": "http://",
}'
// Example successful payroll payment initiation response
{
"payrollId": "c766e7e0-ea15-4b4a-9574-a27c763f6c17",
"sca": "https://www.dnb.no/segb/appo/logon/psd2Start?blob=P98u8VFpnug..."
}
Headers description
- 'PSU-IP-Address': The IP address of the end user. E.g: "192.158.1.38"
Body description
- 'base64Pain001': base64 encoded pain001 file
- 'consentReference': The consent reference given when creating a consent, e.g: “1235667-aojsadpjsa-1021903-aojs"
- 'legalId': Country code and the organization identifier of the debtor, e.g: "NO920970931"
- 'callbackUrl': The URL where the end-user should be redirected after a completed SCA, e.g "https://example.com/callback"
Requirements for PAIN file
- Message ID must be unique
- Requested execution date must be a business day in Norway and cannot be in the past
- Payroll payments can only be executed in NOK currency
- <CstmrCdtTrfInitn><GrpHdr><CtrlSum> tag is required as it is the amount we use to initiate funding payment from client's account to our client account
- Charge bearer code tag can only have value SHAR, e.g: '
SHAR '
Please note that the deadline to initiate payroll payment and complete SCA is set to 11:00:00 CEST the same day as the desired execution date. However, we recommend to initiate the payroll the day before in case of possible bank downtimes and delays to make sure the recipients get their salaries in time. For example, if a payroll payment needs to be completed on the 23 May 2022, the SCA for payroll initiation must be completed before 11:00:00 on May 23rd 2022. (Best practice would be to do this the day before execution date, May 22nd).
PAIN file goes through several rounds of validation to make sure that a valid payroll payment request can be placed. Response code 422 and a list of validation errors are returned in case the PAIN file fails validation. Most of the validation errors are assigned a distinct code for easier error handling and better end-user experience, following table describes possible error codes:
Code | Description |
---|---|
urn:number-of-transactions-invalid |
Provided total number of transactions in a payment job does not match actual number of transactions |
urn:sum-mismatch |
Provided control sum of transaction amounts in a payment job does not match actual sum of transaction amounts |
urn:end-to-end-id-not-unique |
One or several transaction endToEndIDs are used more than one time in the same payment job |
urn:[creditor/debtor]-country-missing |
Country code is missing from creditor/debtor (specified in 'path' field) postal address |
urn:max-length-exceeded |
The value in the field provided under 'path' in error object has exceeded maximum length |
urn:amount-invalid |
Transaction amount decimal scale exceeds the limit for given currency |
urn:[creditor/debtor]-name-missing |
Name is missing from creditor/debtor (specified in 'path' field) account information |
urn:bic-unknown |
Provided BIC in creditor/debtor (specified in 'path' field) account information could not be matched to a bank or |
BIC could not be derived from provided account number | |
urn:invalid-characters |
The value in the field provided under 'path' in error object contains invalid characters |
urn:due-date-invalid |
Provided requested execution date is in the past or is a non-banking day |
urn:bban-invalid |
Provided creditor/debtor (specified in 'path' field) BBAN is invalid |
urn:iban-or-bban-missing |
Provided creditor/debtor (specified in 'path' field) account information is missing account number |
urn:organization-account-mismatch |
Provided organization ID does not own the debtor account |
urn:currency-invalid |
Provided currency is invalid for a payroll payment (currently only NOK is supported) |
urn:debtor-post-code-invalid |
Debtor address is set to Norway, but the given postal code is not a valid Norwegian postal code |
urn:creditor-post-code-invalid |
Creditor address is set to Norway, but the given postal code is not a valid Norwegian postal code |
// Example failed payroll payment initiation response
{
"status": "422",
"title": "Failed to process payroll payment request",
"errors": [
{
"code": "urn:due-date-invalid",
"path": "CstmrCdtTrfInitn.PmtInf.ReqdExctnDt",
"message": "Requested execution date is invalid, must be a business day and not in the past",
"developerText": "Requested execution date 12-03-2010 is invalid, must be a business day and not in the past"
}
]
}
<!-- Example Pain Xml for a payroll payment -->
<?xml version="1.0" encoding="utf-8"?>
<Document xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
<CstmrCdtTrfInitn>
<GrpHdr>
<MsgId>5057ed457e16419188f92c8f7141350a</MsgId>
<CreDtTm>2021-08-28T17:31:12.2039778</CreDtTm>
<NbOfTxs>3</NbOfTxs>
<CtrlSum>83527.3</CtrlSum>
<InitgPty>
<Nm>Ztl Payment Solution AS</Nm>
<PstlAdr>
<Ctry>NO</Ctry>
</PstlAdr>
<Id>
<OrgId>
<Othr>
<Id>920970931</Id>
<SchmeNm>
<Cd>CUST</Cd>
</SchmeNm>
</Othr>
</OrgId>
</Id>
<CtryOfRes>NO</CtryOfRes>
</InitgPty>
</GrpHdr>
<PmtInf>
<PmtInfId>1tCi5w6IS0W1B4UPaaCvA</PmtInfId>
<PmtMtd>TRF</PmtMtd>
<BtchBookg>true</BtchBookg>
<CtrlSum>83527.30</CtrlSum>
<PmtTpInf>
<SvcLvl>
<Cd>NURG</Cd>
</SvcLvl>
<LclInstrm>
<Prtry>DO</Prtry>
</LclInstrm>
<CtgyPurp>
<Cd>SALA</Cd>
</CtgyPurp>
</PmtTpInf>
<ReqdExctnDt>2021-08-31</ReqdExctnDt>
<Dbtr>
<Nm>Some company AS</Nm>
<PstlAdr>
<Ctry>NO</Ctry>
</PstlAdr>
<Id>
<OrgId>
<Othr>
<Id>563909378</Id>
</Othr>
</OrgId>
</Id>
<CtryOfRes>NO</CtryOfRes>
</Dbtr>
<DbtrAcct>
<Id>
<Othr>
<Id>47011155568</Id>
<SchmeNm>
<Cd>BBAN</Cd>
</SchmeNm>
</Othr>
</Id>
<Ccy>NOK</Ccy>
</DbtrAcct>
<DbtrAgt>
<FinInstnId>
<BIC>SNOWNO22</BIC>
</FinInstnId>
</DbtrAgt>
<ChrgBr>SHAR</ChrgBr>
<CdtTrfTxInf>
<PmtId>
<InstrId>mfQQLEc1EEeYufjjqlpkGw-1</InstrId>
<EndToEndId>mfQQLEc1EEeYufjjqlpkGw-1</EndToEndId>
</PmtId>
<Amt>
<InstdAmt Ccy="NOK">15061.38</InstdAmt>
</Amt>
<Cdtr>
<Nm>Leonard James</Nm>
<PstlAdr>
<StrtNm>Leonardgaten 3</StrtNm>
<PstCd>8001</PstCd>
<TwnNm>BODØ</TwnNm>
<Ctry>NO</Ctry>
<AdrLine>Leonardgaten 3</AdrLine>
<AdrLine>8001 BODØ</AdrLine>
</PstlAdr>
<CtryOfRes>NO</CtryOfRes>
</Cdtr>
<CdtrAcct>
<Id>
<Othr>
<Id>31624847906</Id>
<SchmeNm>
<Cd>BBAN</Cd>
</SchmeNm>
</Othr>
</Id>
</CdtrAcct>
<RmtInf>
<Ustrd>Lønn fra Ztl Payment Solution AS</Ustrd>
</RmtInf>
</CdtTrfTxInf>
<CdtTrfTxInf>
<PmtId>
<InstrId>mfQQLEc1EEeYufjjqlpkGw-2</InstrId>
<EndToEndId>mfQQLEc1EEeYufjjqlpkGw-2</EndToEndId>
</PmtId>
<Amt>
<InstdAmt Ccy="NOK">23138.17</InstdAmt>
</Amt>
<Cdtr>
<Nm>Diana Powell</Nm>
<PstlAdr>
<StrtNm>Dianagaten 2</StrtNm>
<PstCd>4985</PstCd>
<TwnNm>VEGÅRSHEI</TwnNm>
<Ctry>NO</Ctry>
<AdrLine>Dianagaten 2</AdrLine>
<AdrLine>4985 VEGÅRSHEI</AdrLine>
</PstlAdr>
<CtryOfRes>NO</CtryOfRes>
</Cdtr>
<CdtrAcct>
<Id>
<Othr>
<Id>19499114608</Id>
<SchmeNm>
<Cd>BBAN</Cd>
</SchmeNm>
</Othr>
</Id>
</CdtrAcct>
<RmtInf>
<Ustrd>Lønn fra Ztl Payment Solution AS</Ustrd>
</RmtInf>
</CdtTrfTxInf>
<CdtTrfTxInf>
<PmtId>
<InstrId>mfQQLEc1EEeYufjjqlpkGw-3</InstrId>
<EndToEndId>mfQQLEc1EEeYufjjqlpkGw-3</EndToEndId>
</PmtId>
<Amt>
<InstdAmt Ccy="NOK">45327.75</InstdAmt>
</Amt>
<Cdtr>
<Nm>Jason Morgan</Nm>
<PstlAdr>
<StrtNm>Jasongaten 4</StrtNm>
<PstCd>3544</PstCd>
<TwnNm>TUNHOVD</TwnNm>
<Ctry>NO</Ctry>
<AdrLine>Jasongaten 4</AdrLine>
<AdrLine>3544 TUNHOVD</AdrLine>
</PstlAdr>
<CtryOfRes>NO</CtryOfRes>
</Cdtr>
<CdtrAcct>
<Id>
<Othr>
<Id>40846824008</Id>
<SchmeNm>
<Cd>BBAN</Cd>
</SchmeNm>
</Othr>
</Id>
</CdtrAcct>
<RmtInf>
<Ustrd>Lønn fra Ztl Payment Solution AS</Ustrd>
</RmtInf>
</CdtTrfTxInf>
</PmtInf>
</CstmrCdtTrfInitn>
</Document>
Status
curl -X GET \
https://api.sandbox.ztlpay-test.io/api/payroll/status/c766e7e0-ea15-4b4a-9574-a27c763f6c17 \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN'
// Example response
{
"statusName": "SETTLEMENT_IN_PROGRESS",
"statusReason": "Initiated payment has been accepted for execution",
"timestamp": "2021-11-30T10:54:34.489808485+01:00"
}
payrollId
the unique payroll ID provided in the response when initiating a payroll payment
Possible statuses are:
Status | Description |
---|---|
AWAITING_AUTHORIZATION | Awaiting authorization of funding payment to ZTL's client account |
SETTLEMENT_IN_PROGRESS | Funding payment to ZTL's client account initiated |
SETTLEMENT_COMPLETED | Funding payment to ZTL's client account completed |
CANCELLED | Funding payment to ZTL's client account was cancelled |
SETTLEMENT_NOT_COMPLETED_IN_TIME | Funding payment to ZTL's client account has not been completed in reasonable time for completing payroll payment execution on the requested execution date, the payroll payment will not go through and money will be transferred back to customer |
PAYROLL_PAYMENT_INITIATED | Payout payment has been sent to ZTL's payroll payment provider |
PAYROLL_PAYMENT_ACCEPTED_TECHNICAL_VALIDATION | Payout payment has passed technical validation at ZTL's payroll payment provider |
ACCEPTED_FOR_EXECUTION | Payout payment has been accepted for execution at ZTL's payroll payment provider |
EXECUTION_COMPLETED | Payroll payment has been paid out to the recipients |
FAILED | Payout payment failed at ZTL's payroll processor |
Cancel
It is possible to cancel a payroll payment with statuses AWAITING_AUTHORIZATION, SETTLEMENT_IN_PROGRESS and SETTLEMENT_NOT_COMPLETED_IN_TIME as long as status of the funding payment in the bank allows it.
curl -X POST \
https://api.sandbox.ztlpay-test.io/api/payroll/cancel \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 49ae0cfe-6b72-4310-81f5-ad4eef897fe3' \
-H 'PSU-IP-Address: 153.110.241.229' \
-H 'PSU-Geo-Location: string' \
-H 'PSU-User-Agent: string' \
-H 'Authorization: Bearer ACCESS_TOKEN'
-d '{
"payrollId" : "1887c36b-4be7-4574-8954-9229c9666631",
"instructionId" : "testydsddff345",
"consentReference": "8eerj3e-ds79jdc",
"callbackUrl": "https://www.example.com"
}'
// Example response
{
"statusReason": {
"reason": "Payment cancellation request is initiated. PSU needs to confirm the cancellation",
"status": "AuthorizationRequired",
"origin": "DNB at NAAS"
},
"scaUrl": "https://lldkit7c6f.execute-api.eu-west-1.amazonaws.com/Prod/bankid/logon?blob=eyJ1c2VySWQiOiIzMTEyNTQ2MTExOCIsInJlZGlyZWN0VXJsIjoiaHR0cHM6Ly9zYW5kYm94Lm5hYXMtdGVzdDEubmV0cy5ldS9kbmJhbm9ray9jYW5waXMvYWNjZXB0L2MwNGRlNTAyNjIzZDRlNmY5ZThiNjMyMjc4MzQyODU5IiwibW9kZSI6IlByb2QiLCJjb25zZW50SWQiOiJlMTdkYmMzMy1mNDM5LTQ0NmYtOWY3YS1jYTc0ZjcyNjExYzUiLCJhY2Nlc3NVcmwiOiJhcHBzL25icC9iZXRhbGluZyIsInBzZDJFbmRwb2ludCI6Imh0dHBzOi8vYXBpLmludGVybmFsMDEuc2FuZGJveC5hcHBzaGFyZWRzdmMudGVjaC0wMy5uZXQvdjEvbmV0YmFuay9wcm94eS9wYXltZW50L3NjYS9lMTdkYmMzMy1mNDM5LTQ0NmYtOWY3YS1jYTc0ZjcyNjExYzUiLCJjb25zZW50RW5kcG9pbnQiOiJodHRwczovL2FwaS5pbnRlcm5hbDAxLnNhbmRib3guYXBwc2hhcmVkc3ZjLnRlY2gtMDMubmV0L3YxL25ldGJhbmsvcHJveHkvYXV0aG9yaXNhdGlvbi9lMTdkYmMzMy1mNDM5LTQ0NmYtOWY3YS1jYTc0ZjcyNjExYzUiLCJwYXltZW50SWQiOiJTU0xfRE9NLTQ5MTQzMjk2MyIsInRwcCI6IlBTRE5PLVRTVE5DQS03ZmEwNjZiMXxOZXRzIFRQUCIsInRpbWVzdGFtcCI6IjE2MDM3ODQyNTYyMTcifQ==&redirectUrl=https%3A%2F%2Fsandbox.naas-test1.nets.eu%2Fdnbanokk%2Fcanpis%2Fdeny%2Fc04de502623d4e6f9e8b632278342859&la"
}
Breaking Changes
Upcoming Changes
There are no planned breaking changes :)
Although we have no major breaking changes planned, we're continuously improving our systems. If you have any feature requests, no matter the size, feel free to contact us!
Previous changes
2022-05-06 - Enforce whitelisting of banks
Starting 2022-05-06, only banks retrieved from the supported banks-service may be used with our services.
Previously, there has been a separation between those banks it has been possible to use our services with, and those banks that we have rigorously tested and confirmed to work as expected with our services. Allowing our services to be used with banks that we have not verified, has caused confusion and unwanted behavior. We will therefore, starting 2022-05-06, block requests towards banks that we do not explicitly support.
If there are any banks that you currently use our services with, that are not in the supported banks-service, we ask that you let us know what bank and which services.
2022-05-02 - Mandatory PSU-IP-ADDRESS for creating consent
Starting 2022-05-02, the header field PSU-IP-Address
will be mandatory when creating a new
consent-token.
This change will enable a more seamless experience when creating consents across a variety of banks, as some banks will not allow consent creation without the header field. This change will not affect any existing consents.
To prepare for this change, users of the API may begin providing the header field in all requests for creating consent at earliest convenience.
2022-03-08 - Validate length of unstructured remittance info
Starting 2022-03-08, the length of the PAIN-field CdtTrfTxInf.RmtInf.Ustrd
will be validated based on the bank-specific
field constraints.maxUnstructuredRemittanceInfoLength
, which can be retrieved from the
supported banks-service.