MobilePay Subscriptions

OpenID Connect Flow

Overview

General Notes

Agreements

Subscription Payments

One-Off Payments

Refunds

Invoices

Mutual SSL Flow

Overview

General Notes

Agreements

Subscription Payments

One-Off Payments

Refunds

Release Notes

Subscriptions Integrator 1.1

Overview - MobilePay Subscriptions [A deprecated API]

Billing your customers has never been easier before. This document explains how to make a technical integration to the MobilePay Subscriptions product. The audience for this document is either technical integrators acting on behalf of merchants, or merchant creating their own integrations. You can find more information on our Developer Portal.

Related links:

Deprecated

This API is deprecated , so we recommend you use the Subscriptions API that uses OAuth 2.0 protocol for authentication and authorization and contains endpoints needed to implement MobilePay Subscriptions in your system. As an existing subscriptions merchant, you can already now migrate from SSL to OpenID Connect. The SSL API version is scheduled for complete retirement, once all merchants have migrated.

The technical reason for deprecation the SSL is that OpenID Connect supports our increasing focus on ensuring high security. We are not going to release new functionality or integrate new merchants using SSL.

The deadline of migrating to OpenID Connect is 01-01-2020.

Please contact us at developer@mobilepay.dk if you have any questions or need technical assistance.

General notes

MobilePay Subscriptions is a full-fledged HTTPS REST api using JSON as request/response communication media.

We provide examples of API calls in this document. In these examples the host is defined as <mobile-pay-root>. Depending on the environment, our host is as shown in the table below:

Environment Host
Production https://api.mobilepay.dk
Sandbox https://api.sandbox.mobilepay.dk

All dates and time-stamps use the ISO 8601 format: date format - YYYY-MM-DD, date-time format - YYYY-MM-DDTHH:mm:ssZ.

Amounts are enquoted with double quotation marks using 0.00 format, decimals separated with a dot.

When doing POST, PATCH or PUT requests, Content-Type: application/json HTTP header must be provided.

$ curl --request POST --header 'Content-Type: application/json' --url https://<mobile-pay-root>/resource --data '{}'

API version is set using the api-version query parameter.

$ curl --url https://<mobile-pay-root>/resource?api-version=1.1 --request POST --header 'Content-Type: application/json' --data '{}'

Authentication

All requests to the API must contain a client certificate and at least two authentication headers - x-ibm-client-id and x-ibm-client-secret in order to authenticate to the API.

$ curl --cert /path/to/cert.pfx --header 'x-ibm-client-id: client-id' --header 'x-ibm-client-secret: client-secret' --url https://<mobile-pay-root>/api/merchants/me/resource
Client certificate for mutual SSL

In order to be authenticated to our REST services you have to provide a self-signed client certificate, which can be generated either using makecert.exe or OpenSSL. Note, that the certificate is valid for 2 years and will have to be regenerated after it expires.

Generate two certificates for Sandbox and Production environments:

  • Sandbox: set environment to Sandbox.
  • Production: leave environment blank.

Send the generated *.cer (or *.crt, if you use OpenSSL) files to developer@mobilepay.dk and store the *.pfx file in a secure private key storage on your end. Note: Please zip the certificate, as our e-mail server is quite sensitive.

Using makecert.exe to generate client certificate
makecert.exe ^
 -n "CN=your-company-name - MobilePay - environment" ^
 -sky exchange ^
 -eku 1.3.6.1.5.5.7.3.2 ^
 -r ^
 -pe ^
 -a sha512 ^
 -len 2048 ^
 -m 24 ^
 -sv environment_MobilePay_your-company-name.pvk ^
 environment_MobilePay_your-company-name.cer

Export private key to pfx:

pvk2pfx.exe ^ 
 -pvk environment_MobilePay_your-company-name.pvk ^
 -spc environment_MobilePay_your-company-name.cer ^
 -pfx environment_MobilePay_your-company-name.pfx
Using OpenSSL to generate client certificate
$ openssl req -x509 -nodes -sha512 -newkey rsa:2048 -keyout environment_MobilePay_your-company-name.pvk -out environment_MobilePay_your-company-name.crt -days 730

