Skip to main content

Vipps MobilePay payment handler

Under development

This documentation is under development. Content and integration details may change as we continue internal discussions. Feedback is welcome.

  • Handler Name: com.vippsmobilepay.ucp.payment_handler
  • Version: 2026-01-23

Introduction

This payment handler supports payment creation through the ePayment API. Two payment methods are supported:

  • WALLET - For customers with a wallet. Uses push notification flow (userFlow: PUSH_MESSAGE).
  • CARD - For customers without a wallet. Uses redirect flow (userFlow: WEB_REDIRECT) where users enter card details manually.

Key benefits

  • Efficient integration with our payment services.
  • Seamless checkout experience for customers with or without a wallet.
  • Secure handling of payment credentials and transactions.

Integration guide

ParticipantIntegration Section
BusinessBusiness Integration
PlatformPlatform Integration

Participants

This payment handler covers two participants: the business for which a customer is shopping from and the platform that facilitates the checkout process (such as an agent).

  • The business is responsible for processing payments using this handler,
  • The platform offers the supported payment instrument to the customer in the checkout flow.
  • The business registers this handler and configures/implements it, and the platform discovers and utilizes the handler during checkout.
  • The business needs to have an active merchant agreement and an ecommerce sales unit registered
  • The platform needs to validate that a user has a valid wallet identity before offering this payment option. Suggestion is to utilize the Login Product to do so

Note on terminology: While this specification refers to the participant as the "business," technical schema fields may retain the standard industry nomenclature merchant_* (e.g., merchant_id). Mappings are documented below.

ParticipantRolePrerequisites
BusinessCreates a payment for the defined customerNeeds to have an active merchant agreement and ecom sales unit
PlatformCollects the customer information, along with validating that the customer has a wallet identity.Needs to validate that the user has an wallet identity

Payment flow diagram

The following diagram shows the async payment flow for this handler. The credential can be either MSISDN (phone number) or TOKEN (customer token):


Note: To create a payment, the business MUST create a Access Token using their credentials and then call the Epayment API CreatePayments endpoint to create a payment. The suggested payment userFlow is PUSH_MESSAGE, which triggers a push notification to the user's app for approval. It is also possible to have the default user flow (REDIRECT_URL), the business must then echo back the redirectUrl to the platform using the UCP status = requires_escalation with the continue_url set to the redirectUrl.

Business integration

Business prerequisites

Before advertising this handler, businesses MUST complete:

  1. Create a merchant agreement and register an Ecommerce sales unit.
  2. Implement server-side logic to generate access tokens and create payments using the Access Token and ePayment APIs.

Prerequisites Output:

FieldDescription
identity.clientIdThe client id to call the APIs with
identity.clientSecretThe secret to call the APIs with
identity.subscriptionKeyThe subscription key to call the APIs with.
merchant.merchantSerialNumberThe merchant identifier in the systems

Handler configuration

Businesses advertise support for this handler in their /.well-known/ucp profile under the payment.handlers array.

Configuration schema

Schema URL: https://ucp.vippsmobilepay.com/ucp/2026-01-23/schemas/wallet_payment_handler.json

FieldTypeRequiredDescription
merchant_serial_numberstringYesThe merchant identifier (Merchant Serial Number). This is obtained when registering an ecommerce sales unit. Format varies by country (5-6 digits typically).
environmentenumNoThe environment. Either TEST or PRODUCTION. Defaults to PRODUCTION if not specified. Use TEST for sandbox testing with test credentials.
allowed_payment_instrumentsarrayNoThe payment instruments allowed for this handler. If not specified, all instrument types are allowed. Supported types: WALLET and CARD.

Example handler declaration

{
"payment": {
"handlers": [
{
"id": "vipps_mobilepay",
"name": "com.vippsmobilepay.ucp.payment_handler",
"version": "2026-01-23",
"spec": "https://ucp.vippsmobilepay.com/ucp/2026-01-23/vipps_mp_payment_handler",
"config_schema": "https://ucp.vippsmobilepay.com/ucp/2026-01-23/schemas/wallet_payment_handler.json",
"instrument_schemas": [
"https://ucp.vippsmobilepay.com/ucp/2026-01-23/schemas/payment_instrument.json"
],
"config": {
"merchant_serial_number": "123456",
"environment": "PRODUCTION"
}
}
]
}
}

