Skip to main content

API guide

The Vipps MobilePay Recurring API delivers recurring payment functionality for a merchant to create a payment agreement with a customer for fixed interval payments. When the agreement is accepted by the end user, the merchant can send charges that will be automatically processed on the due date.

Important: The Recurring v2 API will be phased out and will not be available from 1 November 2023. See the migration guide for an overview of what has changed from v2 to v3.

Requirements

Important: The Recurring API requires additional compliance checks (more than what is required for the ePayment API), as required by Finanstilsynet (the financial authorities).

To get access to the Recurring API in production, please order Billing and Recurring Payments. It is the same order form as Payment Integration (ePayment API). You will then get a new sales unit (MSN) that can be used for recurring payments.

If you need to use an existing sales unit that already has access to the eCom/ePayment API for the Recurring API too, please contact your KAM or customer service. Please have this information ready:

  • Estimated total annual turnover for the sales unit. Example: 100 MNOK.
  • Percentage of the payment volume that will be through recurring payments. Example: 50 MNOK.
  • The length of the agreements. Example: Annual and monthly.
  • The distribution (in %) of the lengths. Example: 80 % annual, 20 % monthly

Please note: You can check if you have access to the Recurring API:

API version: 3.0.0.

Terminology

TermDescription
AgreementA payment subscription with a set of parameters that a customer agrees to.
ChargeA single payment within an agreement.
IdempotencyThe property of endpoints to be called multiple times without changing the result after the initial request.

Flow

The overall flow is:

  1. The merchant creates a draft agreement and proposes it to the customer via Vipps MobilePay.
  2. The customer approves the agreement in Vipps MobilePay.
  3. The customer can find a full overview of the agreement in Vipps MobilePay, including a link to the merchant's website.
  4. The merchant sends a charge request to Vipps MobilePay at least two days before due date
  5. If the agreement is active, Vipps MobilePay authorizes the charge.
  6. Charge will be processed on due date.

This diagram shows a simplified flow:

See the How it works guides for details.

Call by call guide

There are two happy-flows based on how the sales unit is set up: One for "direct capture" and one for "reserve capture". This is specified with the transactionType, and for "direct capture" the sales unit must be configured for this by Vipps MobilePay.

For more details, see Knowledge base: Reserve and capture.

Please note: Vipps MobilePay will only perform a payment transaction on an agreement that the merchant has created a charge for with the POST:/recurring/v3/agreements/{agreementId}/charges endpoint. You can also manage charges and agreements.

Direct capture

For a "transactionType": "DIRECT_CAPTURE" setup, the normal flow would be:

  1. Create a (draft) agreement using the POST:/recurring/v3/agreements endpoint. The user can now confirm the agreement in Vipps MobilePay (the app). See Create a new agreement.
  2. The user approves the agreement in Vipps MobilePay: This will result in a capture(or reserve) of the initial charge (if one was defined in the first step). See Initial charge.
  3. Retrieve the agreement by calling the GET:/recurring/v3/agreements/{agreementId} endpoint. See Retrieve an agreement. Please note: At this point the agreement will be ACTIVE if the user completed step 2.
  4. All future charges can be created by using the POST:/recurring/v3/agreements/{agreementId}/charges endpoint. For direct capture you must set "transactionType": "DIRECT_CAPTURE". See Create a charge. Based on the due set in the request, we will try to process the charge on that day. If for some reason, a charge fails to be processed, we will retry for the number of days specified by the retryDays value. We strongly recommend at least two days retry: retryDays: 2.

Reserve capture

Please note: Reserve capture on recurring charges is available in the Recurring API v3. In the API V2, reserve capture is only available on initial charges.

For a "transactionType": "RESERVE_CAPTURE" setup, the normal flow would be:

  1. Create a (draft) agreement using the POST:/recurring/v3/agreements endpoint. The user can now confirm the agreement in the Vipps or MobilePay app. See Create a new agreement.
  2. The user approves the agreement in Vipps MobilePay: This will result in a capture(or reserve) of the initial charge (if one was defined in the first step). See Initial charge.
  3. Retrieve the agreement by calling the GET:/recurring/v3/agreements/{agreementId} endpoint. See Retrieve an agreement. Please note: At this point the agreement will be ACTIVE if the user completed step 2.
  4. All future charges can be created by using the POST:/recurring/v3/agreements/{agreementId}/charges endpoint. For reserve capture you must set "transactionType": "RESERVE_CAPTURE". See Create a charge. Based on the due set in the request, we will try to process the charge on that day. If the charge is processed successfully, the status will be RESERVED. If for some reason, a charge fails to be processed, we will retry for the number of days specified by the retryDays value. We recommend at least 2 days retry.
  5. If there is a product that is shipped to the customer, the charge should be captured at this point. Capture the charge by calling the POST:/recurring/v3/agreements/{agreementId}/charges/{chargeId}/capture endpoint.

API endpoints

OperationDescriptionEndpoint
List agreementsList all agreements for a merchant.GET:/recurring/v3/agreements
Create an agreementCreate a new, draft agreement.POST:/recurring/v3/agreements
Retrieve an agreementRetrieve the details of an agreement.GET:/recurring/v3/agreements/{agreementId}
Update an agreementUpdate an agreement with new details.PATCH:/recurring/v3/agreements/{agreementId}
Stop an agreementUpdate the status to STOPPED.PATCH:/recurring/v3/agreements/{agreementId}
List chargesGet all charges for an agreement.GET:/recurring/v3/agreements/{agreementId}/charges
Create a chargeCreate a new charge for an agreement.POST:/recurring/v3/agreements/{agreementId}/charges
Create multiple chargesCreate multiple charges with single request.POST:/recurring/v3/agreements/charges
Retrieve a chargeRetrieve all details of a charge.GET:/recurring/v3/agreements/{agreementId}/charges/{chargeId}
Retrieve a charge by IDRetrieve a single charge just by chargeId. Useful for investigating claims from customers, but not intended for automation.GET:/recurring/v3/charges/{chargeId}
Capture a chargeEach charge must first be created, then captured.POST:/recurring/v3/agreements/{agreementId}/charges/{chargeId}/capture
Cancel a chargeCancel an existing charge before the user is charged.DELETE:/recurring/v3/agreements/{agreementId}/charges/{chargeId}
Refund a chargeRefund a charge that has been performed.POST:/recurring/v3/agreements/{agreementId}/charges/{chargeId}/refund

See Authentication and authorization.

See the Quick start guide for en easy way to test the API.

Authentication and authorization

All API calls are authenticated with an access token and an API subscription key. See Get an access token, for details.

Use the standard HTTP headers for all requests.

Idempotency Key header

V3 API only The Idempotency-Key header must be set in any request that creates or modifies a resource (POST, PUT, PATCH or DELETE). This way, if a request fails for any technical reason, or there is a networking issue, it can be retried with the same Idempotency-Key. The idempotency key should prevent operations and side effects from being performed more than once, and you should receive the same response as if you only sent one request.

Important: If the response is a client-error (4xx), you will continue to get the same error as long as you use the same idempotency key, as the requested operation is not retried.

Important: If you reuse an idempotency key on a different request, you will get a 409 CONFLICT.

See the Idempotency header for more details.

Continuation-Token header

V3 API only The Continuation-Token header is introduced on endpoints that returns multiple items to allow pagination. When returned from the API, it indicates that there are more items to be received. In order to receive the next page, repeat the request adding the received token in the Continuation-Token-header.

orderId recommendations

An optional and recommended orderId field can be set in the POST:/recurring/v3/agreements/{agreementId}/charges request.

{
"amount": 49900,
"description": "Premier League subscription",
"due": "2030-12-31",
"transactionType": "DIRECT_CAPTURE",
"retryDays": 5,
"orderId": "acmeshop123order123abc"
}

The orderId replaces the chargeId.

Important: If the orderId is provided:

  • The value of the orderId is used for all instances of chargeId
  • The orderId (and chargeId) is used for all subsequent identification of the charge.
  • The orderId is used in the settlement files if no externalId is specified.