Enter your-company-name - MobilePay - environment for Common Name, when asked.

Export private key to pfx:

$ openssl pkcs12 -export -in environment_MobilePay_your-company-name.crt -inkey environment_MobilePay_your-company-name.pvk -CSP "Microsoft Enhanced RSA and AES Cryptographic Provider" -out environment_MobilePay_your-company-name.pfx

Errors

You might encounter the following HTTP errors:

  1. 400 - Bad Request , if request data is invalid.
     {
         "error": "BadRequest",
         "error_description": {
             "message": "request.Name is required",
             "error_type": "InputError",
             "correlation_id": "f4b02597-32cc-420f-a468-942307e89a97"
         }
     }
    
  2. 404 - Not Found with no response body, if the resource (agreement or payment) is not found.

  3. 412 - Precondition Failed , if business validation rule was violated.
     {
         "error": "PreconditionFailed",
         "error_description": {
             "message": "Duplicate payment.",
             "error_type": "PreconditionError",
             "correlation_id": "f4b02597-32cc-420f-a468-942307e89a97"
         }
     }
    
  4. 500 - Internal Server Error , if something really bad has happened.
     {
         "error": "InternalServerError",
         "error_description": {
             "message": "An error occurred, please try again or contact the administrator.",
             "error_type": "ServerError",
             "correlation_id": "f4b02597-32cc-420f-a468-942307e89a97"
         }
     }
    

REST request correlation

CorrelationId is an optional Guid header value which can be used to link requests on your back-end system to MobilePay Subscriptions business transaction for a more convenient debugging.

$ curl --header 'CorrelationId: 37b8450b-579b-489d-8698-c7800c65934c' --url https://<mobile-pay-root>/api/merchants/me/agreements

REST callback authentication

Use one of these endpoints to set REST callback authentication scheme and credentials:

REST callback retries

In case the REST callback failed, 8 retries will be made using the exponential back-off algorithm, where N - next retry time, c - retry attempt number, R - second retry time in seconds (1st retry is an exception and is done after 5 seconds):


Agreements

Once the user is given to choose the payment method on the merchant’s signup flow, an additional “Pay with MobilePay” button should be shown for the user to be able to click on. When user clicks on this button, merchant’s back-end system must call the POST /api/merchants/me/agreements endpoint in order to create a Pending Subscription Agreement, which can only be activated by the MobilePay user through the app.

{
  "external_id": "AGGR00068",
  "amount": "10",
  "currency": "DKK",
  "description": "Monthly subscription",
  "next_payment_date": "2017-03-09",
  "frequency": 12,
  "links": [
    {
      "rel": "user-redirect",
      "href": "https://example.com/1b08e244-4aea-4988-99d6-1bd22c6a5b2c"
    },
    {
      "rel": "success-callback",
      "href": "https://example.com/1b08e244-4aea-4988-99d6-1bd22c6a5b2c"
    },
    {
      "rel": "cancel-callback",
      "href": "https://example.com/1b08e244-4aea-4988-99d6-1bd22c6a5b2c"
    }
  ],
  "country_code": "DK",
  "plan": "Basic",
  "expiration_timeout_minutes": 5,
  "mobile_phone_number": "4511100118"
}

The Pending Agreement, if not activated, will expire within the value, provided in the expiration_timeout_minutes.

Request parameters