Instrument schema

Schema URL: https://ucp.vippsmobilepay.com/ucp/2026-01-23/schemas/payment_instrument.json

This payment instrument defines the structure for payments submitted by the platforms supporting this handler.

FieldTypeRequiredDescription
typeenumYesThe payment instrument type. One of WALLET or CARD.
credentialobjectYes (WALLET only)The credential information required to process WALLET payments. See Credential Schema.
return_urlstringDependsThe URL to redirect the user back to after a payment. This is required for non-push wallet flows and card. Can be a universal link or website URL.

Instrument types

TypeVipps paymentMethod.typeVipps userFlowDescription
WALLETWALLETPUSH_MESSAGE, WEB_REDIRECTWhen userFlow is PUSH_MESSAGE, this triggers a push notification to user's app. Requires customer credential (MSISDN or TOKEN). If userFlow is WEB_REDIRECT, the user must be redirected to the URL returned in the payment created call.
CARDCARDWEB_REDIRECTRedirect user to Vipps payment page to enter card details. No wallet required.

Credential schema

Schema URL: https://ucp.vippsmobilepay.com/ucp/2026-01-23/schemas/wallet_payment_credential.json

The credential object contains the buyer's wallet identifier. Two credential types are supported:

FieldTypeRequiredDescription
typeenumYesThe credential type. One of MSISDN or TOKEN.
valuestringYesThe credential value. Format depends on the credential type (see below).
MSISDN credential (public)

The MSISDN credential uses the customer's phone number. This maps to the customer.phoneNumber field in the Vipps ePayment API.

FieldDescription
valueThe phone number in MSISDN format: digits only with country code and subscriber number, no leading zeros or plus sign. Example: 4712345678 for Norwegian number +47 12 34 56 78.

Example MSISDN Instrument:

{
"type": "WALLET",
"credential": {
"type": "MSISDN",
"value": "4712345678"
}
}
TOKEN credential

The TOKEN credential uses an encoded customer token obtained from authenticated user sessions. This maps to the customer.customerToken field in the ePayment API.

FieldDescription
valueAn encoded customer token string obtained from authenticated sessions.

Example TOKEN Instrument:

