This guide is for merchants who create and charge subscriptions through the Vipps MobilePay Recurring API (/recurring/v3). It shows how to replace each Recurring API call with its Kustom equivalent, and how to move the agreements you already have to Kustom without asking your customers to sign up again.
In Kustom, a subscription has two parts:
- The customer completes a normal Kustom Checkout order with
recurring: true. That order carries arecurring_token, which plays the role of the VippsagreementId. - You charge the token with the Customer Token API (
/customer-token/v1) whenever a payment is due. Each charge creates an ordinary Kustom order that you manage with the Order Management API.
Note: If you also use Vipps Checkout for one-off purchases, follow Migrating from Vipps Checkout to Kustom Checkout as well. This guide covers only the recurring part.
| Date | What happens |
|---|---|
| 18 November 2026 | Vipps goes live as a recurring payment method in Kustom Checkout. You can create new Vipps tokens and charge them through Kustom. |
| 21 February 2027 | Last day to create new Vipps agreements outside Kustom. From 22 February 2027, every new Vipps token is created through Kustom. |
| 8 March 2027 | Your existing Vipps agreements become chargeable through Kustom. |
| 30 April 2027 | All Vipps recurring charges must run through Kustom from this date. |
Your existing agreements remain valid throughout the migration. The plan is to move them to Kustom without asking customers to sign up again. Step 9 describes how.
The Kustom recurring API is already live. Today a recurring checkout order offers card, Apple Pay, Google Pay, invoice, Klarna and Amazon Pay. You can build and test your integration against these in the Playground now.
Until 18 November 2026, a recurring checkout order does not offer Vipps or MobilePay, so no Kustom token is backed by Vipps. Some Vipps-specific details are not confirmed yet, such as the payment_method_type a Vipps token returns. Kustom will update this guide before Vipps recurring goes live.
| Dimension | Vipps Recurring API | Kustom |
|---|---|---|
| Auth method | Access token + several Ocp-Apim-* / Merchant-Serial-Number / Vipps-System-* headers | HTTP Basic Auth (username:password) |
| Base URL (test) | https://apitest.vipps.no | https://api.playground.kustom.co |
| Base URL (prod) | https://api.vipps.no | https://api.kustom.co |
| What represents the subscription | Agreement (agreementId) | Customer token (recurring_token) |
| Create | POST /recurring/v3/agreements | POST /checkout/v3/orders with recurring: true |
| Customer consent | Redirect to vippsConfirmationUrl, approve in the app | Complete the Kustom Checkout widget |
| First payment | initialCharge on the agreement | The order lines of the recurring checkout order |
| Charge | POST /recurring/v3/agreements/{agreementId}/charges | POST /customer-token/v1/tokens/{recurring_token}/order |
| When the charge runs | On the due date that Vipps schedules | Immediately, when you call the API |
| Retries | Vipps retries for retryDays | You retry on your own schedule |
| Capture model | transactionType: DIRECT_CAPTURE or RESERVE_CAPTURE | auto_capture: true, or capture later via Order Management |
| Read status | GET /recurring/v3/agreements/{agreementId} | GET /customer-token/v1/tokens/{recurring_token} |
| Stop | PATCH /recurring/v3/agreements/{agreementId} with "status": "STOPPED" | PATCH /customer-token/v1/tokens/{recurring_token}/status with "status": "CANCELLED" |
| Capture, cancel, refund a charge | /recurring/v3/agreements/{agreementId}/charges/{chargeId}/... | /ordermanagement/v1/orders/{order_id}/... |
| Idempotency | Idempotency-Key header on every call | No idempotency key on the token charge. Klarna-Idempotency-Key on Order Management calls. |
| Amounts | Minor units (øre / cent) | Minor units (øre / cent) |
Every Recurring API call carries an access token from POST /accesstoken/get plus a set of merchant headers:
Authorization: Bearer <access-token>
Ocp-Apim-Subscription-Key: <your-subscription-key>
Merchant-Serial-Number: <your-msn>
Idempotency-Key: <unique-key>
Vipps-System-Name: <your-system-name>
Vipps-System-Version: <version>
Vipps-System-Plugin-Name: <your-plugin-name>
Vipps-System-Plugin-Version: <version>Kustom uses standard HTTP Basic Auth with a username and password tied to your Kustom Merchant ID (MID). There is no token exchange.
# Encode once and store as a constant
echo -n "username:password" | base64Authorization: Basic <base64(username:password)>
Content-Type: application/json| Environment | Vipps | Kustom |
|---|---|---|
| Test (Playground) | https://apitest.vipps.no | https://api.playground.kustom.co |
| Production | https://api.vipps.no | https://api.kustom.co |
Get your credentials from the Kustom Merchant Portal. Generate separate credentials for Playground and production.
Recurring must be enabled on your MID. Contact Kustom merchant support to turn on recurring payments before you start testing. Ask for Vipps to be included as a recurring payment method once it is available.
From 22 February 2027, every new Vipps token is created through Kustom. Switch your sign-up flow to the Kustom recurring checkout below, and stop creating agreements with POST /recurring/v3/agreements after 21 February 2027.
{
"pricing": {
"type": "LEGACY",
"amount": 29900,
"currency": "NOK"
},
"interval": {
"unit": "MONTH",
"count": 1
},
"initialCharge": {
"amount": 29900,
"description": "First month",
"transactionType": "DIRECT_CAPTURE",
"orderId": "sub-1001-0"
},
"merchantRedirectUrl": "https://example.com/subscription/result",
"merchantAgreementUrl": "https://example.com/my-subscriptions",
"productName": "Coffee subscription",
"productDescription": "1 kg of coffee every month",
"phoneNumber": "4791234567",
"externalId": "sub-1001"
}Response:
{
"agreementId": "agr_5kSeqz",
"uuid": "a3a7a0e3-3b6b-4b0e-8e0a-6f1d2e2c9b11",
"vippsConfirmationUrl": "https://api.vipps.no/v2/register/U6JUjQXq8HQmmV",
"chargeId": "sub-1001-0"
}You redirect the customer to vippsConfirmationUrl. The agreement stays PENDING until the customer approves it in the app, and then becomes ACTIVE.
In Kustom, the sign-up is a normal checkout order. Add recurring: true and render the checkout as usual. The customer approves future charges in the checkout widget.
{
"purchase_country": "NO",
"purchase_currency": "NOK",
"locale": "nb-NO",
"order_amount": 29900,
"order_tax_amount": 5980,
"recurring": true,
"merchant_reference1": "sub-1001-0",
"merchant_reference2": "sub-1001",
"order_lines": [
{
"type": "physical",
"reference": "COFFEE-1KG",
"name": "Coffee subscription, first month",
"quantity": 1,
"quantity_unit": "pcs",
"unit_price": 29900,
"tax_rate": 2500,
"total_amount": 29900,
"total_discount_amount": 0,
"total_tax_amount": 5980
}
],
"options": {
"subscription_buy_button": true
},
"merchant_urls": {
"terms": "https://example.com/terms",
"checkout": "https://example.com/checkout?kustom_order_id={checkout.order.id}",
"confirmation": "https://example.com/subscription/result?kustom_order_id={checkout.order.id}",
"push": "https://example.com/api/kustom/push?kustom_order_id={checkout.order.id}"
}
}The response contains order_id and html_snippet. Render the snippet exactly as for any Kustom Checkout order. If you have not done this before, see the frontend step in Migrating from Vipps Checkout to Kustom Checkout.
recurring: truetells Kustom to create a token when the customer completes the order. Only payment methods that can be charged again are offered. Financing, Vipps, MobilePay and external payment methods are not.options.subscription_buy_buttonchanges the buy button to show that the customer is subscribing. It only works together withrecurring: true.- The consent text is Kustom's. The checkout tells the customer that automatic payments are being set up with your store, using your merchant name. You don't supply this text.
- There is no interval on the token. Your own system decides when to charge. Kustom doesn't store the price or the interval.
Sign-up without a first payment. Vipps lets you create an agreement without
initialCharge. The Kustom docs don't confirm that a recurring checkout order can haveorder_amount: 0. Check with Kustom merchant support before you design a free trial around it.
| Vipps field | Kustom field | Notes |
|---|---|---|
pricing.amount | (none on the token) | Kustom tokens have no stored price. You send the amount on every charge. |
pricing.currency | purchase_currency | ISO 4217. A token can only be charged in a currency its payment method supports. |
pricing.type: VARIABLE, suggestedMaxAmount | (none) | Kustom has no customer-chosen maximum. Tell the customer about amount changes yourself. |
interval.unit, interval.count | (none) | The schedule lives in your system. Kustom doesn't store an interval. |
productName | order_lines[].name | |
productDescription | (none) | The checkout shows its own consent text |
initialCharge.amount | order_amount and order_lines of the checkout order | |
initialCharge.description | order_lines[].name | |
initialCharge.transactionType | options.auto_capture on the checkout order | Or capture later with Order Management |
initialCharge.orderId | merchant_reference1 | Your reference for the first payment |
externalId | merchant_reference2 | Your subscription ID |
merchantRedirectUrl | merchant_urls.confirmation | Browser redirect after the customer completes |
merchantAgreementUrl | (none) | Keep your "My subscriptions" page. Link to it from your own emails. |
phoneNumber | billing_address.phone | Optional prefill |
scope | (none) | The completed order already contains the billing address, email and phone |
campaign | A discount order line on the relevant charge | Kustom has no campaign object. Apply the discounted price when you charge. |
countryCode | purchase_country | NO, DK or FI |
isApp, skipLandingPage | (none) | The checkout handles the app switch for Vipps |
You poll GET /recurring/v3/agreements/{agreementId} or wait for an agreement webhook until the status is ACTIVE. Then you store the agreementId.
When the customer completes the checkout, Kustom calls your merchant_urls.push URL. Your push handler should:
- Read
kustom_order_idfrom the query string. - Fetch the order with
GET /checkout/v3/orders/{order_id}and readrecurring_token. - Store
recurring_tokenagainst the customer's subscription in your system. - Acknowledge the order with
POST /ordermanagement/v1/orders/{order_id}/acknowledge. - Return
HTTP 200.
{
"order_id": "8cf27b55-53e8-6aba-9fb4-7c692e56ddee",
"status": "checkout_complete",
"recurring": true,
"recurring_token": "8b7c6e1a-2f3d-4a5b-9c8d-7e6f5a4b3c2d",
"order_amount": 29900
}Treat the token as a credential. Store it where you would store a card token. A token can only be charged by the MID that created it.
The acknowledge call is the same as for any checkout order. The callback handler step in Migrating from Vipps Checkout to Kustom Checkout shows the full push handler.
{
"amount": 29900,
"transactionType": "DIRECT_CAPTURE",
"description": "Coffee subscription, March",
"due": "2027-03-15",
"retryDays": 5,
"orderId": "sub-1001-3",
"externalId": "sub-1001"
}Vipps queues the charge. It must be created at least two days before due in production, and Vipps attempts it on the due date and for retryDays after.
Kustom charges the token the moment you call it. Each call is one synchronous payment. There is no queue, no due date and no pending state.
POST https://api.kustom.co/customer-token/v1/tokens/8b7c6e1a-2f3d-4a5b-9c8d-7e6f5a4b3c2d/order
Authorization: Basic <base64>
Content-Type: application/json{
"purchase_currency": "NOK",
"locale": "nb-NO",
"order_amount": 29900,
"order_tax_amount": 5980,
"auto_capture": true,
"merchant_reference1": "sub-1001-3",
"merchant_reference2": "sub-1001",
"order_lines": [
{
"type": "physical",
"reference": "COFFEE-1KG",
"name": "Coffee subscription, March",
"quantity": 1,
"quantity_unit": "pcs",
"unit_price": 29900,
"tax_rate": 2500,
"total_amount": 29900,
"total_discount_amount": 0,
"total_tax_amount": 5980
}
]
}Response:
{
"order_id": "a89ec121-1276-419d-882a-c343d58fd1bc",
"fraud_status": "ACCEPTED",
"authorized_payment_method": {
"type": "card"
}
}Store the order_id. You need it to capture, cancel or refund the charge.
- A
200means the charge went through. The order is authorized straight away. Withauto_capture: trueit is also captured.fraud_statusis alwaysACCEPTED, so there is nothing to branch on. - A failed charge is an error response, not a pending order. See Step 6.
redirect_urlis only returned if you sendmerchant_urls.confirmation, and it echoes that URL.- A
shipping_address, if you send one, must be complete:given_name,family_name,email,street_address,postal_code,cityandcountry. A missing field is rejected withBAD_VALUE.
| Vipps field | Kustom field | Notes |
|---|---|---|
agreementId (path) | recurring_token (path) | |
amount | order_amount | Must equal the sum of order_lines[].total_amount |
| (currency set on the agreement) | purchase_currency | Required on every charge |
description | order_lines[].name | Visible to the customer |
transactionType: DIRECT_CAPTURE | auto_capture: true | |
transactionType: RESERVE_CAPTURE | auto_capture: false (default) | Capture later with Order Management, see Step 5 |
due | (none) | Call the API on the day you want to charge |
retryDays | (none) | See Step 6 |
orderId | merchant_reference1 | Use one unique value per billing period |
externalId | merchant_reference2 | |
type: UNSCHEDULED | (no difference) | Every Kustom token charge is on demand |
Idempotency-Key header | (none) | The token charge takes no idempotency key. See Step 6 before you retry. |
A Vipps charge is managed under the agreement. A Kustom token charge is an ordinary order, so you manage it with the Order Management API using the returned order_id.
| Action | Vipps | Kustom |
|---|---|---|
| Read the charge | GET /recurring/v3/agreements/{agreementId}/charges/{chargeId} | GET /ordermanagement/v1/orders/{order_id} |
| Capture a reserved charge | POST /recurring/v3/agreements/{agreementId}/charges/{chargeId}/capture | POST /ordermanagement/v1/orders/{order_id}/captures |
| Cancel a charge | DELETE /recurring/v3/agreements/{agreementId}/charges/{chargeId} | POST /ordermanagement/v1/orders/{order_id}/cancel |
| Refund a charge | POST /recurring/v3/agreements/{agreementId}/charges/{chargeId}/refund | POST /ordermanagement/v1/orders/{order_id}/refunds |
Capture a charge created with auto_capture: false:
POST https://api.kustom.co/ordermanagement/v1/orders/a89ec121-1276-419d-882a-c343d58fd1bc/captures
Authorization: Basic <base64>
Content-Type: application/json
Klarna-Idempotency-Key: sub-1001-3-capture{
"captured_amount": 29900,
"description": "Coffee subscription, March"
}| Vipps charge status | Kustom equivalent |
|---|---|
PENDING, DUE, PROCESSING | No equivalent. The charge does not exist in Kustom until you call the API. |
RESERVED | Order status AUTHORIZED |
CHARGED | Order status CAPTURED |
PARTIALLY_CAPTURED | Order status PART_CAPTURED |
FAILED | The create call returns an error, see Step 6 |
CANCELLED | Order status CANCELLED |
REFUNDED, PARTIALLY_REFUNDED | refunded_amount on the order is greater than zero |
Vipps runs the charge on the due date and retries it for you. Kustom does neither, so your billing job must:
- Decide which subscriptions are due today.
- Charge each token once per billing period, and record in your own system that you did.
- Handle the result. On
PAYMENT_METHOD_FAILED, retry later. If the retries also fail, ask the customer to update their payment method.
Only some errors from POST /customer-token/v1/tokens/{recurring_token}/order have a body, so check the HTTP status code first:
| HTTP status | Body | What to do |
|---|---|---|
| 400 | error_code: BAD_VALUE | A field is invalid, and error_messages names it. Also returned when the token belongs to another merchant ID (Token do not belong to merchant). Fix the request. Do not retry it unchanged. |
| 400 | Empty | The token is cancelled, or recurring is not enabled on your merchant account. Retrying will not help. |
| 403 | error_code: PAYMENT_METHOD_FAILED | The payment did not go through. The body has reason and correlation_id. Retry later, then ask the customer to update their payment method. |
| 404 | Empty | The token was not found. Check the token value and that you are calling the right environment. |
| 500 | Empty | The order may or may not have been created. Check before you charge again, so the customer isn't charged twice. |
See Place order from customer token for the full reference.
Updating the payment method. To replace a failing payment method, send the customer through a new recurring checkout order (Step 2). Store the new
recurring_token, then cancel the old one (Step 7).
GET /recurring/v3/agreements/{agreementId}returnsPENDING,ACTIVE,STOPPEDorEXPIRED.PATCH /recurring/v3/agreements/{agreementId}with{"status": "STOPPED"}stops the agreement.- The same
PATCHcan changeproductName,pricingorinterval.
Read a token:
GET https://api.kustom.co/customer-token/v1/tokens/{recurring_token}
Authorization: Basic <base64>{
"payment_method_type": "CARD",
"status": "ACTIVE",
"card": {
"brand": "visa",
"expiry_date": "11/2031",
"masked_number": "************4242"
}
}statusisACTIVEorCANCELLED. A token staysACTIVEuntil it is cancelled. It doesn't expire when it isn't used.payment_method_typeisCARD,INVOICE,DIRECT_DEBITorWALLET. It can be missing on a cancelled token.- Only card tokens return a
cardobject. - An unknown token returns
404with an empty body.
Check the status before a billing run rather than discovering a dead token in a failed batch.
Cancel a token:
PATCH https://api.kustom.co/customer-token/v1/tokens/{recurring_token}/status
Authorization: Basic <base64>
Content-Type: application/json{
"status": "CANCELLED"
}Kustom answers 202 Accepted with an empty body. CANCELLED is final: a cancelled token can't be made active again, and cancelling it a second time returns 400. Orders already created with the token can still be captured and refunded.
| Vipps agreement status | Kustom token status |
|---|---|
PENDING | No token yet. The checkout order is still checkout_incomplete. |
ACTIVE | ACTIVE |
STOPPED | CANCELLED |
EXPIRED | No equivalent. A token stays ACTIVE until you cancel it. |
Changing price or interval. A Kustom token has no stored price or interval, so there is nothing to patch. Update your own records and send the new amount on the next charge. Tell the customer about the change as your terms require.
Pausing a subscription. There is no pause status. Skip charges in your own billing job while the subscription is paused.
Vipps sends webhooks when agreements and charges change state. Kustom has no agreement webhooks, because nothing happens to a token without an API call from you.
| Event you listened for in Vipps | What to do in Kustom |
|---|---|
| Agreement activated | Handle the push for the recurring checkout order (Step 3). |
| Agreement stopped by the customer | Customers manage subscriptions with you. Cancel the token when they cancel in your "My subscriptions" page. |
| Charge captured, failed or cancelled | Read the result of the token order call directly (Steps 4 and 6). A token charge is never left pending. |
From 8 March 2027, your existing active Vipps agreements can be charged through Kustom. You do not create these tokens yourself. Kustom and Vipps transfer them and give you a mapping from each Vipps agreementId to a Kustom recurring_token.
How existing agreements are transferred is still being confirmed with Vipps and Kustom's payment partner. Kustom will contact each affected merchant with the delivery details and format of the token mapping before 8 March 2027. Make sure your Kustom account has an up-to-date technical contact.
Complete Steps 1 to 8 before the mapping arrives. Then cut over like this:
- Import the mapping. Store each
recurring_tokenon the matching subscription. Keep the VippsagreementIdas well, for reconciliation and support cases. - Check the tokens. Call
GET /customer-token/v1/tokens/{recurring_token}for each token and confirmstatus: ACTIVE. Spread the calls out to stay within the API rate limits. - Stop scheduling new Vipps charges. Do not create Vipps charges with a
duedate after your cut-over date. Cancel any that are already queued for later. - Switch each subscription at its next billing date. Charge it through Kustom as in Step 4. Charge each billing period through exactly one provider, so no customer is charged twice.
- Stop the Vipps agreement only after the first Kustom charge succeeds.
- Finish before 30 April 2027. From that date, all Vipps recurring charges must run through Kustom.
From 22 February 2027, every new Vipps token is created through Kustom, so customers who sign up from that date always go through the Kustom recurring checkout (Step 2).
# 1. Get an access token
curl -X POST https://api.vipps.no/accesstoken/get \
-H "client_id: $VIPPS_CLIENT_ID" \
-H "client_secret: $VIPPS_CLIENT_SECRET" \
-H "Ocp-Apim-Subscription-Key: $VIPPS_SUB_KEY" \
-H "Merchant-Serial-Number: $VIPPS_MSN"
# 2. Create a draft agreement, then redirect the customer to vippsConfirmationUrl
curl -X POST https://api.vipps.no/recurring/v3/agreements \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Ocp-Apim-Subscription-Key: $VIPPS_SUB_KEY" \
-H "Merchant-Serial-Number: $VIPPS_MSN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"pricing": { "type": "LEGACY", "amount": 29900, "currency": "NOK" },
"interval": { "unit": "MONTH", "count": 1 },
"initialCharge": { "amount": 29900, "description": "First month", "transactionType": "DIRECT_CAPTURE" },
"merchantRedirectUrl": "https://example.com/subscription/result",
"merchantAgreementUrl": "https://example.com/my-subscriptions",
"productName": "Coffee subscription"
}'
# 3. Each month: queue a charge at least two days before it is due
curl -X POST https://api.vipps.no/recurring/v3/agreements/$AGREEMENT_ID/charges \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Ocp-Apim-Subscription-Key: $VIPPS_SUB_KEY" \
-H "Merchant-Serial-Number: $VIPPS_MSN" \
-H "Idempotency-Key: sub-1001-3" \
-H "Content-Type: application/json" \
-d '{
"amount": 29900,
"transactionType": "DIRECT_CAPTURE",
"description": "Coffee subscription, March",
"due": "2027-03-15",
"retryDays": 5,
"orderId": "sub-1001-3"
}'KUSTOM_AUTH="Basic $(echo -n "$KUSTOM_USER:$KUSTOM_PASS" | base64)"
# 1. Create a recurring checkout order and render html_snippet
curl -X POST https://api.kustom.co/checkout/v3/orders \
-H "Authorization: $KUSTOM_AUTH" \
-H "Content-Type: application/json" \
-d '{
"purchase_country": "NO",
"purchase_currency": "NOK",
"locale": "nb-NO",
"order_amount": 29900,
"order_tax_amount": 5980,
"recurring": true,
"order_lines": [
{
"type": "physical",
"reference": "COFFEE-1KG",
"name": "Coffee subscription, first month",
"quantity": 1,
"unit_price": 29900,
"tax_rate": 2500,
"total_amount": 29900,
"total_tax_amount": 5980
}
],
"options": { "subscription_buy_button": true },
"merchant_urls": {
"terms": "https://example.com/terms",
"checkout": "https://example.com/checkout?kustom_order_id={checkout.order.id}",
"confirmation": "https://example.com/subscription/result?kustom_order_id={checkout.order.id}",
"push": "https://example.com/api/kustom/push?kustom_order_id={checkout.order.id}"
}
}'
# 2. On push: read recurring_token, then acknowledge
curl https://api.kustom.co/checkout/v3/orders/$ORDER_ID \
-H "Authorization: $KUSTOM_AUTH"
curl -X POST https://api.kustom.co/ordermanagement/v1/orders/$ORDER_ID/acknowledge \
-H "Authorization: $KUSTOM_AUTH" \
-H "Klarna-Idempotency-Key: sub-1001-0"
# 3. Each month, on the billing date: charge the token
curl -X POST https://api.kustom.co/customer-token/v1/tokens/$RECURRING_TOKEN/order \
-H "Authorization: $KUSTOM_AUTH" \
-H "Content-Type: application/json" \
-d '{
"purchase_currency": "NOK",
"locale": "nb-NO",
"order_amount": 29900,
"order_tax_amount": 5980,
"auto_capture": true,
"merchant_reference1": "sub-1001-3",
"order_lines": [
{
"type": "physical",
"reference": "COFFEE-1KG",
"name": "Coffee subscription, March",
"quantity": 1,
"unit_price": 29900,
"tax_rate": 2500,
"total_amount": 29900,
"total_tax_amount": 5980
}
]
}'
# → returns order_id, fraud_status and authorized_payment_methodCharges run immediately A Vipps charge is queued for its due date. A Kustom token charge is taken the moment you call the API. Do not port a job that creates Vipps charges days in advance without changing when it runs, or customers will be charged early.
No automatic retries Vipps retries a failed charge for retryDays. Kustom returns PAYMENT_METHOD_FAILED and leaves the retry to you. Build retry logic into your billing job before you move any volume.
Amounts must add up order_amount must equal the sum of order_lines[].total_amount, and order_tax_amount the sum of order_lines[].total_tax_amount. A single amount field is not enough in Kustom.
Currency on every charge Vipps stores the currency on the agreement. Kustom needs purchase_currency on every token order, and it must match what the payment method supports.
Tokens belong to one MID A recurring_token can only be used with the credentials of the MID that created it. Another MID gets 400 BAD_VALUE with Token do not belong to merchant. If you run several MIDs, store which MID each token belongs to.
Recurring must be switched on If recurring is not enabled on your MID, a token charge is rejected with 400 and an empty body. Ask Kustom merchant support to enable it on both your Playground and production MIDs.
Sign-ups that still create Vipps agreements From 22 February 2027, every new Vipps token is created through Kustom. Switch your sign-up flow to the Kustom recurring checkout (Step 2) before then.
Double charging during cut-over Charge each billing period through one provider only. Keep a record per subscription of which provider charged each period until 30 April 2027.
Stopping the Vipps agreement too early If you stop a Vipps agreement before the first Kustom charge succeeds, you have no fallback for that customer. Stop it only after the first Kustom charge succeeds.
No idempotency key on token charges Vipps deduplicates charges with Idempotency-Key. The Kustom token charge takes no idempotency key. If a charge times out or returns 500, check whether the order was created before you send it again.
Error codes are few, and most errors have no body Branch on the HTTP status code. Only BAD_VALUE and PAYMENT_METHOD_FAILED come with an error_code.
Legacy fields don't carry over You don't need recurring_description or the order-line subscription object. Kustom Checkout shows its own consent text, and the Customer Token API ignores the subscription object.
| Operation | Vipps Recurring API | Kustom |
|---|---|---|
| Authenticate | POST /accesstoken/get | HTTP Basic Auth on every call |
| Create subscription | POST /recurring/v3/agreements | POST /checkout/v3/orders with recurring: true |
| Read subscription | GET /recurring/v3/agreements/{agreementId} | GET /customer-token/v1/tokens/{recurring_token} |
| Stop subscription | PATCH /recurring/v3/agreements/{agreementId} | PATCH /customer-token/v1/tokens/{recurring_token}/status |
| Charge | POST /recurring/v3/agreements/{agreementId}/charges | POST /customer-token/v1/tokens/{recurring_token}/order |
| Read charge | GET /recurring/v3/agreements/{agreementId}/charges/{chargeId} | GET /ordermanagement/v1/orders/{order_id} |
| Capture charge | POST /recurring/v3/agreements/{agreementId}/charges/{chargeId}/capture | POST /ordermanagement/v1/orders/{order_id}/captures |
| Cancel charge | DELETE /recurring/v3/agreements/{agreementId}/charges/{chargeId} | POST /ordermanagement/v1/orders/{order_id}/cancel |
| Refund charge | POST /recurring/v3/agreements/{agreementId}/charges/{chargeId}/refund | POST /ordermanagement/v1/orders/{order_id}/refunds |