Parameter Type Required Description Valid values
amount number(0.00)   Agreement amount, which will be displayed for the user in the MobilePay app. >= 0.00, decimals separated with a dot.
currency string(3) required The Agreement currency code, that will be displayed for the use in the MobilePay app. Currency and country_code must match a valid pair of: DKK->DK, EUR->FI. DKK, EUR
country_code string(2) required Country code, which will be used to differentiate between MobilePay DK and FI apps. DK, FI
plan string(30) required Short Agreement information text, that will be displayed on the Agreement screen. (examples: “Basic” / “Premium”).  
description string(60)   Additional information provided by the merchant to the user, that will be displayed on the Agreement screen.  
next_payment_date date   The date of the first scheduled Payment Request. This will be displayed on the Agreement creation screen and on the Agreement details screen if first payment date > current date. ISO date format: yyyy-MM-dd
frequency int   Frequency of Payment Requests. This value will be used to divide the amount of days in a year to get a frequency in days (e.g. 365 / 12 = 30.4 - approx. every month.). If not provided will default to 12. 1, 2, 4, 12, 26
external_id string   *Agreement identifier on the merchant’s side. This will be included in the request body of the success / cancel callback. The external_id should be unique to the agreement. Two different agreements should not have the same external_id  
expiration_timeout_minutes int required Agreement expiration timeout in minutes. Min: 5, max: 20160 (2 weeks)
links string required Link relation of the Agreement creation sequence. Must contain 3 values for user redirect, success callback and cancel-callback links.  
links[].rel string required Link relation type. user-redirect, success-callback, cancel-callback
links[].href string required Link relation hyperlink reference. https://<merchant’s url>

Detailed info on the non-required parameters:

mobile_phone_number It is not a required parameter, but we recommend you to use it, because then the phone number is pre-filled on our landing page, which means that the user will not have to type the phone number on the page, and therefore more convenient.

frequency It is not a required parameter. frequency provides informational value to the customer, so the customer can see in the app, how many times they can expect to pay Subscription Payment. The frequency does not impact, how often the merchant can send payments. If the merchant knows how often they will be sending Subscription Payments, e.g. monthly, then it is recommended to fill out frequency. If not provided, the frequency will be set to 12.

external_id It is meant as a unique identifier, which shouldn’t change. On MobilePay side, we have the agreement_id, which never changes, regardless of what happens to the agreement. agreement_id is the counterpart to the external_id on your side. It should stay the same, so MobilePay can trace the full history of the agreement. The customer can see the “external_id” in the app. Our recommendation is, that the external_id is their agreement_id or reference with you. Therefore, it is more clear for the user. In addition, if they have more than one Subscriptions agreement with you, they should be able to see which Subscriptions agreement is connected to which agreement.

The response of POST /api/merchants/me/agreements contains two values: a unique id of the newly created Pending Agreement and a link rel = mobile-pay.

{
  "id": "1b08e244-4aea-4988-99d6-1bd22c6a5b2c",
  "links": [
    {
      "rel": "mobile-pay",
      "href": "https://<mobile-pay-landing-page>/?flow=agreement&id=1b08e244-4aea-4988-99d6-1bd22c6a5b2c&redirectUrl=https%3a%2f%2fwww.example.com%2fredirect&countryCode=DK&mobile=4511100118"
    }
  ]
}

The link can be used in two ways:

  1. Redirect the user automatically using the HTTP response 302 or 303. Once the user is redirected, the MobilePay app will be opened to activate the Pending Agreement. In this case, it is recommended to set the expiration_timeout_minutes value to 5 minutes.
  2. E-mail the generated link to the user. Once the user clicks on the link, the MobilePay app will be opened to activate the Pending Agreement. In this case, it is recommended to set the expiration_timeout_minutes to a higher value (10080 - a week, 20160 - two weeks). Note, that the link will be valid only until the user accepts the agreement or a timeout occurs.

Update existing Agreement Request

Use the PATCH /api/merchants/me/agreements/{agreementId} endpoint to change agreement request parameters. Its request must match the rules of RFC 6902 JSON Patch standards.

[
    {
        "value": "10.01",
        "path": "/amount",
        "op": "replace"
    }
]

Agreement creation

New agreement creation in landing page

New agreement creation in APP

Callbacks

When the Agreement’s status changes from Pending we will do a callback to the merchant’s system (see the sequence diagram below).

The table below shows possible status, status_text and status_code values depending on the Agreement status changes.

New Status Condition URL Callback status Callback status_text Callback status_code
Accepted User swiped to accept the Agreement success-callback Accepted   0
Rejected User tapped the Cancel button during the signup cancel-callback Rejected Agreement rejected by user 40000
Expired User did not do anything during the agreement timeout period. cancel-callback Expired Pending agreement expired 40001
Canceled User canceled an Active agreement cancel-callback Canceled Agreement canceled by user 40002
Canceled Merchant canceled an Active agreement cancel-callback Canceled Agreement canceled by merchant 40003
Canceled System canceled an Active agreement because user was Deleted cancel-callback Canceled Agreement canceled by system 40004