{
"type": "WALLET",
"credential": {
"type": "TOKEN",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}

CARD instrument (no wallet required)

The CARD instrument allows customers who do not have a wallet to pay using their card. This uses the redirect flow where the user is sent to a payment page to enter their card details.

FieldTypeRequiredDescription
typeenumYesMust be CARD.
return_urlstringYesThe URL to redirect the user back to after payment. Can be a universal link (e.g., myapp://payment-complete) or website URL (e.g., https://example.com/payment-complete).

Example CARD Instrument:

{
"type": "CARD",
"return_url": "https://chat.vippsmobilepay.com/payment-complete?session_id=cs-ABC123XYZ"
}

Example CARD Instrument with Universal Link:

{
"type": "CARD",
"return_url": "vipps-chat://payment-complete?session_id=cs-ABC123XYZ"
}

Processing payments

Upon receiving a call for completing a checkout with this handler, businesses MUST:

  1. Validate Handler: Confirm instrument.handler_id matches an advertised handler.
  2. Ensure Idempotency: If the request is a retry (matches a previous checkout_id or idempotency key), return the previous result immediately without re-processing funds.
  3. Determine Payment Method: Based on the instrument type:
  • WALLET → Use paymentMethod: { type: "WALLET" } and userFlow: "PUSH_MESSAGE" or "WEB_REDIRECT (requires return_url)"
  • CARD → Use paymentMethod: { type: "CARD" } and userFlow: "WEB_REDIRECT"
  1. Build API Request: See WALLET Payment Flow or Redirect Payment Flow below.
  2. Call the ePayment API CreatePayment endpoint.
  3. Return Response: Based on instrument type:
  • WALLET → Return status: complete_in_progress with message to check the app
  • CARD → Return status: complete_in_progress with continue_url for platform to redirect user
  1. Handle callback and poll: When the user approves/rejects, We sends a callback. Update the checkout session accordingly. As callbacks are not guaranteed, implement polling as a backup to check payment status. It is also possible to base this on webhooks if the platform supports that.
  2. Return completed order: When the platform polls and payment is authorized, return status: completed with the order.

paymentMethod mapping

The paymentMethod.type field in the ePayment API MUST match the instrument type:

Instrument TypepaymentMethod.typeuserFlowDescription
WALLETWALLETPUSH_MESSAGEPush notification to user's app
CARDCARDWEB_REDIRECTRedirect to a payment page for card entry

See the ePayment API documentation for full details on the paymentMethod and userFlow fields.


WALLET payment flow

The PUSH_MESSAGE flow is asynchronous - the user must approve the payment in their app. This can take seconds to minutes. Businesses MUST NOT block the HTTP response waiting for user approval.

Why async?

ApproachProblem
Block HTTP requestTimeouts after 30s, user often needs more time
Short timeout (5s)User can't approve that fast
Keep connection openUnreliable, poor scalability
  1. Business creates payment → We return state: CREATED
  2. Business returns immediately with status: complete_in_progress
  3. We send push notification to user's phone
  4. User approves in app
  5. We sends callback to business with state: AUTHORIZED
  6. Business updates checkout to status: completed
  7. Platform polls and receives completed order

Complete checkout response (pending approval)

When the payment is created but awaiting user approval:

{
"id": "cs-ABC123XYZ",
"status": "complete_in_progress",
"messages": [
{
"type": "info",
"code": "payment_pending_user_approval",
"content": "A payment request has been sent to your Vipps app. Please open Vipps and approve the payment to complete your order."
}
],
"payment": {
"state": "pending_approval",
"expires_at": "2026-01-26T12:35:00Z"
},
"ucp": {
"version": "2026-01-11",
"capability": "dev.ucp.shopping.checkout"
}
}

Message codes

CodeTypeInstrumentDescription
payment_pending_user_approvalinfoWALLETPayment request sent, awaiting user approval in the app
payment_redirect_requiredinfoCARDUser must be redirected to a payment page to enter card details
payment_approvedinfoBothUser approved/completed the payment
payment_rejectederrorBothUser rejected or cancelled the payment
payment_expirederrorBothPayment request expired before user responded

Timeout handling

Businesses SHOULD implement timeout handling:

  • Default timeout: 5 minutes from payment creation
  • On timeout: Cancel the payment and set checkout to error state
  • Expose expiry: Include payment.expires_at in responses

Callback handling

Businesses MUST implement a callback endpoint to receive payment status updates.

Important: We don't guarantee callback delivery. Businesses MUST also implement polling as a backup. See Polling Guidelines.

Callback URL: Configured in MerchantInfo.callbackUrl

Callback Payload:

{
"reference": "cs-ABC123XYZ",
"state": "AUTHORIZED",
"pspReference": "1234567890"
}

Callback States:

StateAction
AUTHORIZEDUpdate checkout to completed, create order
ABORTEDUpdate checkout with error: user rejected
EXPIREDUpdate checkout with error: payment expired
TERMINATEDUpdate checkout with error: payment cancelled

Business-side polling (backup)

Since callbacks aren't guaranteed, businesses MUST implement polling as a backup mechanism to check payment status.

Polling Guidelines:

  • Start polling: 5 seconds after payment creation
  • Poll interval: Every 2 seconds
  • Endpoint: GET /epayment/v1/payments/{reference}

Example Polling Logic:

async function pollPaymentStatus(reference: string, maxAttempts = 150) {
const POLL_INTERVAL_MS = 2000; // 2 seconds
const INITIAL_DELAY_MS = 5000; // 5 seconds

// Wait 5 seconds before starting
await new Promise(resolve => setTimeout(resolve, INITIAL_DELAY_MS));

for (let attempt = 0; attempt < maxAttempts; attempt++) {
const payment = await getPayment(reference);

if (payment.state === "AUTHORIZED") {
// Payment approved - complete the order
return { success: true, state: "AUTHORIZED" };
}

if (["ABORTED", "EXPIRED", "TERMINATED"].includes(payment.state)) {
// Payment failed - update checkout with error
return { success: false, state: payment.state };
}

// Still CREATED - wait and poll again
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL_MS));
}

// Timeout - treat as expired
return { success: false, state: "TIMEOUT" };
}

