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:
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.
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 '{}'
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
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.
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
$ 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
You might encounter the following HTTP errors:
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" } }
404 - Not Found
with no response body, if the resource (agreement or payment) is not found.
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" } }
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" } }
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
Use one of these endpoints to set REST callback authentication scheme and credentials:
PUT /api/merchants/me/auth/oauth2
- set OAuth2 scheme which conforms to RFC 6749 section 4.4..PUT /api/merchants/me/auth/basic
- set Basic auth scheme using username and password.PUT /api/merchants/me/auth/apikey
- set a value which will be set to the Authorization header. API key must conform to the token68 specification as defined in RFC 7235 section2.1..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):
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",
"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.
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. | ||
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:
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"
}
]
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.
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_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"
}
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_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.
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",
"external_id": "PMT000023",
"description": "Monthly payment"
}
]
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 1 day in the future, otherwise the Subscription Payment will be declined. | 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:
{
"pending_payments": [{
"payment_id": "263cfe92-9f8e-4829-8b96-14a5e53c9041",
"external_id": "PMT000023"
}
],
"rejected_payments": [{
"external_id": "PMT000023",
"error_description": "The Amount field is required."
}
]
}
The merchant can send a payment max 32 days prior due date, and at least 1 day 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 1 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).
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:
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-1 days 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-1 days 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 | Antoher payment is already scheduled on that day for the user | 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-1 days 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-1 days 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 1 day 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,
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 |
[
{
"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"
}
]
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_id" : "63679ab7-cc49-4f75-80a7-86217fc105ea",
"status_code" : "3000",
"status_text" : "Server is down.",
"transaction_id" : "63679ab7-cc49-4f75-80a7-86217fc105ea"
}
]
Use the PATCH /api/merchants/me/paymentrequests/{paymentId}
endpoint to decrease the requested amount to be paid.
[
{
"value": "10.01",
"path": "/amount",
"op": "replace"
}
]
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 1 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
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",
"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"
}
]
}
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.
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"
}
]
}
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 |
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.
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
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.
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"
}
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.
{
"id": "263cfe92-9f8e-4829-8b96-14a5e53c9041",
"amount": 10.99,
"status_callback_url": "http://example.com",
"external_id": "ABC123"
}
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_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_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: