MobilePay PSP API guide
Merchants
As a PSP, you need to create the merchants in MobilePay, in order to create payments on their behalf.
This can be done by invoking the Create merchant endpoint: POST:/api/v1/merchants/
.
The details supplied in the merchant onboarding will be used for invoicing purposes and ongoing support. VAT number is required for all merchants. For new merchants, the vatNumber
field is required for the Create merchant endpoint: POST:/api/v1/merchants
.
When a Merchant is no longer using the solution, it must be offboarded using the Delete merchant endpoint: DELETE:/api/v1/merchants/{merchantId}
.
All merchants must be onboarded as individual merchants. If you have super merchants or payment facilitators, you can also use them for MobilePay. However, each submerchant must be onboarded, instead of the super merchant and payment facilitator.
Currency and country code
When creating a merchant, billingCurrency
and countryCode
are defined:
countryCode
: The country of the merchant. If a merchant operates in two countries, you must create one merchant for each country and set the appropriate country code.billingCurrency
: Currency used for invoicing between MobilePay and PSP. This doesn't affect the currency of the payments between merchants and MobilePay users. When initiating a paymentcurrencyCode
is defined.
Franchise and partners
For franchises, you must create each franchisor as individual merchants. You can also choose to set up the franchisee as partner if you want to, but that is not required from our side. Please contact developer@vippsmobilepay.com to inquire about partner setup.
Payments
When you have created a merchant, you can start initiating payments.
-
In order to create a payment, you need to invoke the Initiate payment endpoint:
POST:/api/v3/payments/
. To use this, you need to provide information about the merchant, the payment, the public key used for encrypting the data, callback URL, and redirection URL. This will return a URL that the end user should be redirected to. -
When the user has accepted the payment in the MobilePay app, you'll receive a callback on the URL defined in the previous step. This will contain the card data with which you can create the authorization.
-
When you have successfully authorized the payment (or it has failed), you must patch the
authorisationAttempt
. Then, we'll show a receipt (or error message) to the user. Don't expect the user to return client side in the UI.infoIf the authorization fails, we will allow the user to accept the payment again (encouraging them to use another card). This will result in a new callback to your callback URL with a new
authorizationAttempt
. -
When the merchant captures, refunds, or cancels the payment, the status of the payment must be updated to reflect this. The updates are used for invoicing purposes. To update the payment use these:
- Capture payment endpoint:
POST:/api/v1/payments/{paymentId}/captures
- Cancel payment endpoint:
POST:/api/v1/payments/{paymentId}/cancels
- Refund payment endpoint:
POST:/api/v1/payments/{paymentId}/refunds
- Capture payment endpoint:
Restrictions
A payment will time out within 10 minutes, by default. Thus, the entire process of user accepting the payment, making callback, and authorization must be completed within 10 minutes.
Upon receiving the callback with the card data, you have 20 seconds to update the authorization status to either authorize-successful
or authorize-failed
.
If this status is not updated within 20 seconds, the payment will be marked as failed, making it impossible to capture. Additionally, this may result in inconsistencies between what the user sees in their MobilePay app and what the merchant sees, potentially causing situations where the customer retries the order and the merchant processes and ships it as separate orders.
Merchant logo
On Initiate payment, you must define a merchantLogoUrl
.
If the merchant doesn't have a logo, please use following URL: https://no-logo.png
This results in standard merchant logo to be displayed in the MobilePay app.
All merchants should have their own logo to give the best user experience. Only use this as a temporary solution!
Diagrams
Payment - Happy day
Native app switching, Payment - Happy day
When acquirer or issuer rejects a payment
When the user rejects a payment
Strong Customer Authentication
We aim to ensure Delegated Authentication (DA). This means that responsibility for authenticating the customer/payer no longer lies with the Issuer, but is delegated to MobilePay. When/if we fail, and the Issuer is responding to an authorization attempt with a Soft Decline/"step-up", a 3-D Secure fallback solution must be in place.
All the SCA implementations are mandatory. Only Dankort can be skipped, if you do not accept Dankort.
Delegated Authentication can only be effectuated by the Issuer if the authorization is based on a token. If you for some reason receive a PAN-based transaction from us, you should handle data in the card data callback as non-authenticated PAN-transactions according to scheme rules to ensure correct handling regarding SCA. If in doubt, please contact your acquirer.
Delegated authentication for Dankort
As long as you use the tags and values described here, all is well. Nets will recognize MobilePay and trust our authentication process.
Use POS code: K005K0K00130
.
Using Nets SDI specification
- In Field S120 tag 36: the value of 8844101001
- In Field S120 tag 70 pos 14 (exemption Tag): the value of 3 for Delegated Authentication
Using Nets TRG PSIP/ ISO 8583 / Merchant Guide SSL
- In Field 47 tag 7R: the value of 8844101001
- In Field 47 tag V!: the value of 23
Delegated authentication using tokens
Both Visa Token Service (VTS) and Mastercard S4C (MS4C) are based on the EMVCo standard. Please pass the data to the Acquirer, as you would do if the token response was from your own VTS or MS4C integration. In case you have questions to the Acquirer API, please ask the Acquirer.
When you initiate a payment, make sure to use version 3 of the API. Here, you give a tokenCallbackUrl
for all accepted Visa and Mastercard types. However, please also provide a carddataCallbackUrl
as failover, because not all cards can be tokenized. For Dynamic Linking, please give us MerchantUrl
and MerchantName
.
When you receive the token callback, you'll find a cardIssuedInCountryCode
(possible values DK
, FI
). You can use this for your Acquirer routing logic.
Exactly as for encrypted card data callbacks, make sure you respond to the callback immediately. DO NOT leave the transaction "hanging" while you call out to the Acquirer. If we do not get a response to the callback in proper time we will allow the user to accept the payment again.
Delegated Authentication for Visa card
Example of Visa Token Service (VTS) response:
{
"paymentId":"8dab9219-ab03-4524-bae7-f0ad55119da5",
"authorizationAttemptId":"32eedb2b-a536-4eb6-b618-c2d6c1bf7aab",
"cardType":"VISA-CREDIT",
"cardIssuedInCountryCode":"DK",
"maskedCardNumber":"479694XXXXXX1234",
"tokenMethod":"VTS",
"tokenData":{
"vPaymentDataID":"da17bd1568bdc8b418d71cf80c44ea02",
"cryptogramInfo":{
"cryptogram":"/wAAAAwAUkMTObMAAAAAgS0AAAA=",
"eci":"07"
},
"paymentInstrument":{
"last4":"1234",
"paymentType":{
"cardBrand":"VISA"
},
"paymentAccountReference":"V0010013020217426481676671969"
},
"tokenInfo":{
"token":"4895737462013403",
"last4":"3403",
"expirationDate":{
"month":"02",
"year":"2023"
}
}
},
"isDelegatedAuthentication": false
}
Delegated Authentication for Mastercard
Example of Mastercard S4C (MS4C) response:
{
"paymentId":"1ba21790-5e10-4db1-8e90-330fb41916e7",
"authorizationAttemptId":"3205ec7c-2d50-49d2-95dc-326e34edce47",
"cardType":"MC-CREDIT",
"cardIssuedInCountryCode":"DK",
"maskedCardNumber":"520473XXXXXX4792",
"tokenMethod":"MC S4C",
"tokenData":{
"token":{
"paymentToken":"5204731613942625",
"tokenExpirationMonth":"05",
"tokenExpirationYear":"2024",
"paymentAccountReference":"5001BO8B9NXVVIXCT0HAJU98I512Z"
},
"dynamicData":{
"dynamicDataType":"CARD_APPLICATION_CRYPTOGRAM_SHORT_FORM",
"dynamicDataValue":"MD1eEaqbngDNAy0iuRqOAAADFEA="
},
"eci":"06"
}
}
3DSecure Fallback
If Delegated Authentication fails, the 3DSecure fallback solution applies.
When the user has completed the challenge, please immediately redirect to https://products.mobilepay.dk/remote-website/apppages/done3ds.html
For Sandbox use: https://sandprod-products.mobilepay.dk/remote-website/apppages/done3ds.html
If the user cancels the 3DS challenge on the 3DS website or if it fails in some way, you should fail the authorization attempt with reasonCode=1000
and redirect to the done3ds.html
page. This will cancel the 3DS flow in the MobilePay app and allow the user to retry with another card (starting a new authorization attempt).
Optional: As soon as you have 3DS crypto from ACS and before retrying the authorization-attempt towards Acquirer and Issuer, you can call the MP backend with reasonCode=1009
. This enables us to prevent the user from retrying with another card.
Callbacks
As a rule of thumb, MobilePay Online is idempotent in all operations. Likewise, we expect PSPs to be able to handle the same callback more than once in the event of transient errors on network, ours, or your side. This means that if we make a callback to you on a given payment ID or a given authorization attempt you may receive the same data more than once and should ensure that your systems are able to handle that. We will retry our callbacks for 15 seconds in the event of network errors or non-200-range HTTP status codes in your responses. After this we will mark the callback as failed and allow the user to accept the payment again. In this case you will receive a new callback with a new authorization attempt. Please note that the 15-second limit may be subject to change. If you need to whitelist our servers, find details on the servers page.
Card data callback
A callback will be made on the encryptedPanCallbackUrl
when the user swipes to accept the payment. The callback will have a JSON body like this:
{
"EncryptedCardData": "fsfnsdjkfbgdft34895u7345",
"PaymentId": "a84781b3-af34-42ae-b296-260cfb6859fe",
"AuthorizationAttemptId": "ba12c5d5-8fd1-49cc-bc3f-2cb2ecb888c7",
"PublicKeyId": 263012,
"CardType" : "DANKORT"
}
The EncryptedCardData
is encrypted according to this OAEP algorithm and padding scheme: RSA/NONE/OAEPWithSHA256AndMGF1Padding
(note that SHA-256 hash is also used for padding).
Once decrypted, you'll see:
{"timestampticks":123456789123456789,"encryptedCardData": { "cardNumber": 1234567812345678, "expiryMonth": 12, "expiryYear": 28 }}
Make sure you respond to the callback immediately once you've decrypted the content. DO NOT leave the transaction "hanging" while you call out to the Acquirer.
Strictly process the payment on the card type given in field "CardType"! This is the only way MobilePay can offer card type picking for co-branded cards in accordance with PSD2 requirements.
Also, the SCA method vary on the co-branded Visa/Dankort. This means that if you process the PAN given with CardType=DANKORT
as a Visa the SCA is missing and the (unwanted) 3DS step-up is likely to happen.
Note: When receiving a PAN-based transaction from us, it can't be regarded as authenticated. You should handle these according to scheme rules to ensure correct handling regarding SCA. If in doubt please contact your acquirer.
Failed payment callback
In case the payment times out, a callback is made to the FailedPaymentCallbackUrl
supplied in the initiate payment call. Note that this callback is not sent if the user rejects the payment, only when payment times out after 10 minutes of no user interaction. This means that it will not be sent if user have inserted their phone number on the landing page, but doesn't accept or reject the payment. The payment will still expire after 10 minutes, but the callback will not be sent.
{
"Code": "100",
"Reason": "Payment expired",
"PaymentId": "8d72ece4-1b0b-464b-98d9-6bbb02199dc8"
}
Endpoint security
To ensure no unauthorized calls to your callback endpoints we strongly suggest you apply SSL "Common name" inspection. The "Common name" in our SSL client certificate should never change. Even when the certificate itself changes or is issued to a different root.
Error codes
The following will describe the error codes thrown and in which cases they can occur.
The error format for status code 409 will be the following:
{
"code": "2020",
"message": "Some description",
"correlationId": "8d72ece4-1b0b-464b-98d9-6bbb02199dc8"
}
Code | Endpoint(s) | Description |
---|---|---|
2000 | Get merchant POST:/api/v1/merchants/{merchantId} | Merchant doesn't exist |
2010 | Get merchant POST:/api/v1/merchants/{merchantId} | The merchant isn't created by you |
2020 | Get merchant POST:/api/v1/merchants/{merchantId} | The merchant is deleted or disabled |
2030 | Initiate payment POST:/api/v3/payments | Allowed card types are not set |
2040 | Initiate payment POST:/api/v3/payments | One or more of the allowed card types are invalid |
2050 | Initiate payment POST:/api/v3/payments | Currency code is invalid |
2060 | Initiate payment POST:/api/v3/payments | Customer phone number must be valid Danish or Finnish number |
2070 | Initiate payment POST:/api/v3/payments | minimumUserAge must be between 1 and 150 |
Allowed currencies
Currencies must be specified according to the ISO-4217 standard, either by using the alphabetic or numeric version. Allowed currencies are:
Name | Alphabetic code | Numeric code |
---|---|---|
Danish kroner | DKK | 208 |
Euro | EUR | 978 |
Norwegian kroner | NOK | 578 |
Swedish kroner | SEK | 752 |
United States dollar | USD | 840 |
Pound sterling | GBP | 826 |
Allowed card types
The following card types are allowed:
Card name | Code |
---|---|
Visa electron | ELEC-DEBIT |
Mastercard credit | MC-CREDIT |
Mastercard debit | MC-DEBIT |
Visa credit | VISA-CREDIT |
Visa debit | VISA-DEBIT |
Dankort | DANKORT |
Dual device and single device flow
MobilePay Online supports both dual device and single device flows.
Redirecting to redirectToMobilePayUrl
depends on the user's device type.
The landing page will detect if the redirect happens on a mobile device or browser.
Only the user experience will be affected by type of flow. The payment flow and API requests are the same regardless of flow type.
Single device flow
If the redirect happens on a mobile device, the MobilePay app will be launched.
The user can then accept or reject the payment, and we will redirect the user to redirectFromMobilePayUrl
afterwards.
This URL will always open in the default browser (determined by the operating system) of the mobile device.
- If the MobilePay app is not installed and therefore can't be opened, our landing page will open in the browser instead, and the flow will continue as a dual device flow.
- It is also possible to open the app directly instead of using the universal link.
To do this you will need to use the
redirectToMobilePayAppUrl
given in the response from initiating a payment. This can be useful when using frames. See Manually engaging the App for a guide on this.
Dual device flow
If the redirect happens on a desktop device, our landing page will open in the browser.
The user can then insert the phone number.
We will then send a push notification to the user's mobile device and the user can then accept or reject the payment in the app.
Afterwards, the landing page in the desktop browser will redirect to redirectFromMobilePayUrl
.
Landing page
The link to the landing page is received in response to payment initiation as: redirectToMobilePayUrl
.
On the landing page, the user must insert their phone number and click continue.
A timer will then be shown to indicate how long time the user have to accept the payment in the MobilePay app.
The language of the page is initially defined by the merchant's countryCode
(i.e., the merchant that the payment is initiated on behalf of).
The merchant countryCode
is defined when creating a merchant with POST:/api/v1/merchants
.
This setting can be overruled by setting the customerLanguageCode
on payment initiation.
The code can be either DK
or FI
. If DK
, the language on landing page will be Danish. If FI
the language on landing page will be Finnish.
The first time a user visits the landing page, a functional cookie will be saved.
This cookie defines the language of the page.
Upon the next time the user visits the landing page, the language will be determined by a cookie.
This is regardless of the setting for customerLanguageCode
or countryCode
.
Hierarchy:
- Functional cookie in browser
customerLanguageCode
on paymentcountryCode
on merchant
Prefill phone number on landing page
To ease the user flow you can prefill the phone number on the landing page. When the payment is initiated using POST:/api/v3/payments
you set customerPhoneNumber
to prefill the phone number. It must be provided without whitespace and with either Danish (45) or Finnish (358) prefix.
Redirect to redirectFromMobilePayurl
The user is redirected to redirectFromMobilePayUrl
when a payment is either completed or rejected.
This means the user is not redirected immediately after they swipe in the MobilePay app, but when you either patch the authorization or the user rejects the payment.
In single device flow, the app switches to the default browser and opens the redirectFromMobilePayUrl
.
If an app link is supplied, the OS will open the app instead.
For dual device flow, the browser will redirect to redirectFromMobilePayUrl
.
In the following scenario, you will always see two redirects by design:
- Complete a single device flow > The MobilePay app automatically opens
redirectFromMobilePayURL
in the browser. - Now, access the browser tab with the landing page that opened the app in step 1. > The tab will redirect to
redirectFromMobilePayURL
.
Embedded Flow (IFrame)
Web shops and Payment Service Providers (PSPs) may embed the MobilePay landing page inside their own website by nesting the page in an IFrame and then listen to the events emitted from the IFrame.
Embedding is especially relevant for 'dual-flow' use; where the user starts by entering his or her phone number in a browser and then continues the flow in the app to accept the payment.
When the user has accepted the payment (swiped) in the app, the landing page will by default redirect to the URL provided when the payment was created.
Build your logic on the parent page, listen for the events published by the IFrame and redirect the user to the right page based on the returned data (e.g. depending on whether the user cancelled or completed the payment).
Embed the website in an IFrame
Add an "IFrame" to the HTML source and set the IFrame "src" property to the URL returned from the payment link creation endpoint.
On mobile devices, it is expected that the MobilePay flow visually covers the whole screen (simple header and footer is acceptable).
Be aware that you might want to show the user different content depending on whether the user is inside an IFrame or not. If you support both IFrame and full window, we recommend that you have a neutral return page without visual content.
The width should be 375px.
Example
<IFrame
scrolling="no"
src="https://products.mobilepay.dk/remote-website/index.html?page=request&id=83554a83-cd90-4ac9-bf6e-39357c21dca5&version=2"
style="width: 375px; height: 480px; border: 0;" >
</IFrame>
Add an Event Listener to the parent page of the IFrame
The parent page can listen for events by adding an event listener to the IFrame.
Before the IFrame redirects, it will post a message to all registered event handlers. This gives any parent page the alternative option of handling the page navigation instead. For example, in the case where you want the parent page to change also.
window.addEventListener(
"message",
function(event) {
if (event.data && event.data.startsWith("mobilepay:")){
// Do your logic
// Continue purchase processing
alert(event.data);
}
},
false);
When the user terminates the payment flow, the published event will look like as follows. The code and matching description indicate how the flow was terminated (i.e., either because the payment was completed, rejected, cancelled by the user or because the timer expired).
mobilepay:rc={code}&message={description}
// example
mobilepay:rc=0&message=completed
Response Code | Description |
---|---|
0 | completed |
1 | rejected |
3 | expired |
4 | cancelled |
Manually engaging the App from the parent page
On mobile devices the app is not guaranteed to engage when the website is nested inside an IFrame.
To preserve the expected behavior of engaging the app on mobile devices, the
parent of the IFrame may try to engage the app with the following JavaScript
snippet that uses a custom URL registered for both Android and iOS.
This custom URL (redirectToMobilePayAppUrl
) is part of the response received, when a payment is initiated.
You should still display the landing page inside the IFrame, in case the app is not installed or the user navigates back to your page.
function tryToOpenMobilePayApp(paymentId) {
try {
window.open("_redirectToMobilePayAppUrl", "_parent");
} catch {
// window.open should just fail without throwing aside from logging an
// error to console.
//
// This catch is strictly for good measure to avoid any incidents
// in legacy or future browser settings.
}
}
Retry policy
Even though we have above 99% uptime and handle millions of transactions each week, external factors, such as network related issues, can contribute to momentary disturbances in our response times. To reduce the risk of such issues having negative impact on the customer experience, you should implement a retry mechanism.
Our suggestions:
- If you don't get a response within 2 seconds, try again.
- If you get an unexpected error (HTTP > 299) wait 1 second, then try again.
- Suggested retry count is 2 (three attempts in total). You should under no circumstance retry more than 6 times as this could worsen any ongoing situation in our setup.
You're welcome to make use of more advanced retry strategies (e.g., exponential back-off, circuit breakers), as long as you take the suggestions above into consideration.
Request Fishing Scenario
This scenario is a thought out "attack" where a fraudster tricks someone else to pay for the goods, by sending the request to multiple users from our "dual device" website, until someone accepts the payment.
Initialize payment (/api/v3/payment/
) is idempotent.
However, if it is called with the same set of MerchantId
and PSPReferenceId
values
(but anything else might have changed), request fishing will be initiated.
Depending on the scenario, a DomainError
will be returned stating the problem.
If the user initiates more than 3 payments with the same MerchantId
and OrderId
,
a permanent DomainError
will be returned.