User can’t cancel agreement if Reserved payment exists.

Agreement state diagram

Other callback properties
Name Type Description Format
agreement_id guid Subscription agreement ID on the MobilePay side.  
external_id string Agreement ID on the merchant’s side  
timestamp datetime Timestamp when the status change occurred. ISO 8601 UTC date and time format: YYYY-MM-DDThh:mm:ssZ
Agreement callback request example
{
    "agreement_id" : "63679ab7-cc49-4f75-80a7-86217fc105ea",
    "status" : "Canceled",
    "status_text" : "Canceled by user",
    "status_code" : "40000",
    "external_id" : "SF0000568",
    "timestamp" : "2016-09-29T09:50:39Z"
}
Agreement callback response properties
Name Description
agreement_id Subscription agreement ID on the MobilePay side.
status_code Status code on merchant’s system
status_text Description of the status.
transaction_id Unique identifier of the transaction on the merchant’s system.

status_code can have the following values:

The callback response properties are optional. In case of technical errors (HTTP response is not 2xx), we will try to re-POST the callback.

Agreement callback response example
{
    "agreement_id" : "63679ab7-cc49-4f75-80a7-86217fc105ea",
    "status_code" : "3000",
    "status_text" : "Server is down",
    "transaction_id" : "63679ab7-cc49-4f75-80a7-86217fc105ea"
}

When the Agreement activation is complete or canceled, the user will be navigated to the link rel = user-redirect to finalize the signup.

Agreement creation sequence diagram

When merchant cancels agreement - sequence diagram

When user cancels a Pending agreement during signup - sequence diagram

When user cancels an Active agreement - sequence diagram


Subscription Payments

When the Agreement between Merchant and MobilePay User is established, use the POST /api/merchants/me/paymentrequests endpoint to en-queue Subscription Payments. This service accepts a JSON array of individual Subscription Payments to be processed asynchronously. You can batch payment requests into payloads of maximum 2000 payments. We allow merchants to bundle multiple payment requests into a single HTTP request. Notice that the Subscription Payments payload does not contain a currency code - this will be fetched from the Agreement using the provided agreement_id.

[
    {
        "agreement_id": "fda31b3c-794e-4148-ac00-77b957a7d47f",
        "amount": "10.99",
        "due_date": "2017-03-09",
        "next_payment_date": "2017-04-09",
        "external_id": "PMT000023",
        "description": "Monthly payment"
    }
]

Request parameters

Parameter Type Required Description Valid values
agreement_id guid required The Subscription Agreement identifier that maps a Merchant to a MobilePay User.  
amount number(0.00) required The requested amount to be paid. > 0.00, decimals separated with a dot.
due_date date required Payment due date. Must be at least 8 days in the future, otherwise the Subscription Payment will be declined. ISO date format: yyyy-MM-dd
next_payment_date date   Next Subscription Payment’s due date, to be shown to the user in the Agreement details. ISO date format: yyyy-MM-dd
external_id string required The identifier of a specific payment in the external merchant’s system. Maximum length is 30 characters  
description string(60) required Additional information of the Subscription Payment.  

The POST /api/merchants/me/paymentrequests service returns HTTP 202 - Accepted response if at least one payment is provided in the request payload.

The response body containts two lists:

HTTP 202 Response body example
{
    "pending_payments": [{
            "payment_id": "263cfe92-9f8e-4829-8b96-14a5e53c9041",
            "external_id": "PMT000023"
        }
    ],
    "rejected_payments": [{
            "external_id": "PMT000023",
            "error_description": "The Amount field is required."
        }
    ]
}

Frequency of Payment Requests

The merchant can send a payment max 32 days prior due date, and at at least 8 days before due date. Valid values are 1, 2, 4, 12, 26. This means that the bi-weekly payment (26) is the most frequent. When you are requesting a payment, you need to keep the 8 day rule. The user can have a single pending payment on due date. E.g. User can have 3 pending payments but the DueDate of those payments should be different.