Note: The callback and polling mechanisms are complementary. Whichever receives the status update first should process it, and the other should recognize that processing has already occurred (idempotency).


Redirect payment flow

This is applicable to CARD payments or WALLET payments with WEB_REDIRECT

In the payment flows that uses WEB_REDIRECT, the user is redirected to a payment page to complete their payment. This flow is for customers who do not have a wallet identity (card) or for cases where PUSH_MESSAGE is not possible.

Flow diagram

Redirect specific request fields

When creating a payment with userFlow: { type: "WEB_REDIRECT" }, the business MUST include:

FieldDescription
returnUrlThe URL to redirect the user back to after payment. Use the return_url from the instrument.

Business response

When the payment is created, we return a redirectUrl. The business MUST return this to the platform as continue_url per the UCP Continue URL specification.

The continue_url enables checkout handoff from platform to business UI, allowing the buyer to complete the payment on the Vipps MobilePay payment page. Per UCP, when status is complete_in_progress, the business is processing the Complete Checkout request and may provide a continue_url for user interaction.

{
"id": "cs-ABC123XYZ",
"status": "complete_in_progress",
"continue_url": "https://api.vipps.no/checkout/v3/session/...",
"messages": [
{
"type": "info",
"code": "payment_redirect_required",
"content": "Please complete your payment on the payment page."
}
],
"payment": {
"state": "pending_redirect",
"expires_at": "2026-01-26T12:35:00Z"
}
}

Platform redirect handling

When the platform receives a complete_in_progress response with a continue_url and message code payment_redirect_required:

  1. Redirect the user: Open the continue_url in a browser or webview to hand off to the payment page
  2. User completes payment: User enters card details on the payment page
  3. Return redirect: After payment, we redirect user to the returnUrl provided in the instrument
  4. Poll for completion: After redirect, poll GET /checkout-sessions/{id} to get the final status (completed or error)

Return URL Formats:

TypeExampleUse Case
Universal Linkvipps-chat://payment-complete?session_id=cs-ABC123XYZNative mobile app
Website URLhttps://chat.vippsmobilepay.com/payment-complete?session_id=...Web-based chat or fallback

Note: The returnUrl should include sufficient context (e.g., session_id) for the platform to resume the checkout flow after the user returns.


Example business implementation

const instrument: PaymentInstrument; // The payment instrument from the request
const checkout: Checkout; // The checkout with lineItems
const credentials: Credentials; // API Credentials
const accessToken: string; // The accessToken you received
const idempotencyKey: string; // Your idempotency key

const url = "https://apitest.vipps.no/epayment/v1/payments"; // Replace for production

/**
* Build the API request body based on instrument type.
* See: https://developer.vippsmobilepay.com/api/epayment/#tag/CreatePayments/operation/createPayment
*/
function buildPaymentRequest(instrument: PaymentInstrument, checkout: Checkout) {
const baseRequest = {
amount: {
currency: checkout.currency,
value: checkout.totals.amount
},
reference: checkout.id,
paymentDescription: `Order ${checkout.id}`
};

if (instrument.type === "WALLET") {
// WALLET: Use PUSH_MESSAGE flow with customer credential
return {
...baseRequest,
customer: buildCustomerObject(instrument.credential),
paymentMethod: {
type: "WALLET" // REQUIRED for WALLET instruments
},
userFlow: "PUSH_MESSAGE" // Or web_Redirect, then returnUrl must be set
};
} else if (instrument.type === "CARD") {
// CARD: Use WEB_REDIRECT flow, no customer credential needed
return {
...baseRequest,
paymentMethod: {
type: "CARD" // REQUIRED for CARD instruments
},
userFlow: "WEB_REDIRECT",
returnUrl: instrument.return_url // Platform provides this for redirect back
};
}

throw new Error(`Unsupported instrument type: ${instrument.type}`);
}

/**
* Build the customer object for WALLET payments based on credential type.
*/
function buildCustomerObject(credential: WalletCredential): { phoneNumber?: string; customerToken?: string } {
switch (credential.type) {
case "MSISDN":
return { phoneNumber: credential.value };
case "TOKEN":
return { customerToken: credential.value };
default:
throw new Error(`Unsupported credential type: ${credential.type}`);
}
}