This orderId must be unique across all Recurring and eCom transactions for the given merchantSerialNumber.

If the field is not provided, we will automatically create a unique ID prefixed with chr-: chr-xxxxxxx(where each x is an alphanumeric character).

If you ever have a problem that requires us to search in our logs, we will need orderId values that are "unique enough" to actually find them. An orderId that is just a number may not be possible to find.

While the minimum length for orderId technically is just one character, we strongly recommend using at least 6 characters, and a combination of numbers and characters.

The maximum length of an orderId is 50, and can contain alphanumeric characters and dashes: a-z, A-Z, 0-9, -. Example: 2c2a838c-5a88-4b3a-ab9f-e884b92b9bec.

We strongly recommend to use orderId format that makes it easy to search for them in logs. This means that abc123def456 is a better format than 123456.

Leading zeros should be avoided, as some applications (like Excel) tend to remove them, and this may cause misunderstandings.

With multiple sales units, prefixing the orderId with the MSN for each sales unit is recommended: If the MSN is 654321, the orderId values could start at 654321000000000001 and increment by 1 for each order, or some similar, unique and readable pattern.

Agreements

An agreement is between the Vipps MobilePay user and the merchant. This payment agreement allows you to routinely charge the customer without requiring them to manually approve every time. See charges for more details.

Create an agreement

This is an example of a request body for the POST:/recurring/v3/agreements call:

{
"phoneNumber":"4791234567",
"interval": {
"unit" : "MONTH",
"count": 1
},
"merchantRedirectUrl": "https://example.com/confirmation",
"merchantAgreementUrl": "https://example.com/my-customer-agreement",
"pricing": {
"amount": 49900,
"currency": "NOK"
},
"productDescription": "Access to all games of English top football",
"productName": "Premier League subscription"
}

Agreement fields and their usage in the Vipps or MobilePay app

  • pricing: Price of the subscription.
  • interval: Describes how often the user will be charged.
  • productName: A short description of the subscription. Will be displayed as the agreement name in the Vipps or MobilePay app.
  • productDescription: More details about the subscription. Optional field.
  • merchantAgreementUrl: URL where you will send the customer to view/manage their subscription. See Merchant agreement URL for more details.

Agreement fields

Please note: To create agreements with support for variable amounts on charges, see Recurring agreements with variable amount.

Agreements may be initiated with or without an initial charge.

The agreement price and the amount for the initial charge, is given in øre, the centesimal subdivision of the Norwegian kroner (NOK). There are 100 øre in 1 krone.

#AgreementDescription
1Agreement starting nowAgreement with an initialcharge that uses DIRECT_CAPTURE will only be active if the initial charge is processed successfully
2Agreement starting in futureAgreement without an initialcharge, or with initialcharge that uses RESERVE_CAPTURE, can be approved, but no payment will happen until the first charge is provided

The response contains an agreementResource, a vippsConfirmationUrl and an agreementId. This agreementResource is a complete URL for performing a GET:/recurring/v3/agreements/{agreementId} request.