##### Example of Frequency For example: if you have a customer where the frequency of an agreement is set to 4, that means 365 / 4 = 91.25 (approximately payment requests every 3rd month).

Payment screens

Callbacks

Once the payment status changes from Pending to Executed, Declined, Rejected or Failed, a callback will be done to the callback address, which is configurable via PATCH /api/merchants/me with path value /payment_status_callback_url.

We are sending callbacks in two ways:

  1. A batch that runs every 2 mins. It contains Subscription payment(s) with statuses: Declined/Rejected/Failed/Executed/OneOff_Expired. So in theory, there is a possible delay of 2 mins.
  2. Right after the user made an action. It contains OneOff_Reserved/OneOff_Rejected.

Every two minutes we take up to 1000 events (notifications about payment state), group them by merchant and make the calls. Therefore, as for merchant you should get up to 1 call every two minutes.

We will post the integrator or merchant a callback, and expect a HTTP 2xx response. If not we will retry 8 times.

[
    {
        "value": "https://example.com",
        "path": "/payment_status_callback_url",
        "op": "replace"
    }
]
New Status Condition When to expect Callback status Callback status_text Callback status_code
Executed The payment was successfully executed on the due-date After 03:15 in the morning of the due-date Executed   0
Failed Payment failed to execute during the due-date. After 23:59 of the due-date Failed   50000
Rejected User rejected the Pending payment in MobilePay Any time during the 8 day period when user is presented with the Pending payment in the MobilePay activity list. Rejected Rejected by user. 50001
Declined Merchant declined the Pending payment via the API Any time during the 8 day period when user is presented with the Pending payment in the MobilePay activity list. Declined Declined by merchant. 50002
Declined Agreement is not in Active state. Right after the payment request was received. Declined Declined by system: Agreement is not “Active” state. 50003
Declined If the Agreement’s frequency period has not passed since the last *Pending* or *Executed* Payment Request for that Agreement. Monthly agreements have a 1 week tolerance level. Right after the payment request was received. Declined Declined by system: Another payment is already due. 50004
Declined When the Agreement was canceled by merchant or by system Any time during the 8 day period when user is presented with the Pending payment in the MobilePay activity list. Declined Declined by system: Agreement was canceled. 50005
Rejected When the Agreement was canceled by user Any time during the 8 day period when user is presented with the Pending payment in the MobilePay activity list. Rejected Declined by system: Agreement was canceled. 50005
Declined A catch-all error code when payment was declined by core system. Right after the payment request was received. Declined Declined by system. 50006
Declined Declined due to user status. Right after the payment request was received. Declined Declined due to user status. 50009
Declined When the Agreement does not exist Right after the payment request was received. Declined Agreement does not exist. 50010
Declined When the due date before rule is violated Right after the payment request was received. Declined Due date of the payment must be at least 8 days in the future. 50011
Declined When the due date ahead rule is violated Right after the payment request was received. Declined Due date must be no more than 32 days in the future. 50012

There are validation rules; however, the payments are not validated until they have been created in our system. Therefore, even though you get a response with pending payments, they may not be valid. When you make a payment request, we will validate the request itself, but not the individual payments. So it only validates if you have the required parameters with the correct types. So the response you get for the payment request, does not say if the payment is pending, but if the payment creation is pending. Then the payments are processed in our system, and they will either be requested (valid) or declined (invalid). Moreover, you will receive a callback to inform whether payments are requested or declined. This will be sent to your payment status callback

The process on failed payments the DueDate is as follows:

• 06:00 First hiccup is run at 06:00 on the due date. Once done, a notification about completion is returned. Merchant is informed about successful payments and user about failed payment

• 13:30 Second hiccup is run at 13:30 on the due date. Once done, a notification about completion is returned. Merchant is informed about successful payments and user about failed payment.

• 18:00 20:00 22:30 - hiccups keep running throughout the day. Once done, a notification about completion is returned. Merchant is informed about successful payments and user about failed payment,

Payment state diagram