const body = buildPaymentRequest(instrument, checkout);

const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${accessToken}`,
"Ocp-Apim-Subscription-Key": credentials.subscriptionKey,
"Merchant-Serial-Number": credentials.merchantSerialNumber,
"Idempotency-Key": idempotencyKey
},
body: JSON.stringify(body)
});

const payment = await response.json();

if (!response.ok) {
return mapErrorToUCPResponse(response.status, payment);
}

// Build response based on instrument type
checkout.status = "complete_in_progress";
checkout.payment = {
reference: payment.reference,
expires_at: new Date(Date.now() + 5 * 60 * 1000).toISOString()
};

if (instrument.type === "WALLET") {
// WALLET: Return message to check app
checkout.payment.state = "pending_approval";
checkout.messages = [{
type: "info",
code: "payment_pending_user_approval",
content: "A payment request has been sent to your Vipps / Mobilepay app. Please open the app and approve the payment."
}];
} else if (instrument.type === "CARD") {
// CARD: Return continue_url for platform to redirect user (per UCP spec)
checkout.payment.state = "pending_redirect";
checkout.continue_url = payment.redirectUrl; // From Vipps response, returned as continue_url
checkout.messages = [{
type: "info",
code: "payment_redirect_required",
content: "Please complete your payment on the payment page."
}];
}

return checkout;

TypeScript Types:

type InstrumentType = "WALLET" | "CARD";
type CredentialType = "MSISDN" | "TOKEN";

interface WalletCredential {
type: CredentialType;
value: string;
}

interface WalletInstrument {
type: "WALLET";
credential: WalletCredential;
return_url?: string; // Only for non push-flow
}

interface CardInstrument {
type: "CARD";
return_url: string; // Universal link or website URL
}

type PaymentInstrument = WalletInstrument | CardInstrument;

// Example API payloads:

// WALLET with MSISDN -> paymentMethod.type: "WALLET", customer.phoneNumber
const walletMsisdnPayload = {
paymentMethod: { type: "WALLET" },
userFlow: "PUSH_MESSAGE",
customer: { phoneNumber: "4712345678" },
// ... other fields
};

// WALLET with TOKEN -> paymentMethod.type: "WALLET", customer.customerToken
const walletTokenPayload = {
paymentMethod: { type: "WALLET" },
userFlow: "PUSH_MESSAGE",
customer: { customerToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." },
// ... other fields
};

// WALLET with msisdn and redirect
const walletTokenPayload = {
paymentMethod: { type: "WALLET" },
userFlow: "WEB_REDIRECT",
returnUrl: "vipps-chat://payment-complete?session_id=cs-ABC123XYZ",
// ... other fields
};

// CARD -> paymentMethod.type: "CARD", no customer field needed
const cardPayload = {
paymentMethod: { type: "CARD" },
userFlow: "WEB_REDIRECT",
returnUrl: "vipps-chat://payment-complete?session_id=cs-ABC123XYZ",
// ... other fields
};

NOTE: For WEB_REDIRECT flows, the returnUrl must be exactly the same as provided by the platform, to ensure the customer is successfully redirected back to the platform after payment approval.

Error handling

Businesses MUST follow UCP Checkout error handling: return error messages in the checkout messages array only when the platform or buyer can act on them. The severity field declares who resolves the error:

  • recoverable — The platform can fix via API (e.g. correct payment data and retry). Use for invalid instrument data the platform can correct.
  • requires_buyer_input — The business requires input that cannot be provided via API (e.g. buyer must install or link a wallet). Contributes to status: requires_escalation; platform should hand off via continue_url.

Do not return UCP error messages for implementation or backend errors on the business side (e.g. merchant misconfiguration, auth failure, internal errors). The platform cannot resolve these; the checkout should be discarded or set to a terminal state. The business may handle such errors server-side (retries, alerts, logging) at their discretion.

Map ePayment API errors (RFC 7807, extraDetails error code when present) using the table below. If the error does not appear in the mapping, treat it as a business-side/implementation error: do not surface it as a UCP message to the platform; handle server-side and treat the checkout as non-recoverable.

Note: This mapping covers only payment creation and UCP payment-handling flows.

HTTP StatusVipps Error CodeContext (Vipps title / description)UCP Error CodeUCP SeverityUser-Facing Message
400Request body validation (no error code)invalid_payment_datarecoverableThe payment request contains invalid data. Please verify and try again.
4004050Invalid CustomerTokeninvalid_customer_tokenrecoverableThe provided customer information is invalid. Please validate and try again.
4004070Invalid phone numberinvalid_phonerecoverableThe provided phone number is invalid.
4007010Customer not foundwallet_not_foundrecoverableNo wallet account found for the provided identifier. Please try a different payment method.

For wallet_not_found and invalid_phone, the business has no continue_url to redirect to (the buyer cannot “fix” the wallet link in business UI). The platform should treat these as recoverable—e.g. offer another payment method such as CARD, or prompt the user to try a different identifier.

Important: Error messages returned to platforms should be opaque and user-friendly. Do not expose internal API error details. Log detailed errors server-side for debugging.

Example UCP Error Response (e.g. for 7010 Customer not found):

{
"messages": [
{
"type": "error",
"code": "wallet_not_found",
"severity": "recoverable",
"content": "No wallet account found for this phone number. Please try a different payment method.",
"path": "$.payment.instruments[0]"
}
]
}

Platform integration

Platform prerequisites

Before using this handler, Platforms MUST complete:

  1. If applicable: Register with merchant to allow to call their checkout_sessions endpoint. This could involve getting an access token
  2. Obtain a valid customer identifier based on the credential type:
  • MSISDN: Capture the user's MSISDN registered with the wallet. A suggestion is to implement the Login Product in your application.
  • TOKEN: Obtain a customer token from an authenticated session.

Prerequisites Output:

FieldDescription
merchant.access_tokenAccess token to call merchant (if applicable)
vippsUser.msisdnUser MSISDN registered on their wallet (for MSISDN credential)
vippsUser.customerTokenEncoded customer token from authenticated session (for TOKEN credential)

Payment protocol

Platforms MUST follow this flow to acquire a payment instrument:

Step 1: Discover handler

The Platform identifies com.vippsmobilepay.pay.payment_handler in the business's payment.handlers array.

{
"id": "vipps_mobilepay",
"name": "com.vippsmobilepay.pay.payment_handler",
"version": "2026-01-23",
"config": {
"merchant_serial_number": "123456",
"environment": "PRODUCTION"
}
}

Step 2: Choose payment method and gather required data

The Platform MUST determine which payment method to use based on whether the customer has a Vipps MobilePay wallet:

Option A: WALLET (User has wallet)

For users with a Vipps MobilePay wallet, use the WALLET instrument. The Platform MUST obtain a customer identifier:

  • MSISDN: Prompt the user for their MSISDN registered with Vipps MobilePay. Use the Login Product to authenticate and retrieve the MSISDN.
  • TOKEN: Use an encoded customer token obtained from an authenticated session.
Option B: CARD (User does not have wallet)

For users without a wallet identity, use the CARD instrument. The Platform MUST provide:

  • return_url: The URL to redirect the user back to after payment. This can be:
  • Universal Link: e.g., vipps-chat://payment-complete?session_id=... (for native mobile apps)
  • Website URL: e.g., https://chat.vippsmobilepay.com/payment-complete?session_id=... (for web-based platforms)

Tip: Include the checkout session ID in the return URL so the platform can resume the checkout flow after redirect.

Step 3: Complete checkout

The Platform submits the checkout with the constructed payment instrument.

Example with MSISDN credential:

POST /checkout-sessions/{checkout_id}/complete
Content-Type: application/json

{
"payment_data": {
"id": "instr_vipps_1",
"handler_id": "vipps_mobilepay",
"type": "WALLET",
"credential": {
"type": "MSISDN",
"value": "4712345678"
}
},
"risk_signals": {
"device_id": "device_abc123",
"session_id": "session_xyz789",
"ip_address": "192.168.1.1",
"user_agent": "Mozilla/5.0 ...",
"accept_language": "nb-NO"
}
}

Example with TOKEN credential:

POST /checkout-sessions/{checkout_id}/complete
Content-Type: application/json

{
"payment_data": {
"id": "instr_vipps_1",
"handler_id": "vipps_mobilepay",
"type": "WALLET",
"credential": {
"type": "TOKEN",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
}

Example with CARD instrument (No wallet required):

POST /checkout-sessions/{checkout_id}/complete
Content-Type: application/json

{
"payment_data": {
"id": "instr_card_1",
"handler_id": "vipps_mobilepay",
"type": "CARD",
"return_url": "vipps-chat://payment-complete?session_id=cs-ABC123XYZ"
}
}

Step 4: Handle async response

Both WALLET and CARD flows return status: complete_in_progress. The Platform MUST handle the response based on the message code:

WALLET Response (pending user approval in Vipps app):

{
"id": "cs-ABC123XYZ",
"status": "complete_in_progress",
"messages": [
{
"type": "info",
"code": "payment_pending_user_approval",
"content": "A payment request has been sent to your Vipps Mobilepay app. Please open and approve the payment in the app to complete your order."
}
],
"payment": {
"state": "pending_approval",
"expires_at": "2026-01-26T12:35:00Z"
}
}

CARD Response (redirect required):

{
"id": "cs-ABC123XYZ",
"status": "complete_in_progress",
"continue_url": "https://api.vipps.no/checkout/v3/session/...",
"messages": [
{
"type": "info",
"code": "payment_redirect_required",
"content": "Please complete your payment on the payment page."
}
],
"payment": {
"state": "pending_redirect",
"expires_at": "2026-01-26T12:35:00Z"
}
}

Platform behavior for WALLET (payment_pending_user_approval):

  1. Inform the user: Display the message content (e.g., "Please check your app to approve the payment")
  2. Poll for completion: Call GET /checkout-sessions/{checkout_id} every 2-5 seconds
  3. Check status: Continue polling until status is completed or an error state
  4. Handle timeout: If payment.expires_at passes, inform user the payment expired

Platform behavior for CARD (payment_redirect_required):

Per the UCP Continue URL specification, the continue_url enables checkout handoff from platform to business UI:

  1. Redirect the user: Open the continue_url in a browser or webview to hand off to the payment page
  2. User completes payment: User enters card details on the payment page
  3. Handle return: After payment, we redirect user back to the return_url provided in the instrument
  4. Resume checkout: Extract session ID from URL and poll GET /checkout-sessions/{id} for final status (completed or error)

Step 5: Poll for completion

GET /checkout-sessions/{checkout_id}

Response when payment is approved:

{
"id": "cs-ABC123XYZ",
"status": "completed",
"order": {
"id": "order-789",
"reference": "ORD-2026-001234",
"created_at": "2026-01-26T12:32:15Z"
},
"messages": [
{
"type": "info",
"code": "payment_approved",
"content": "Payment approved. Your order has been placed."
}
]
}

Response when user rejects:

{
"id": "cs-ABC123XYZ",
"status": "incomplete",
"messages": [
{
"type": "error",
"code": "payment_rejected",
"severity": "requires_buyer_input",
"content": "Payment was declined. Please try again or choose a different payment method."
}
]
}

Polling best practices

RecommendationDetails
Poll interval2-5 seconds initially, increase if many retries
Max durationStop after payment.expires_at + small buffer
Exponential backoffConsider increasing interval after 30 seconds
User feedbackShow progress indicator while polling

Security considerations

RequirementDescription
TLS RequiredAll communication with our APIs MUST use TLS 1.2 or higher. Connections using older TLS versions will be rejected.
MSISDN ValidationFor MSISDN credentials, the value MUST be validated to ensure it is a valid phone number format (digits only, 7-15 digits, no leading zero) before sending to the API.
TOKEN ValidationFor TOKEN credentials, the value MUST be validated to ensure it is a non-empty string. Token validity is verified by the API.
Data ResidencyAll payment data is processed within the EEA (European Economic Area). PII is handled according to GDPR requirements.

References

  • Handler Spec: https://ucp.vippsmobilepay.com/ucp/2026-01-23/vipps_mp_payment_handler
  • Config Schema: https://ucp.vippsmobilepay.com/ucp/2026-01-23/schemas/wallet_payment_handler.json
  • Instrument Schema: https://ucp.vippsmobilepay.com/ucp/2026-01-23/schemas/payment_instrument.json
  • Credential Schema: https://ucp.vippsmobilepay.com/ucp/2026-01-23/schemas/wallet_payment_credential.json