Skip to main content

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:/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:/v1/merchants.

When a Merchant is no longer using the solution, it must be offboarded using the Delete merchant endpoint DELETE /merchants/.

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.

Merchant sequence diagram

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 payment currencyCode 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.

  1. In order to create a payment, you need to invoke the "create payment" endpoint (POST:/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.

  2. 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.

  3. 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.

    info

    If 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.

  4. 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:

    • POST payments/{paymentId}/captures
    • POST payments/{paymentId}/cancels
    • POST payments/{paymentId}/refunds

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.

On InitiatePayment, 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.  

NOTE

All merchants should have their own logo to give the best user experience. Only use this as a temporary solution!

Diagrams

Payment - Happy day

Payment sequence diagram

Native app switching, Payment - Happy day

Payment with native app switching diagram

After authorization sequence diagram

When acquirer or issuer rejects a payment

Acquirer or issuer reject payment sequence diagram

When the user rejects a payment

User rejects payment sequence diagram

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.

Note

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.

Token

Delegated Authentication for Visa card

Example of Visa Token Service (VTS) response:

VisaTokenCallbackPayload
{
"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:

MastercardTokenCallbackPayload
{
"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.

3DS fallback

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:

EncryptedCardUpdateCallbackPayload
{
"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.

Failed payment callback example
{
"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"
}
CodeEndpoint(s)Description
2000POST:/paymentsMerchant doesn't exist
2010POST:/paymentsThe merchant isn't created by you
2020POST:/paymentsThe merchant is deleted
2030POST:/paymentsAllowed card types are not set
2040POST:/paymentsOne or more of the allowed card types are invalid
2050POST:/paymentsCurrency code is invalid
2100PUT payments/{paymentId:guid}/invalidateCan't invalidate payment with completed authorization attempt
2101PUT payments/{paymentId:guid}/invalidateCan't invalidate payment - the authorization attempt has not yet been patched with success or failure. Try again later.

Allowed currencies

Currencies must be specified according to the ISO-4217 standard, either by using the alphabetic or numeric version. Allowed currencies are:

NameAlphabetic codeNumeric code
Danish kronerDKK208
EuroEUR978
Norwegian kronerNOK578
Swedish kronerSEK752
United States dollarUSD840
Pound sterling GBP 826

Allowed card types

The following card types are allowed:

Card nameCode
Visa electronELEC-DEBIT
Mastercard creditMC-CREDIT
Mastercard debitMC-DEBIT
Visa creditVISA-CREDIT
Visa debitVISA-DEBIT
DankortDANKORT

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.

Online flow

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:

  1. Functional cookie in browser
  2. customerLanguageCode on payment
  3. countryCode 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:

  1. Complete a single device flow > The MobilePay app automatically opens redirectFromMobilePayURL in the browser.
  2. 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 CodeDescription
0completed
1rejected
3expired
4cancelled

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 (POST:/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.

Help us improve our documentation

Did you find what you were looking for?