Other callback properties
Name Type Description Format
agreement_id guid Subscription agreement ID on the MobilePay side.  
payment_id guid Subscription payment ID on the MobilePay side.  
amount number(0.00) Amount withdrawn from the MobilePay user.  
currency string Amount currency (agreement’s currency)  
payment_date date Date of the batch when the payment was executed. ISO 8601 UTC date: YYYY-MM-DD
external_id string Payment ID on the merchant’s side. Maximum length is 30 characters  
Payment callback body example
[
    {
        "agreement_id" : "1b08e244-4aea-4988-99d6-1bd22c6a5b2c",
        "payment_id" : "c710b883-6ed6-4506-9599-490ead89525a",
        "amount" : "10.20",
        "currency" : "DKK",
        "payment_date" : "2016-09-29",
        "status" : "Rejected",
        "status_text" : "Rejected by user.",
        "status_code" : "50001",
        "external_id" : "SFPMT134560"
    }
]
Payment callback response properties

An array containing the following properties.

Name Description
payment_id Subscription payment ID on the MobilePay side.
status_code Status code on merchant’s system
status_text Description of the status.
transaction_id Unique identifier of the transaction on the merchant’s system.

status_code can have the following values:

The callback response properties are optional. In case of technical errors (HTTP response is not 2xx), we will try to re-POST the callback.

Payment callback response example
[
    {
        "payment_id" : "63679ab7-cc49-4f75-80a7-86217fc105ea",
        "status_code" : "3000",
        "status_text" : "Server is down.",
        "transaction_id" : "63679ab7-cc49-4f75-80a7-86217fc105ea"
    }
]

Update existing Payment Request

Use the PATCH /api/merchants/me/paymentrequests/{paymentId} endpoint to decrease the requested amount to be paid.

[
    {
        "value": "10.01",
        "path": "/amount",
        "op": "replace"
    }
]

One-Off Payments

You are able to:

Note: Subscription payments are charged automatically, while one-off are charged when the customer manually swipes accept. OneOff payment does not affect the frequency and grace period. So if you create an agreement with a OneOff, you can request the first subscriptions payment whenever you want. You can also request a OneOff on an existing agreement in between two subscriptions payments, and it will not be affected by the frequency. But if you do it on an existing agreement, the user has to swipe to accept the payment. When you create an agreement with a OneOff, and the user accepts the agreement, the payment will be processed and executed right away. OneOff is an instant payment, and it is not subject to the 8 day rule.

If you create a OneOff payment, it will have the state requested. If the user then is not able to accept it, due to blocked card or so, it will expire after 1 day. You will receive a callback for that. The payment will be requested, to give the user the option to change card, but it will never be reserved if the user cannot accept it.

If an agreement was requested with a OneOff, and the user cannot accept it, the agreement will never be created. Because the user must accept the OneOff and the agreement at the same time. Therefore, you have to start again if the agreement request expires. But again, the user have the option to change card and accept the agreement.

User cannot cancel the agreement with pending payment reservation, only the merchant can do so.

By cancelling the agreement with a pending payment reservation, then the merchant also automatically cancels the reservation

Request One-Off Payment With a New Subscriptions Agreement

Use this when the user wants to setup an agreement and you want to charge upfront, i.e. a newspaper subscription. It starts to be effective from the moment the money is payed. It is initiated by user, which is why a redirect needs to happen, so that the user can accept it.

Add a one_off_payment property to the POST /api/merchants/me/agreements?api-version=1.1 request payload if you want the agreement being activated only when the user is successfully charged an initial subscription amount.

{
  "external_id": "AGGR00068",
  "amount": "10",
  "currency": "DKK",
  "description": "Monthly subscription",
  "next_payment_date": "2017-03-09",
  "frequency": 12,
  "links": [
    {
      "rel": "user-redirect",
      "href": "https://example.com/1b08e244-4aea-4988-99d6-1bd22c6a5b2c"
    },
    {
      "rel": "success-callback",
      "href": "https://example.com/1b08e244-4aea-4988-99d6-1bd22c6a5b2c"
    },
    {
      "rel": "cancel-callback",
      "href": "https://example.com/1b08e244-4aea-4988-99d6-1bd22c6a5b2c"
    }
  ],
  "country_code": "DK",
  "plan": "Basic",
  "expiration_timeout_minutes": 5,
  "mobile_phone_number": "4511100118",
  "one_off_payment": 
    {
      "amount": "80",
      "external_id": "OOP00348",
      "description": "Down payment for our services"
    }
}

Newly added request parameters

Parameter Type Required Description Valid values
one_off_payment object   One-Off Payment details.  
one_off_payment.amount number(0.00) required One-Off Payment amount, which will be displayed for the user in the MobilePay app. > 0.00, decimals separated with a dot.
one_off_payment.description string(60)   Additional information provided by the merchant to the user, that will be displayed on the One-off Payment screen.  
one_off_payment.external_id string(30) required One-Off Payment identifier on the merchant’s side. This will be included in the request body of the payment callback.  

In this case the response of POST /api/merchants/me/agreements?api-version=1.1 will contain additional one_off_payment_id value - id of the newly requested One-Off Payment.

{
  "id": "1b08e244-4aea-4988-99d6-1bd22c6a5b2c",
  "one_off_payment_id": "2a5dd31f-32c1-4517-925f-9c60ba19f8ca",
  "links": [
    {
      "rel": "mobile-pay",
      "href": "https://<mobile-pay-landing-page>/?flow=agreement&id=1b08e244-4aea-4988-99d6-1bd22c6a5b2c&redirectUrl=https%3a%2f%2fwww.example.com%2fredirect&countryCode=DK&mobile=4511100118"
    }
  ]
}

Request One-off Payment on an Existing Agreement

Use this when your customer has an active subscription agreement, and he wants to order extra services / products. i.e. on top of iTunes subscriptions to rent a movie. It needs to be initiated by the user, and a redirect needs to happen for the user to accept it.

Use a POST /api/merchants/me/agreements/{agreementId}/oneoffpayments?api-version=1.1 endpoint in order to charge your customer one time for extra services.

{
  "amount": "80",
  "external_id": "OOP00348",
  "description": "Pay now for additional goods",
  "links": [
    {
      "rel": "user-redirect",
      "href": "https://example.com/1b08e244-4aea-4988-99d6-1bd22c6a5b2c"
    }
  ]
}

One-off Payment will expire in 1 day if it is not accepted or rejected by the user during that time.

Request parameters
Parameter Type Required Description Valid values
amount number(0.00) required One-off Payment amount, which will be displayed for the user in the MobilePay app. > 0.00, decimals separated with a dot.
description string(60) required Additional information provided by the merchant to the user, that will be displayed on the One-off Payment screen.  
external_id string required One-off Payment identifier on the merchant’s side. This will be included in the request body of the payment callback.  
links string required Link relation of the One-off Payment creation sequence. Must contain 1 value for user redirect.  
links[].rel string required Link relation type. user-redirect
links[].href string required Link relation hyperlink reference. https://<merchant’s url>

The response of POST /ap/merchants/me/agreements/{agreementId}/oneoffpayments?api-version=1.1 contains two values: a unique id of the newly requested One-Off Payment and a link rel = mobile-pay.

{
  "id": "07b70fdd-a300-460d-9ba1-aee2c8bb4b63",
  "links": [
    {
      "rel": "mobile-pay",
      "href": "https://<mobile-pay-landing-page>/?flow=agreement&id=1b08e244-4aea-4988-99d6-1bd22c6a5b2c&oneOffPaymentId=07b70fdd-a300-460d-9ba1-aee2c8bb4b63&redirectUrl=https%3a%2f%2fwww.example.com%2fredirect&countryCode=DK&mobile=4511100118"
    }
  ]
}
One-Off payment screens

Callbacks

Once the one-off payment status changes from Requested to Reserved, Rejected or Expired, a callback will be done to the callback address, which is configurable via PATCH /api/merchants/me with path value /payment_status_callback_url. The same way as with callbacks for regular payment requests.

New Status Condition When to expect Callback status Callback status_text Callback status_code
Reserved The one-off payment was accepted by user and money is reserved for you on his card. You can now capture the money. After user accepts the requested one-off payment. Reserved Payment successfully reserved. 0
Rejected User rejected one-off payment request in MobilePay. Right after user rejects one-off payment. Rejected Rejected by user. 50001
Expired One-off payment was neither accepted, nor rejected by user. 1 day after you requested one-off payment Expired Expired by system. 50008