The vippsConfirmationUrl should be used to redirect the user to the Vipps MobilePay landing page in a desktop flow (with https://), or to Vipps MobilePay in a mobile flow (with vipps://), where the user can then approve the agreement.

See landing page from Knowledge base, for more details.

Please note: If payment should be required to activate an agreement, you need to specify an initial charge. If you are dealing with physical goods, this should be a RESERVE_CAPTURE, but for digital goods where the customer instantly gains access, DIRECT_CAPTURE might be easier to manage. See Initial charge.

Pricing representation

There are two different types of pricing available:

The first one is LEGACY, this is the default type. See Amount limits for the limit rules.

Here is a truncated example of request body for the POST:/recurring/v3/agreements endpoint:

{
"pricing": {
"type": "LEGACY",
"amount": 100000,
"currency": "NOK"
},
"productName": "MyNews Digital",
"phoneNumber": "4791234567",
"...": "..."
}

The second one is VARIABLE. See variable amount.

Here is a truncated example of request body for the POST:/recurring/v3/agreements endpoint:

{
"pricing": {
"type": "VARIABLE",
"suggestedMaxAmount": 3000,
"currency": "NOK"
},
"productName": "MyNews Digital",
"phoneNumber": "4791234567",
"...": "..."
}

Accept an agreement

The POST:/recurring/v3/agreements endpoint will return the following JSON structure.

{
"vippsConfirmationUrl": "https://api.vipps.no/dwo-api-application/v1/deeplink/vippsgateway?v=2/token=eyJraWQiOiJqd3RrZXkiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJmMDE0MmIxYy02YjI",
"agreementId": "agr_TGSuPyV"
}

The vippsConfirmationUrl should be used to redirect the user to the Vipps MobilePay landing page. The user can then confirm their identity and receive a prompt to accept the agreement within Vipps MobilePay.

If the payment is initiated in a native app, it is possible to explicitly force a vipps:// URL by sending the isApp parameter in the initiate call:

  • "isApp": false: The URL is https://, which handles everything automatically for you. The phone's operating system will know, through "universal linking", that the https://api.vipps.no URL should open the Vipps or MobilePay app, and not the default web browser. Please note: In some cases, this requires the user to approve that Vipps MobilePay is opened, but this is usually only the first time.
  • "isApp": true: The URL is for a deeplink, for forced app-switch to Vipps, with vipps://. Please note: In our test environment (MT), the scheme is vippsMT://

If the user does not have Vipps MobilePay installed:

  • "isApp":false: The Vipps MobilePay landing page will be shown, and the user can enter a phone number and pay on a device with Vipps MobilePay installed.
  • "isApp": true: The user will get an error message saying that the link can not be opened.

Agreement activation or rejection

If the user accepts or rejects the agreement the user will be redirected back to themerchantRedirectUrl.

Activation of the agreement is not guaranteed to be finished by the time the user is redirected back to the merchantRedirectUrl. The agreement could still have the status PENDING. Also, if the user closes the Vipps or MobilePay app before the redirect is done, the merchantRedirectUrl will not be used.

Therefore, it is important to actively check the agreement's status with the GET:/recurring/v3/agreements/{agreementId} endpoint instead of relying on the redirect to the merchantRedirectUrl. See current rate limits for more details about polling.

Once a final status (ACTIVE, EXPIRED or STOPPED) is returned by the API, the agreement can be updated in your system.

Merchant agreement URL

The merchantAgreementUrl is a link to the customer's account page on your website, where they can manage the agreement (e.g., change, pause, cancel the agreement). The URL is opened in the standard web browser.

Please note: Vipps MobilePay does not offer any form of agreement management, as this may include quite complex operations (e.g., changing subscription types, temporary address change, pausing the agreement, etc.).

Important: The integrator must implement such functionality for the customer to manage the agreement in their system. It is the integrator's responsibility to make sure the merchantAgreementUrl in the agreement works for the user.

We don't have any specific requirements for the security of the page, other than using HTTPS, but strongly recommend using Login, so the user does not need a username or password, but is logged in automatically through Vipps MobilePay. See the Login API for more details.

Intervals

There are two types of interval : "RECURRING" and "FLEXIBLE".

When an interval has type "RECURRING", it is defined with an interval unit YEAR, MONTH, WEEK, or DAY and frequency as a count. The count can be any number between 1 and 31. The interval defines how often the user will be charged.

When an interval has type "FLEXIBLE", it means that it has no periodicity. For example, a user can subscribe to an agreement for e-scooter rental, where the user is charged every time they rent a scooter, without any interval involved. To create an agreement with a flexible interval, the interval field has to be omitted.

Example for a bi-weekly subscription:

{
"interval": {
"unit": "WEEK",
"count": 2
}
}

Users can be charged the full amount every two weeks, regardless of the day in the week. (E.g. First charge can be due on Wednesday of week 1 and the second charge can be due on Monday of week 3).

Example for a quarterly subscription

{
"interval": {
"unit": "MONTH",
"count": 3
}
}

Users can be charged the full amount every third month, regardless of the day in the month. (E.g. First charge can be due on 05.01.2023 and second on 02.04.2023)

Examples for a yearly subscription

{
"interval": {
"unit": "YEAR",
"count": 1
}
}

OR

{
"interval": {
"unit": "MONTH",
"count": 12
}
}

Users can be charged the full amount once every year, regardless of the day in the year. (E.g. First charge can be due on 02.06.2022 and second charge on any day in 2023, for example on 01.01.2023)

Example for a subscription every 30th day:

{
"interval": {
"unit": "DAY",
"count": 30
}
}

Users can be charged the full amount once every 30 days, regardless of the day in the month. (E.g. First charge can be due on 12.06.2022 and second charge on 04.07.2022)

Initial charge

Please note: If the subscription is cheaper in the beginning than the normal price later, use campaigns in combination with initial charge. If you use initialcharge alone for campaigns, users will be confused by how it appears in Vipps MobilePay, as it looks like the full price period starts immediately.

Initial charge will be performed if the initialcharge is provided when creating an agreement. If there is no initial charge, don't send initialcharge when creating the new agreement.

Unlike regular (or RECURRING) charges, there is no price limit on an initialCharge. This allows for products to be bundled with agreements as one transaction (for example, a phone). The user will be clearly informed when an initialCharge is included in the agreement they are accepting.

See Charge Titles for explanation of how the charge description is shown to the user.

The initial charge has two forms of transaction, DIRECT_CAPTURE and RESERVE_CAPTURE.

DIRECT_CAPTURE processes the payment immediately, while RESERVE_CAPTURE reserves the payment for capturing at a later date. See: What is the difference between "Reserve Capture" and "Direct Capture" in the FAQ.

RESERVE_CAPTURE must be used when selling physical goods bundled with an agreement - such as a phone when subscribing to an agreement.

This example shows the same agreement as above, with an initialCharge of 499 NOK:

{
"phoneNumber": "4791234567",
"initialCharge": {
"amount": 49900,
"description": "Premier League subscription",
"transactionType": "DIRECT_CAPTURE"
},
"interval": {
"unit" : "MONTH",
"count": 1
},
"merchantRedirectUrl": "https://example.com/confirmation",
"merchantAgreementUrl": "https://example.com/my-customer-agreement",
"pricing": {
"amount": 49900,
"currency": "NOK"
},
"productDescription": "Access to all games of English top football",
"productName": "Premier League subscription"
}

Change the transactionType field to RESERVE_CAPTURE to reserve the initial charge.

{
"initialCharge": {
"transactionType": "RESERVE_CAPTURE",
"amount": 19900,
"description": "Phone"
}
}

A reserved charge can be captured with the POST:/recurring/v3/agreements/{agreementId}/charges/{chargeId}/capture endpoint when the product is shipped.

Retrieve an agreement

A newly created agreement will be in status PENDING for 10 minutes before it expires. If the customer approves the agreement, and the initialCharge (if provided) is successfully processed, the agreement status will change to ACTIVE.

The approved agreement is retrieved from the GET:/recurring/v3/agreements/{agreementId} endpoint with "status":"ACTIVE" when the customer has approved the agreement. It is important to keep retrieving the agreement until the status is ACTIVE, STOPPED or EXPIRED.

See Agreement states.

This is an example response from a call to the GET:/recurring/v3/agreements/{agreementId} endpoint:

{
"id": "agr_ADbq4JK",
"created": "2018-08-22T12:59:56Z",
"start": "2018-08-22T13:00:00Z",
"stop": null,
"status": "ACTIVE",
"productName": "Premier League subscription",
"productDescription": "Access to all games of English top football",
"pricing": {
"type": "LEGACY",
"amount": 49900,
"currency": "NOK"
},
"interval": {
"unit" : "MONTH",
"count": 2,
"text": "every 2 months"
},
"campaign": null,
"merchantAgreementUrl": "https://www.merchant.no/subscriptions/1234/",
"uuid": "6080c099-d7f2-43ef-a82b-2991ccc3a239",
"countryCode": "NO"
}

Campaigns

A campaign in Recurring is a period where the price is lower than usual, and this is communicated to the customer with the original price shown for comparison. Campaigns cannot be used in combination with variable amount.

There are 3 different campaign types (just in V3 API version): price campaign, period campaign, event campaign. See more about the different campaign types in the table below.

Campaign typesDescriptionExample
price campaignDifferent interval price until specified date. Same interval as agreement.1kr every week until 2022-12-25T00:00:00Z and then 50kr every week
period campaignA set price for a given duration. A duration is defined by a number of periods (DAY, WEEK, MONTH, YEAR)10 weeks for 1kr and then 349kr every month
event campaignA set price until a given event date with a text describing the event1kr until Christmas and then 349kr every month

In order to start a campaign, the campaign field has to be added to the agreement draft body in the POST:/recurring/v3/agreements call.

Product description guidelines for agreements with campaigns

We do not recommend you to use Product Description for agreements with a campaign. We see that the user experience is not optimal when a lot of text is "squeezed" in the purple bubble displaying an agreement.

Price campaign

price-campaign-explanation

{
"campaign": {
"type": "PRICE_CAMPAIGN",
"end": "2022-12-25T00:00:00Z",
"price": 100
}
}
FieldDescription
typeThe type of the campaign
priceThe price that the customer will pay for each interval during the campaign
endThe end date of the campaign

screen-price-campaign

Period campaign

period-campaign-explanation

{
"campaign": {
"type": "PERIOD_CAMPAIGN",
"price": 100,
"period": {
"unit": "WEEK",
"count": 10
}
}
}
FieldDescription
typeThe type of the campaign
priceThe price that the customer will pay for the period of the campaign
periodThe period where the campaign price is applied. Consists of a Unit and a Count, for example: { "unit": "MONTH", "count": 1 }

screen-price-campaign

Event campaign

event-campaign

{
"campaign": {
"type": "EVENT_CAMPAIGN",
"price": 100,
"eventDate": "2022-09-01T00:00:00Z",
"eventText": "To the end of august"
}
}
FieldDescription
typeThe type of the campaign
priceThe price that the customer will pay until the event date
eventDateThe date of the event marking the end of the campaign
eventTextThe event text to display to the end user

Please note: We recommend starting the event text with lowercase for better user experience. See example below.

Screen event campaign

Charges

An agreement has payments, called charges.

orderId and externalId

The optional orderId-field will override the automatically generated chargeId for the charge if specified, and will be used for all subsequent identification of the charge, and in settlement reports unless an externalId is specified.

The externalId-field is a more flexible alternative to orderId which does not override the chargeId, but will be used in settlement reports.

This means that if both orderId and externalId are specified, the orderId will override chargeId and be used for all subsequent identification of the charge, while the externalId will be used in settlement reports.

As externalId does not have the same requirements for uniqueness as orderId, it is important that you understand how this will impact your settlements before using it.

Also see Recommendations for reference and orderId

Charge type

There are two types available for a charge. RECURRING and UNSCHEDULED.

note

If charge type is not specified on charge creation, type defaults to RECURRING.

Recurring charge

A recurring charge is a payment that is set up to occur on a specific due date. The charge will be processed at the due date, and retries are attempted according to the number of retryDays specified. We recommend using this type of charge for pre-planned payments that occur on a regular schedule (based on the agreement interval).

{
"amount": 49900,
"transactionType": "DIRECT_CAPTURE",
"description": "October",
"due": "2018-09-01",
"retryDays": 5,
"type": "RECURRING"
}

Unscheduled charge

note

To be able to create UNSCHEDULED charges, ask to be added to the allow list. See how to Contact us.

An unscheduled charge is sporadic or one-time payment that do not follow a set schedule. We recommend using this type of charge for a one-time purchase or a one-time service fee in relation to the agreement. Unscheduled charge will be processed asynchronously after creation. If the charge is successful, recurring.charge-reserved or recurring.charge.captured event is sent (depending on the transaction type of the charge). If the charge fails because of an invalid payment method, recurring.charge.failed event will be sent and the user is notified that the payment method linked to the agreement needs to be updated.

Please note: When unscheduled charges fail, we do not retry. It is the merchant/partner´s responsibility to retry the unscheduled charges.

{
"amount": 49900,
"transactionType": "DIRECT_CAPTURE",
"description": "October",
"type": "UNSCHEDULED"
}

Due date

due will define for which date a recurring charge will be performed. This date has to be minimum two days (one day in the test environment) in the future and maximum two years in advance. The minimum is set to two days because the user should be able to see the upcoming charge in the Vipps or MobilePay app. All charges due in 35 days or less are visible under the Payments tab in the Vipps or MobilePay app.

Example: If the charge is created on the 25th, the earliest the charge can be due is the 27th (25+2). This is so that the user can be informed about the upcoming charge. The user is only shown one charge per agreement, in order to not overwhelm the user when doing daily or weekly charges.

Please note: You can request to be put on a "one day minimum" allow list if you have a need to be able to create charges that are DUE 1 day after being created. This means that a charge can be created to be DUE the next day. Example: If the charge is created at any time on the 25th, the charge can be due and processed at the 26th.

Transaction type

A charge has two forms of transaction, DIRECT_CAPTURE and RESERVE_CAPTURE. Please note: RESERVE_CAPTURE transaction type is only available in the V3 API.

DIRECT_CAPTURE processes the payment immediately, while RESERVE_CAPTURE reserves the payment for capturing at a later date. See What is the difference between "Reserve Capture" and "Direct Capture" in the FAQ for more details.

RESERVE_CAPTURE must be used when selling physical goods or a need to provide access at a later point.

The advantage to using reserve capture is that you can release the reservation immediately:

  • For a reserved payment, the merchant can make a /cancel call to immediately release the reservation and make it available in the customer's account.
  • For a captured payment, the merchant must make a /refund call. It then takes a few days before the amount is available in the customer's account.

Amount limits

LEGACY pricing (default)

The amount of a charge is flexible and does not have to match the price of the agreement.

A limit is in place however, which is 5 times the agreement price. For example, in the agreement above a limit of 2495 NOK (499 x 5) would be in place. If this limit becomes a hindrance the agreement price can be updated.

Please note: Although it is technically possible to increase the price 10 times, we strongly recommend that you are as user-friendly as possible. Make sure the user understands any changes and are provided with updated information.

VARIABLE pricing

The user chooses a max amount themselves when accepting the agreement based on the recommended suggestedMaxAmount. There is currently a limit of 20 000 NOK for the suggestedMaxAmount. The max amount can at any time be changed by the user.

Charge descriptions

When charges are shown to users in the app, they will have a title, and a description. The title of a charge is derived directly from {agreement.productName} whereas the description is set per charge, i.e. {charge.description}. For example, a charge on an agreement with product name "Premier League subscription" with description "October" would look like the following screenshot:

Charge description example

When the charge is completed (the money has been moved), the payment will show up in the users' payment history. There, a charge from Vipps MobilePay recurring payments will have a description with follow format {agreement.ProductName} - {charge.description}.

This is an example of a request body for the POST:/recurring/v3/agreements/{agreementId}/charges call:

{
"amount": 49900,
"transactionType": "DIRECT_CAPTURE",
"description": "October",
"due": "2018-09-01",
"retryDays": 5
}

Please note: description cannot be longer than 45 characters.

Create a charge

To create a charge use the POST:/recurring/v3/agreements/{agreementId}/charges endpoint.

For agreements with VARIABLE_AMOUNT pricing, see Recurring agreements with variable amount.

Create multiple charges

To create multiple charges with single request use the POST:/recurring/v3/agreements/charges endpoint.

We validate charges in two steps. First, we check them at the API level, and any failed charges are returned in the API response. Then, we schedule asynchronous creation, where charges are validated against different criteria. If a charge doesn't pass this validation, a webhook is sent with the event type "recurring.charge-creation-failed.v1." Learn more about Webhooks

Capture a charge

Vipps The capture can be made up to 180 days after reservation.

MobilePay The capture can be made up to 7 days after reservation.

Capture payment allows the merchant to capture the reserved amount of a charge. The API allows for both a full amount capture and a partial amount capture (V3 API only)

The amount to capture cannot be higher than the reserved amount. According to Norwegian regulations, capture cannot be done before the goods have been shipped. The description text is mandatory and is displayed to the end user in the Vipps or MobilePay app.

Capture is done with the POST:/recurring/v3/agreements/{agreementId}/charges/{chargeId}/capture endpoint.

Please note: It is important to check the response of the /capture call. The capture is only successful when the response is HTTP 204 No Content.

Capture can be made up to 180 days after reservation. Attempting to capture an older payment will result in HTTP 400 Bad Request.

Partial capture

MobilePay Limited availability for MobilePay. You must inquire during onboarding or contact customer service.

V3 API only: Partial capture may be used in cases where a partial order is shipped or for other reasons. Partial capture can be called as many times as required while remaining reserved amount is available.

If one or more partial capture have been made, any remaining reserved amount will be automatically released after a few days. See FAQ: For how long is a payment reserved for more details.

If you cancel a charge that is PARTIALLY_CAPTURED, the remaining funds on the charge will be released back to the customer and the charge status will be set to CHARGED.

Cancel a charge

You can cancel charges that are in the PENDING, DUE or RESERVED state. If you cancel a charge that is PARTIALLY_CAPTURED, the remaining funds on the charge will be released back to the customer and the charge status will be set to CHARGED.

Please note: If you cancel an agreement, there is no need to cancel the charges that belong to the agreement. We will do this automatically for you.

A charge can be cancelled with the POST:/recurring/v3/agreements/{agreementId}/charges/{chargeId} endpoint.

Refund a charge

A charge can be refunded with the POST:/recurring/v3/agreements/{agreementId}/charges/{chargeId}/refund endpoint.

Charge attempts

Charge attempts are primarily made two times during the day: 07:00 and 15:00 UTC. We may do extra attempts and/or change this without notice. The processing of charges typically takes around one hour, however this varies, and we do not guarantee any time. This is the same both for our production and test environment. Subsequent attempts are made according to the retryDays specified.

When a charge has reached its due date, the status of the charge will be DUE until the charge is successful, for as long as the merchant has specified with retryDays. In other words, there will be no status updates while we are attempting to charge.

Important: We do not "leak" the customers' information about insufficient funds, blocked cards, or other problems. Users are informed about all such problems in the Vipps or MobilePay app, which is the only place they can be corrected. The merchant's customer service should always ask the user to check in the app if a charge has failed.

Please note: Payments might get processed any time during the day (07:00 UTC - 23:59 UTC) due to special circumstances requiring it.

Please note: Since payments can be processed any time (07:00UTC - 23:59 UTC) it is advisable to fetch the charge at/after 00:00 UTC the day after the last retry day to be sure you get the last status.

Payment Batches Schedule:

For Norwegian Agreements:
  • 9.00: Biggest batch with new charges
  • 17.00: Retry batch
For Finnish Agreements:
  • 1.00: Biggest batch with new charges
  • 6.00, 13.00, 18.00, 20.00, 22.00, and 23.00: Retry batches
For Danish Agreements:
  • 1.00: Biggest batch with new charges
  • 6.00, 13.00, 18.00, 20.00, 22.00, and 23.00: Retry batches

Charge retries

We will retry the charge for the number of days specified in retryDays. The maximum number of retryDays is 14.

This means that if the user's card has insufficient funds, the card has expired, the card is invalid, etc.: The user is notified and can correct the problem. We will make sure the user is able to pay.

tip

We strongly recommend at least two days retry: retryDays: 2.

The retryDays are not tied to the agreement’s interval. This means that a charge can be retried for a maximum of 14 days even though the next interval has started. For example, an agreement with daily interval can have a charge retried for multiple days, and it is possible to create new daily charges while others are still retrying.

info

We don't provide details about each charge attempt to the merchant, but we do help the user to correct any problems in the Vipps or MobilePay app. This results in a very high success rate for charges.

The status of a charge will be DUE while we are taking care of business, from the due date until the charge has succeeded, or until the retryDays have passed without a successful charge. The final status will be CHARGED or FAILED.

See: Charge states.

Retrieve a charge

To retrieve a charge, we recommend using the GET:/recurring/v3/agreements/{agreementId}/charges/{chargeId} endpoint.

Please note: The endpoint GET:/recurring/v3/charges/{chargeId} is not intended for automation. There is a stricter rate limiting (See Rate limiting) on this endpoint because it is more expensive to fetch a charge without the agreementId. Its purpose is to simplify investigations when the merchant lost track of which charge belongs to which agreement. It should not be used as a substitute for the GET:/recurring/v3/agreements/{agreementId}/charges/{chargeId} endpoint.

Details on charges

The response from the GET:/recurring/v3/agreements/{agreementId}/charges/{chargeId} endpoint contains the history of the charge and not just the current status. It also contains a summary of the total of amounts captured, refunded and cancelled.

Truncated example of the response from the GET:/recurring/v3/agreements/{agreementId}/charges/{chargeId} endpoint:

{
"id": "chr_WCVbcA",
"status": "REFUNDED",
"amount": 1000,
"type": "RECURRING",
"transactionType": "RESERVE_CAPTURE",
"...": "...",
"summary": {
"captured": 1000,
"refunded": 600,
"cancelled": 0
},
"history": [
{
"occurred": "2022-09-14T10:31:15Z",
"event": "CREATE",
"amount": 1000,
"idempotencyKey": "e80bd8c6-3b83-4583-a49c-847021fcd839",
"success": true
},
{
"occurred": "2022-09-16T06:01:00Z",
"event": "RESERVE",
"amount": 1000,
"idempotencyKey": "chr-4assY8f-agr_FJw2Anb-ProcessPayment",
"success": true
},
{
"occurred": "2022-09-18T06:01:00Z",
"event": "CAPTURE",
"amount": 1000,
"idempotencyKey": "096b1415-2c77-4001-9576-531a856bbaf4",
"success": true
},
{
"occurred": "2022-09-20T06:01:00Z",
"event": "REFUND",
"amount": 600,
"idempotencyKey": "0bc7cc3b-fdef-4d24-b4fe-49b7da40d22f",
"success": true
}
]
}

List charges

All charges, including the optional initial charge, for an agreement can be retrieved with the GET:/recurring/v3/agreements/{agreementId}/charges endpoint.

Manage charges and agreements

It is the merchant's responsibility to manage and update charges and agreements, and to use the API to make sure everything is in sync.

Agreement states

#StateDescription
1PENDINGAgreement has been created, but not approved by the user in the Vipps or MobilePay app yet
2ACTIVEThe agreement has been confirmed by the end user in the Vipps or MobilePay app and can receive charges
3STOPPEDAgreement has been stopped, either by the merchant by the PATCH:/recurring/v3/agreements/{agreementId} endpoint, or by the user by cancelling or rejecting the agreement.
4EXPIREDThe user did not accept, or failed to accept (due to processing an initialCharge), the agreement in the Vipps or MobilePay app

Update an agreement

A merchant can update an agreement by calling the PATCH:/recurring/v3/agreements/{agreementId} endpoint. The following properties are available for updating:

{
"productName": "A new name",
"productDescription": "A new description",
"merchantAgreementUrl": "https://example.com/vipps-subscriptions/1234/",
"pricing": {
"amount": 25000,
"suggestedMaxAmount": 300000
},
"interval": {
"type": "RECURRING",
"period": {
"count": 1,
"unit": "MONTH"
}
}
}

Updating amount is only possible for agreements with pricing.type:LEGACY

Updating suggestedMaxAmount is only possible for agreements with pricing.type:VARIABLE

You can also update the type of the interval. For instance, to make a recurring agreement flexible, you can change the interval to FLEXIBLE, by sending the following payload:

{
"interval": {
"type": "FLEXIBLE"
}
}

Please note: As a PATCH operation all parameters are optional. However, when setting an agreement status to STOPPED no other changes are allowed. Attempts at changing other properties while stopping an agreement will result in a 400 Bad Request response.

Pause an agreement

Today unfortunately we do not have a pause/freeze status. This is something we are looking into. If there should be a pause in an agreement, like a temporary stop of a subscription: Simply do not create any charges during the pause. We recommend to use Agreement Description to communicate to the user that the agreement is paused/frozen.

We recommended not to set the agreement status to STOPPED. STOPPED agreements cannot be reactivated.

Stop an agreement

When a user notifies the merchant that they want to cancel a subscription or service, the merchant must ensure that the status of the recurring agreement is set to STOPPED at a suitable time.

A merchant can stopped an agreement by calling the PATCH:/recurring/v3/agreements/{agreementId} endpoint. Request body for stopping an agreement:

{
"status": "STOPPED"
}

Stopping an agreement results in cancellation of any charges that are DUE/PENDING/RESERVED at the time of stopping it, and it will not be possible to create new charges for a stopped agreement.

We recommend that the recurring agreement remains ACTIVE for as long as the user has access to the service. For example; if the user cancels their subscription, but they are still able to use the service until the end of the billing cycle, the agreement should only be set to STOPPED at the end of the billing cycle.

Since STOPPED agreements cannot be reactivated, a benefit of waiting until the "end of service" before setting the agreement status to STOPPED is that the merchant will be able to reactivate the user's subscription without having to set up a new agreement.

Charge states

This table has all the details for the charge states returned by the GET:/recurring/v3/agreements/{agreementId}/charges/{chargeId} endpoint:

StateDescription
PENDINGThe charge has been created, but is not yet be visible in the Vipps or MobilePay app.
DUEThe charge is visible in the Vipps or MobilePay app and will be processed on the due date.
PROCESSINGThe charge is being processed right now.
UNKNOWNThe charge status is unknown. This is usually very transient and will be resolved shortly.
CHARGEDThe charge has been successfully processed, and the available amount has been captured.
FAILEDThe charge has failed because of insufficient funds, no valid cards, etc. The Vipps or MobilePay app gives the user all possible opportunities to pay, including adding a new card, but does not provide the details to the merchant.
REFUNDEDThe charge has been refunded. Refunds are allowed up to 365 days after the capture date.
PARTIALLY_REFUNDEDPart of the captured amount has been refunded.
RESERVEDThe charge amount has been reserved, and can now be captured POST:/recurring/v3/agreements/{agreementId}/charges/{chargeId}/capture
PARTIALLY_CAPTUREDPart of the reserved amount has been captured and the remaining amount as not been cancelled yet. If you do not plan on capturing the rest, you should cancel the remaining amount to release the funds to the customer.
CANCELLEDThe charge has been cancelled.

IMPORTANT: We don't provide details about each charge attempt to the merchant, but we help the user to correct any problems in the Vipps or MobilePay app. This results in a very high success rate for charges.

Example charge flows

Scenario: Everything goes as it should: The user has money, and the charge is successful on the due date:

  • PENDING -> DUE (just for the one due day)-> CHARGED

Scenario: The user does not have funds and retryDays = 0:

  • PENDING -> DUE -> FAILED

Scenario: The user does not have funds on the due date, retryDays = 10, and has funds on the fifth day:

  • PENDING -> DUE (for five days) -> CHARGED

Please note: Since charges are polled by the merchant, it is possible that the charge status appears to "skip" a transition, e.g. moving directly from PENDING to CHARGED, or even from PENDING to REFUNDED depending on your systems.

Charge failure reasons

Please note: failureReason and failureDescription are experimental, and will be replaced by an event log. Subscribe to get updates: Technical updates.

When fetching a charge through the API, you can find two fields in the response body to identify why the charge failed failureReason and failureDescription.

An example from a response:

{
"status": "FAILED",
"type": "RECURRING",
"failureReason": "user_action_required",
"failureDescription": "User action required"
}

Here is a list of possible values for failureReason, their respective descriptions and possible actions that the user/merchant could take.

ReasonDescriptionAction
user_action_requiredPayment failed. Could be lack of funds, card is blocked for ecommerce, card is expired. If you want to send an email or similar to the user, you should encourage them to open the Vipps or MobilePay app and check the payment there to see why it is not paid.User will get notified in their Vipps or MobilePay app and need to take action. This could be to add funds to the card or change the card on the agreement.
charge_amount_too_highAmount is higher than the user's specified max amount.The user has a lower maxAmount on the variableAmount agreement than the amount of the charge. The user must update their maxAmount on the agreement for the charge to be processed.
non_technical_errorPayment failed. Could be that the user has deleted their Vipps MobilePay profile.The user needs to take action in the app.
technical_errorPayment failed due to a technical error in Recurring or a downstream service.As long as the charge is not in the status FAILED, we are retrying the payment. If the status is FAILED and the customer has not been charged by any other means, we recommend creating a new charge with a new due date.

App

The user will be able to see these failures in their Vipps or MobilePay app and take the necessary action.

Example if a user has an expired card:

app-expired-card

Deprecated failureReasons

The following failureReasons are no longer exposed on charges:

ReasonDescriptionAction
insufficient_fundsPayment was declined by the payer bank due to lack of funds.User must either add funds to the card to cover the difference between the amount to be paid. Alternatively they can change to another, or add a new, payment source that is adequately funded to complete the transaction.
invalid_cardThe user tried to pay using a card that has either expired or is disabled by the issuer.User must change, or add a new, payment source on the agreement in Vipps.
verification_requiredPayment declined because the issuing bank requires verification.Ask the user to change, or add a new, payment source on their agreement in the Vipps or MobilePay app. Alternatively removing and then adding the card might solve the issue.
invalid_payment_sourceThe provided payment source is disabled or does not exist.User must change payment source for the agreement.
internal_errorInternal Error / Something went wrongThe error could not be identified as one of the above. Try to create the charge again, changing or adding payment sources on the agreement, or contact us for more information.

The user gets more information in the Vipps or MobilePay app regarding why the charge did not get processed. If they contact you about failing charges, you should refer them to the Vipps or MobilePay app. As long as the charge has retryDays left, we will continue to try and process the charge and notify the user.

Userinfo

We offer the possibility for merchants to ask for the user's profile information as part of the payment flow. This is also called "Profile Sharing".

To enable the possibility to fetch profile information for a user the merchant can add a scope parameter to the POST:/recurring/v3/agreements call.

See the Userinfo API guide for details.

Userinfo call by call guide

Scenario: You want to complete a payment and get the name and phone number of a customer.

  1. Retrieve the access token by calling the POST:/accesstoken/get endpoint.
  2. Add the scope field to the draft agreement request body and include the scope you wish to get access to (valid scope) before calling the POST:/recurring/v3/agreements endpoint.
  3. The user consents to the information sharing and accepts the agreement in Vipps.
  4. Retrieve the sub by calling the GET:/recurring/v3/agreements/{agreementId} endpoint.
  5. Using the sub from step 4, call the GET:/vipps-userinfo-api/userinfo/{sub} endpoint to retrieve the user's information.

Important note: The API call to the GET:/vipps-userinfo-api/userinfo/{sub} endpoint must not include the subscription key (the Ocp-Apim-Subscription-Key header) used for the Recurring API. This is because userinfo is part of Login and is therefore not under the same subscription, and will result in a HTTP Unauthorized 401 error.

Example calls

To request this scope, add the scope to the initial POST:/recurring/v3/agreements call

Example of request with scope:

{
"phoneNumber":"4791234567",
"interval": {
"unit": "MONTH",
"count": 1
},
"merchantRedirectUrl": "https://example.com/confirmation",
"merchantAgreementUrl": "https://example.com/my-customer-agreement",
"pricing": {
"type": "LEGACY",
"amount": 49900,
"currency": "NOK"
},
"productDescription": "Access to all games of English top football",
"productName": "Premier League subscription",
"scope": "address name email birthDate phoneNumber"
}

The user then consents and pays in Vipps.

Please note: This operation has an all or nothing approach, a user must complete a valid agreement and consent to all values in order to complete the session. If a user chooses to reject the terms the agreement will not be processed. Unless the whole flow is completed, this will be handled as a regular failed agreement by the recurring APIs.

Once the user completes the session a unique identifier sub can be retrieved with the GET:/recurring/v3/agreements/{agreementId} endpoint, alongside the full URL to Userinfo.

Example sub and userinfoUrl format:

{
"sub": "c06c4afe-d9e1-4c5d-939a-177d752a0944",
"userinfoUrl": "https://api.vipps.no/vipps-userinfo-api/userinfo/c06c4afe-d9e1-4c5d-939a-177d752a0944"
}

This sub is a link between the merchant and the user and can be used to retrieve the user's details from the GET:/vipps-userinfo-api/userinfo/{sub} endpoint.

The sub is based on the user's national identity number ("fødselsnummer" in Norway), and does not change (except in very special cases).

Please note: It is recommended to get the user's information directly after completing the transaction. There is however a time limit of 168 hours (one week) to retrieve the consented profile data from the /userinfo endpoint to better support merchants that depend on manual steps/checks in their process of fetching the profile data. The merchant will get the information that is in the user profile at the time when they actually fetch the information. This means that the information might have changed from the time the user completed the transaction and the fetching of the profile data.

Userinfo call

This endpoint returns the payload with the information that the user has consented to share.

Call the GET:/vipps-userinfo-api/userinfo/{sub} endpoint with the sub that was retrieved earlier.

See the Userinfo API guide for details.

Recurring agreements with variable amount

Recurring with variable amounts offer merchants a way to charge users a different amount each payment, as long as the amount is lower than the user's specified max amount.

To create a variable amount agreement, use the VARIABLE type in Pricing. With VARIABLE pricing, you no longer specify a price, but a suggestedMaxAmount for the user. This field should be set to what the maximum price could be each payment. This suggestedMaxAmount is presented to the user together with a list of auto generated amount suggestions that is created by Vipps. The suggestedMaxAmount is however pre-selected for the user.

The user chooses a max amount themselves when accepting the agreement, but we recommended the user to choose the same amount as suggestedMaxAmount. The max amount can at any time be changed by the user. What the user has picked as their max amount will be available in the GET agreement response. Its recommended that when you set the suggestedMaxAmount, that you set a realistic amount - as setting it to unrealistic amounts might scare off the user when they accept the agreement.

Create agreement

Create an agreement and specify that it's with variableAmount and set a suggestedMaxAmount (in øre).

Create agreement request:

{
"pricing": {
"suggestedMaxAmount": 200000,
"currency": "NOK",
"type": "VARIABLE"
},
"interval": {
"unit" : "MONTH",
"count": 1
},
"merchantRedirectUrl": "https://example.com/confirmation",
"merchantAgreementUrl": "https://example.com/my-customer-agreement",
"phoneNumber": "4791234567",
"productDescription": "Access to subscription",
"productName": "Power company A"
}

Restrictions when using variable amount:

  • There is currently a limit of 20 000 NOK for the suggestedMaxAmount.
  • Campaign can not be used when the agreement has variableAmount.

The user will be presented with the variable agreement in the Vipps or MobilePay app, where they can change the max amount they allow to be charged each interval.

In the example below, the suggestedMaxAmount is 5000 kr, this amount gets pre-selected in the app. The user clicks max amount and opens the list of auto generated suggestions together with the suggestedMaxAmount. The text above the list explains that the merchant recommends the user to set their max amount to 5000 kr. The user proceeds with 5000 kr and accepts the agreement.

Please note: The auto generated list is based on the suggestedMaxAmount and can not be changed by the merchant individually. It will however change if suggestedMaxAmount changes, which can be done in the PATCH agreement endpoint.

Accepting agreement in Vipps: variable_amount_accept

Variable amount and initial charge can be combined: variable_amount_accept_initial

Get agreement

Retrieving the agreement shows the maxAmount picked by the user.

GET:/recurring/v3/agreements/{agreementId} response:

{
"id": "agr_Yv2zYk3",
"start": "2021-06-18T19:56:22Z",
"stop": null,
"status": "ACTIVE",
"productName": "Power company A",
"pricing": {
"type": "VARIABLE",
"suggestedMaxAmount": 500000,
"maxAmount": 1800000,
"currency": "NOK"
},
"productDescription": "Access to subscription",
"interval": {
"unit": "MONTH",
"count": 1,
"text": "hver måned"
},
"campaign": null,
"sub": null,
"userinfoUrl": null
}

Change suggestedMaxAmount

It's possible to change the suggestedMaxAmount on the agreement by calling the update agreement endpoint with the PATCH:/recurring/v3/agreements/{agreementId} request below.

{
"suggestedMaxAmount": 300000
}

Please note: The user will not be alerted by this change.

Create charge

The amount of the charge/charges in the interval can not be higher than the suggestedMaxAmount or maxAmount field, depending on which is highest.

Examples:

  • If suggestedMaxAmount is set to 5000 kr and maxAmount chosen by the user is 2000 kr, then the charge amount can not be higher than 5000 kr
  • If suggestedMaxAmount is set to 5000 kr and maxAmount chosen by the user is 7000 kr, then the charge amount can not be higher than 7000 kr

Charge amount higher than the user's max amount

If the amount of a charge is below (or equal) the suggestMaxAmount but above the user's maxAmount, the charge will be set to DUE and the user will be notified and encouraged to alter the max amount to a higher amount. If the user does not update their maxAmount to the same or a higher amount than the charge, it will fail when due + retryDays is reached, and the status will be FAILED.

The user will also see a failure description on the charge in the Vipps or MobilePay app.

Display of charge failure due to a charge being higher than the maxAmount in the app: variable_amount_charge

Skip landing page

This functionality is only available for special cases.

See: Landing page

If the skipLandingPage property is set to true in the POST:/recurring/v3/agreements call, it will cause a push notification to be sent to the given phone number immediately, without loading the landing page.

If the sales unit is not whitelisted, the request will fail and an error message will be returned.

HTTP responses

This API returns the following HTTP statuses in the responses:

HTTP statusDescription
200 OKRequest successful
204 No contentRequest successful
202 AcceptedRequest accepted, indicates that the request has been accepted for processing, but the processing has not been completed yet.
400 Bad RequestInvalid request, see the error for details
401 UnauthorizedInvalid credentials
403 ForbiddenAuthentication ok, but credentials lacks authorization
404 Not FoundThe resource was not found
409 ConflictUnsuccessful due to conflicting resource
422 Unprocessable EntityVipps MobilePay could not process
429 Too Many RequestsLook at table below to view current rate limits
500 Server ErrorAn internal Vipps MobilePay problem.

Please note: Responses might include a Retry-After-header that will indicate the earliest time you should retry the request or poll the resource to see if an operation has been performed. This will follow the spec, and will either be a http-date or a number indicating a delay in seconds. This will mostly apply to 429 responses, but may also appear in certain other circumstances where it would be natural to retry the request at a later point in time.

Error responses

HTTP responses for errors follow the RFC 7807 standard. For more details about error types, see Knowledge base: Errors.

Rate limiting

We have added rate-limiting to our API (HTTP 429 Too Many Requests) to prevent fraudulent and wrongful behavior, and increase the stability and security of our API. The limits should not affect normal behavior, but please contact us if you notice any unexpected behavior.

The "Key" column specifies what we consider to be the unique identifier, and what we "use to count". The limits are of course not total limits.

APILimitKey usedExplanation
CreateCharge2 per minuteagreementId + chargeId (based on idempotency key)Two calls per minute per unique agreementId and chargeId
CancelCharge5 per minuteagreementId + chargeIdFive calls per minute per unique agreementId and chargeId
CaptureCharge5 per minuteagreementId + chargeIdFive calls per minute per unique agreementId and chargeId
RefundCharge5 per minuteagreementId + chargeIdFive calls per minute per unique agreementId and chargeId
ListAgreements5 per minute(per merchant)Five calls per minute per merchant
UpdateAgreement5 per minuteagreementIdFive calls per minute per unique agreementId
FetchCharge10 per minuteagreementId + chargeIdTen calls per minute per unique agreementId and chargeId
FetchChargeById100 per minutechargeIdHundred calls per minutes per merchant
ListCharges10 per minuteagreementIdTen calls per minute per unique agreementId
FetchAgreement120 per minuteagreementId120 calls per minute per unique agreementId
DraftAgreement300 per minute(per merchant)300 calls per minute per merchant

Please note: The "Key" column is important. The above means that we allow two CreateCharge calls per minute per unique agreementId and chargeId. This is to prevent too many CreateCharge calls for the same charge. The overall limit for number of different payments is far higher than 2.

Please note: Responses might include a Retry-After-header that will indicate the earliest time you should retry the request or poll the resource to see if an operation has been performed. This will follow the spec as defined here, and will either be a http-date or a number indicating a delay in seconds. This will mostly apply to 429 responses, but may also appear in certain other circumstances where it would be natural to retry the request at a later point in time.

Partner keys

In addition to the normal Authentication we offer partner keys, which let a partner make API calls on behalf of a merchant.

If you are a Vipps MobilePay partner who is managing agreements on behalf of Vipps MobilePay merchants, you can use your own API credentials to authenticate, and then send the Merchant-Serial-Number header to identify which of your merchants you are acting on behalf of. The Merchant-Serial-Number must be sent in the header of all API requests.

By including the HTTP Headers you will make it easier to investigate problems, if anything unexpected happens. Partners may re-use the values of the Vipps-System-Name and Vipps-System-Plugin-Name in the plugins headers if having different values do not make sense.

Here's an example of headers (please refer to the Recurring API specification for all the details):

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1Ni <snip>
Ocp-Apim-Subscription-Key: 0f14ebcab0ec4b29ae0cb90d91b4a84a
Merchant-Serial-Number: 123456
Vipps-System-Name: acme
Vipps-System-Version: 3.1.2
Vipps-System-Plugin-Name: acme-pos
Vipps-System-Plugin-Version: 4.5.6
Content-Type: application/json

Please note: The Merchant Serial Number (MSN) is a unique ID for the sale unit. This is a required parameter if you are a Vipps MobilePay partner making API requests on behalf of a merchant. The partner must use the merchant's MSN, not the partner's MSN. This parameter is also recommended for regular Vipps MobilePay merchants making API calls for themselves.

Polling guidelines

General guidelines for polling with the GET:/recurring/v3/agreements/{agreementId} endpoint can be found at: Knowledge base: Polling guidelines. See also Knowledge base: Timeouts.

Notifications to users for failed charges

We notify the user in two ways, through push notifications and failure texts on the charge. When a charge fails to be processed, we send the user a push notification letting them know the charge failed to process. This push message is sent every time we try to process the charge, see Charge attempts for when the processing of charges happen. More information about how and when we send push notifications can be found in the Recurring API FAQ.

Please note: We send push notification for failed payments regardless if Notification upon payment is toggled on or off on the agreement. This toggle only determine if the user will get notified when a charge is successfully charged.

In addition to sending push notifications, a failure texts is also set on the charge. By letting the customer know why the charge failed we enable them to fix the underlying issue before the retryDays are over. For more information about what the failure texts are, see the FAQ.

Please note: These exact reasons why the charge fails is not shown in the recurring API, only to the customers (users). See charge failure reason for an overview of what is available in the merchant API.

Testing

To facilitate automated testing in the Test Environment (MT), the Recurring API provides a force accept agreement endpoint to avoid manual agreement acceptance in the Vipps or MobilePay app:

The force approve endpoint allows developers to approve a payment through the Recurring API without the use of the Vipps or MobilePay app. This is useful for automated testing. The endpoint is only available in our test environment.

Handling redirects

You can redirect the user to a website or app once they have used the Vipps or MobilePay app to log in to your site, complete a payment, accept an agreement, or similar.

We have limited control over the redirect back to the merchant's website after a completed purchase or log-in. Your integration must not assume that the app will redirect to the exact same session. For example, don't rely entirely on cookies in order to handle the redirect event. The redirect may send the user to a different web browser

For more details, see Knowledge base: Recommendations regarding handling redirects.

Different agreement types and when to use them

Vipps MobilePay recurring payments is a fairly flexible service that allows merchants to tailor the user experience in the Vipps or MobilePay app by utilizing the normal agreements, initial charges, campaigns, or a combination of those.

This can be a bit confusing when deciding on which implementation to use. In short, our advice is to implement support for all our flows, and also implement features in your own systems for moving between the flows depending on the use case.

Normal agreement

Normal_agreement

This is the preferred flow whenever there is no campaign or no required payment on the start of an agreement.

In the normal agreement, the user gets presented with the agreement, agrees to it, and gets sent to a confirmation screen. On the agreement we present the start date, the price of the agreements, the productName and the product description which are all defined by the merchant. We also present an agreement explanation which is used to describe the agreement interval to the user. For example, for an agreement with interval.unit=YEAR and interval.count=1, the agreement explanation will be hvert år til du sier opp or every year until cancelled

Agreement with initial charge

flow_Initial_charge

If you require a payment to be completed at the same time that the agreement is created, you must use initial charge.

When an initial charge is present and the amount is different from the agreement price (or campaign price), the flow in the app will change. First the user gets presented with an overview over both the agreement and the initial charge. Then, when the user proceeds to confirm the agreement, the payment of the initial charge will be processed.

Here we also show productName and the agreement explanation on the agreement, as well as description on the initial charge. productName and initial charge description are defined by the merchant. The agreement explanation is created by automatically based on the interval and the campaign, if specified.

Initial charges are designed to be used whenever there is an additional cost in setting up the agreement. This could be bundling of a mobile phone together with a mobile subscription, or a TV setup-box when becoming a customer at a cable company. We do not recommend this flow to be used purely for campaigns, as it could be confusing to the user.

As an example: If you have a campaign of 10 NOK for a digital media subscription for 3 months, and the normal price is 299,- monthly, the user would see both the charge of 10 NOK, and have to confirm the agreement for 299,- monthly, which can lead the user to believe that both will be paid upon entering the agreement.

Agreement with campaign

This is the preferred flow whenever you have a type of campaign where the subscription has a certain price for a certain interval or time, before it switches over to ordinary price.

See Campaigns and How it works: Campaigns for details about campaigns.

When setting a campaign, this follows the normal agreement flow - with some changes. Instead of showing the ordinary price of the agreement, the campaign price will override this, and the ordinary price will be shown below together with information about when the change from the campaign price to the ordinary price will happen.

Please note: Campaign is not supported for variableAmount agreements.

Agreement with initial charge and campaign

Ideally, this flow is intended for when you have a combination of an additional cost when setting up the agreement, presented as the initial charge, as well as having a limited time offer on the actual subscription.

In addition to campaigns and initial charges being available as individual flows, they can also be combined. In this case, the user would see first a summary of both the agreement, including the campaign as described in the sections on campaigns, as well as the initial charge. Again, all fields described in previous flows are available for the merchant to display information to the user.

Agreement screens with initial and campaign v2 screen_initial_charge_legacy_campaign

Agreement screens with initial and campaign v3 screen_initial_charge_legacy_campaign

Webhooks integration

You can receive instant notifications about important events, such as successful payments and agreement cancellations. To set up the basic webhook infrastructure, you need to register your webhook URL, as described in the Webhooks API guide. We'll send the real-time notifications about subscription events to the URL you specify.

Event types of payment webhooks:

Event typeDescription
recurring.charge-reserved.v1Charge was reserved. The event is not sent for recurring charges with transaction type DIRECT_CAPTURE.
recurring.charge-captured.v1Charge was fully or partially captured.
recurring.charge-canceled.v1Charge was fully or partially cancelled.
recurring.charge-failed.v1Charge failed and will no longer be retried.
recurring.charge-creation-failed.v1Charge failed to be created asynchronously.

Available fields of payment webhooks:

Field nameTypeDescriptionPossible values
agreementIdstringID of an agreementagr_kFW4chk
chargeExternalIdnullable stringMerchant provided external ID of chargeExtId123
chargeIdstringID of a charge82ce990f-d08a-448c-bd26-ee6be8418d06
amountnumberAmount of charge in cents300
chargeTypeenumIndicates type of chargeRECURRING, INITIAL, UNSCHEDULED
eventTypeenumIndicates what has happened to a chargeValues provided in a table above
currencyenumCurrency of chargeDKK, NOK, EUR
occurredISO 8601 UTC dateWhen change has occurred2023-10-10T13:30:36.079765975Z
amountCapturednumberAmount of payment that was captured100
amountCancelednumberAmount of charge that was canceled200
amountRefundednumberAmount of charge that was refunded100
failureCodenumberCode of an error during async creationListed below
failureTextstringExplanation of an error during async creationListed below

Possible failureCode and failureText combinations. These fields are only present if charge failed async validation after it was created using multiple charges creation endpoint POST:/recurring/v3/agreements/charges

Failure codeFailure textExplanation
50006DeclinedBySystemUnspecified exception during charge creation
50003AgreementNotActiveCharge is requested for non active agreement
70001ChargeAmountTooHighForFixedAmountAgreementCharge amount is 5 times higher than fixed amount agreement's amount
70002ChargeCreationConflictTrying to create charge with same idempotency key more than once
70004ChargeTooFarInFutureDue date is too far in the future
70005ChargeDueDateTooSoonDue date is too soon

This is an example of payment webhook

{
"agreementId": "agr_kFW4chk",
"chargeExternalId": "extId",
"chargeId": "82ce990f-d08a-448c-bd26-ee6be8418d06",
"amount": 300,
"chargeType": "RECURRING",
"eventType": "recurring.charge-canceled.v1",
"currency": "NOK",
"occurred": "2023-10-10T13:30:36.079765975Z",
"amountCaptured": 0,
"amountCanceled": 300,
"amountRefunded": 0,
"failureCode" null,
"failureText" null
}

These are the possible event types of agreement webhook:

Event typeDescription
recurring.agreement-activated.v1User has accepted agreement
recurring.agreement-rejected.v1User has rejected agreement
recurring.agreement-stopped.v1Agreement was stopped either by merchant either by user
recurring.agreement-expired.v1Agreement has expired

These are the available fields of agreement webhook

Field nameTypeDescriptionPossible values
agreementIdstringID of an agreementagr_kFW4chk
agreementUUIDUUIDID of an agreement82ce990f-d08a-448c-bd26-ee6be8418d06
agreementExternalIdnullable stringMerchant provided external ID of agreementExtId123
eventTypeenumIndicates what has happened to an agreementValues provided in a table above
occurredISO 8601 UTC dateWhen change has occurred2023-10-10T13:30:36.079765975Z
actornullable enumIndicates who has initiated action. Applicable only for recurring.agreement-stopped.v1 webhook.MERCHANT, USER

Here are examples of new agreement webhooks:

{
"agreementId": "agr_hXbXJUN",
"occurred": "2023-10-11T09:51:04.562829303Z",
"agreementExternalId": null,
"eventType": "recurring.agreement-expired.v1",
"agreementUUID": "c81bf516-7972-488e-bbf1-146dcd8592f9",
"actor": null
}
{
"agreementId": "agr_hXbXJUN",
"occurred": "2023-10-11T09:51:04.562829303Z",
"agreementExternalId": null,
"eventType": "recurring.agreement-stopped.v1",
"agreementUUID": "c81bf516-7972-488e-bbf1-146dcd8592f9",
"actor": "MERCHANT"
}

Help us improve our documentation

Did you find what you were looking for?