One-off payment state diagram

Capture Reserved One-Off Payment

When you receive a callback about successfully reserved payment, now it’s time to capture your money. You can do that by making a call to POST /api/merchants/me/agreements/{agreementId}/oneoffpayments/{paymentId}/capture?api-version=1.1 endpoint. If the HTTP response is 204 - No Content, it means that the money was transfered to your account.

Cancel One-Off Payment Request/Reservation

In case you weren’t able to deliver goods or any other problem occur, you can always cancel one-off payment until it’s not captured or expired. You can do that by making a call to DELETE /api/merchants/me/agreements/{agreementId}/oneoffpayments/{paymentId}?api-version=1.1 endpoint. If the HTTP response is ‘204 - No Content’, it means that one-off payment request/reservation was canceled.

It is mandatory for the merchant to Capture or Cancel one-off payment if it was reserved on a customer account. You need to do so within 14 days


Refunds

Full refund - 100% of the amount paid is returned to the payer.
Partial refund - An amount up to the net (the amount the merchant received) will be returned to the payer. Multiple partial refunds can be made.

Request a Refund

Use the POST /api/merchants/me/agreements/{agreementId}/payments/{paymentId}/refunds endpoint to request a Refund.

{
    "amount": 10.99,
    "status_callback_url": "https://example.com",
    "external_id": "ABC123"
}

Request parameters

Parameter Type Required Description Valid values
amount number(0.01) optional The requested amount to be returned. >= 0.01, decimals separated with a dot. If not specified, payment will be fully refunded.
status_callback_url string required Link relation hyperlink reference. valid url
external_id string optional Refund’s identifier on the merchant’s side. This will be included in the request body of the refund callback.  

The POST /api/merchants/me/agreements/{agreementId}/payments/{paymentId}/refunds service returns HTTP 202 and the response contains single value: a unique id of the newly created Refund.

HTTP 202 Response body example
{
    "id": "263cfe92-9f8e-4829-8b96-14a5e53c9041",
    "amount": 10.99,
    "status_callback_url": "http://example.com",
    "external_id": "ABC123"
}

Callbacks

When the Refund’s status changes from Requested we will do a callback to the callback address provided in request parameter status_callback_url.

Refund callback body example
{
    "refund_id" : "4bb9b33a-f34a-42e7-9143-d6eabd9aae1d",
    "agreement_id" : "1b08e244-4aea-4988-99d6-1bd22c6a5b2c",
    "payment_id" : "c710b883-6ed6-4506-9599-490ead89525a",
    "amount" : "10.99",
    "currency" : "DKK",
    "status" : "Issued",
    "status_text" : null,
    "status_code" : 0,
    "external_id": "ABC123"
}
Refund callback response example
{
    "refund_id" : "4bb9b33a-f34a-42e7-9143-d6eabd9aae1d",
    "agreement_id" : "1b08e244-4aea-4988-99d6-1bd22c6a5b2c",
    "payment_id" : "c710b883-6ed6-4506-9599-490ead89525a",
    "status_code" : "3000",
    "status_text" : "Server is down.",
    "transaction_id" : "63679ab7-cc49-4f75-80a7-86217fc105ea"
}

New Status Condition When to expect Callback status Callback status_text Callback status_code
Issued The Refund was successfully issued Right after the refund request was received Issued    
Declined If Payment is already fully refunded Right after the refund was requested Declined Payment is already fully refunded. 60001
Declined If the total sum of previous Refunds exceed the original payment amount Right after the refund was requested Declined The total sum of previous Refunds cannot exceed the original payment amount. 60002
Declined When Refund was declined by system Right after the refund was requested Declined Payment was not found. 60003
Declined When Refund was declined by system Right after the refund was requested Declined Payment cannot be refunded. 60004
Declined A catch-all error code when Refund was declined by core system. Right after the refund was requested Declined Refund was declined by system. 60005
Declined When Refund was declined by system. Right after the refund was requested Declined Cannot refund payments that are older than 30 days. 60006

Refund screens within mobile application: