> **Description:** Full API reference for online payment integration.

# eCom API guide

**INFO**

 **All new integrations:** Use the [ePayment API](https://developer.vippsmobilepay.com/docs/APIs/epayment-api/README.md).

 **Existing eCom API users:** See the [eCom to ePayment migration guide](https://developer.vippsmobilepay.com/docs/APIs/epayment-api/migration.md) for step-by-step migration instructions.

## Payment flows

### Regular flow

The user pays from an online store by selecting Vipps.
They enter their phone number and are redirected to the Vipps app
where they confirm payment.
For an example, see [eCom: How it works](https://developer.vippsmobilepay.com/docs/APIs/ecom-api/how-it-works/vipps-ecom-api-howitworks.md).

The regular flow is as follows:

1. The customer selects Vipps as the method of payment and enters their mobile number into the app or website.
2. The merchant initiates the payment command, and the customer confirms the purchase through their Vipps app.
3. If the purchase is approved, the merchant registers the sale in their system.

Screenshots: In-store Vipps payment flow. Left: POS system showing order and a "Pay with Vipps" button. Middle: Vipps app payment screen showing amount owed to The Store. Right: receipt screen showing the payment is Paid.

This diagram shows a simplified payment flow:

Payment flow

```mermaid
stateDiagram
    direction LR
    [*] --> Initiate
    Initiate --> Reserve: User confirms
    Reserve --> Capture: Merchant captures
    Capture --> [*]
    Capture --> Refund: Merchant refunds
    Refund --> [*]
    Reserve --> Cancel: Merchant cancels
    Initiate --> Cancel : User cancels, or timeout
    Cancel --> [*]
```

Payment lifecycle states: A payment begins at Initiate. When the user confirms, it moves to Reserve. From Reserve, the merchant can either capture the payment (moving it to Capture) or cancel it (moving it to Cancel). From Capture, the merchant can refund the payment (moving it to Refund). Both Refund and Cancel are terminal states. A payment can also move directly from Initiate to Cancel if the user cancels or a timeout occurs.

#### Phone and mobile browser flow

A payment is initiated with a call to
[`POST:/ecomm/v2/payments`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml).

Three-screen sequence showing a push-notification payment flow: (1) Phone lock screen showing a Vipps push notification "Ecom Services is requesting 399 kr". (2) Vipps payment screen showing 399 kr for "Green Hoodie" with Mastercard as payment method. (3) Receipt screen showing the payment is Approved with date and transaction details.

Triggered by the payment initiation, the [landing page](https://developer.vippsmobilepay.com/docs/knowledge-base/landing-page.md) will automatically
detect if it is being invoked on a phone, and whether Vipps is installed on the phone.
If Vipps is installed, Vipps will automatically be opened.

##### Vipps installed

1. Vipps is invoked (with app-switch).
2. The user accepts or rejects the payment request in Vipps.
3. The Vipps backend makes a call to the merchant's `callbackPrefix` with
   information about the payment.
4. Once payment process is completed, Vipps redirects to the
   `fallBack` URL that merchant provided earlier (see above).

##### Vipps not installed

1. The landing page (in the browser) prompts the user for the phone number.
2. The Vipps backend sends a push notification to the user's phone,
   and also displays a notification on the landing page for the user to
   continue the payment in Vipps on the phone.
3. The user accepts or rejects the payment in Vipps.
4. The Vipps backend makes a call to the merchant's `callbackPrefix` with
   information about the payment.
5. Once the payment process is completed, Vipps redirects to the
   `fallBack` URL that the merchant provided earlier.

**NOTE**

1. Vipps cannot guarantee that the user will get to the
   `fallBack` URL, since the user may switch away from Vipps or "kill" the app,
   or there may be network or battery problems before the URL is opened.
   If the merchant relies 100 % on users visiting the `fallback` URL, there will
   be problems: The user has completed the payment, but the merchant ignores it.
2. If the user has started the payment in an embedded browser, such as in
   Facebook or Instagram, it is not possible for Vipps to open the
   `fallBack` URL in the embedded browser. The phone OS will always open URLs
   in the default browser. See:
   [General info: Recommendations regarding handling redirects](https://developer.vippsmobilepay.com/docs/knowledge-base/redirects.md).
3. Because of the above, a successful payment *must not* rely on session cookies
   in the browser.
4. Vipps cannot guarantee a particular sequence of callback and fallback,
   as this depends on user actions, network connectivity, and network speed.
   Because of this, it is not possible to base an integration on a specific
   sequence of events.
5. URLs must be valid. See:
   [General info: URL validation](https://developer.vippsmobilepay.com/docs/knowledge-base/url-validation.md)

#### Desktop flow

Screenshot: Vipps landing page. Left panel: phone number entry (Norwegian +47 prefix) for a 200 NOK payment with "Remember my number" checkbox. Right panel: waiting screen with "Open Vipps" and a countdown timer.

1. The landing page will be opened in the desktop browser.
2. The landing page will prompt for the user's phone number.
   If the phone number is known, it should be prefilled by the merchant.
3. Vipps sends a push notification to the user's phone, with a
   notification on the landing page to continue the payment in Vipps on the phone.
4. The user accepts or rejects the payment in Vipps.
5. The Vipps backend makes a call to the merchant's `callbackPrefix` with
   information about the payment. See:
   [Callbacks](#callbacks).
6. Once the payment process is completed, the landing page will redirect to the
   `fallBack` URL that merchant provided earlier.

#### Payments initiated in an app

If payments are always initiated in the merchant's native app, there
is no need to pass any additional parameters. Vipps will handle everything
automatically.

It is also possible to send the optional `isApp` parameter, but this
comes with extra responsibility. For details, see
[General info: App flow recommendations](https://developer.vippsmobilepay.com/docs/knowledge-base/app-flow.md).

### In-store flow

The service provider initiates the payment through their Point of Sales (POS) system after requesting
and entering the customer's phone number.
For an example, see
[eCom API: How it works in the store](https://developer.vippsmobilepay.com/docs/APIs/ecom-api/how-it-works/vipps-in-store-howitworks.md).

This can be made smoother with the QR code: A QR code is displayed on a customer-facing screen. The customer scans this with their Vipps app to open a payment request in their app.
This removes the need for the customer to manually enter their mobile number.
See [One-time payment QR codes](#one-time-payment-qr-codes).

### Express flow

Express (*Vipps Hurtigkasse*) expands upon the regular payment flow
with the user selecting a shipping option and sharing their address with the merchant
through the Vipps app. See [Express](#express).

### Profile sharing flow

Similar to the regular flow, with the additional consent screen where the customer consents to sharing
their personal information (e.g., address, phone, and payment information).
They then complete the purchase from the website where their information is
automatically prefilled.
See [Profile sharing](#profile-sharing).

## API endpoints

The eCom API offers functionality for online payments.
Payments are supported in both web browsers and in native apps (via deep-linking).

| Operation                                                                                                            | Description                                                                                                | Endpoint |
|:---------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------|:-|
| [Initiate payment](#initiate)                                                                                        | Payment initiation, the first request in the payment flow. This *reserves* an amount.                      | [`POST:/ecomm/v2/payments`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) |
| [Capture payment](#capture)                                                                                          | When an amount has been reserved, and the goods are (about to be) shipped, the payment must be *captured*. | [`POST:/ecomm/v2/payments/{orderId}/capture`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) |
| [Cancel payment](#cancel)                                                                                            | The merchant may cancel a reserved amount, but not on a captured amount.                                   | [`PUT:/ecomm/v2/payments/{orderId}/cancel`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) |
| [Refund payment](#refund)                                                                                            | The merchant may refund a captured amount.                                                                 | [`POST:/ecomm/v2/payments/{orderId}/refund`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) |
| [Get payment details](#get-payment-details)                                                                          | The full history of the payment.                                                                           | [`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) |
| Get order status                                                                                                     | Deprecated, use [Get payment details](#get-payment-details).                                               | Deprecated, use [`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) |

Necessary endpoint from other APIs:

| Operation | Description | Endpoint |
|:----------|:------------|:---------|
| [Get Access token](https://developer.vippsmobilepay.com/redocusaurus/access-token-swagger-id.yaml) | Fetch the access token | [`POST:/accesstoken/get`](https://developer.vippsmobilepay.com/redocusaurus/access-token-swagger-id.yaml) |

We recommend using the standard HTTP headers for all requests.

See [General info: HTTP headers](https://developer.vippsmobilepay.com/docs/knowledge-base/http-headers.md) for details.

All API calls are authenticated with an access token and an API subscription key.
See
[Request an access token](https://developer.vippsmobilepay.com/docs/APIs/access-token-api/standard-authentication.md#step-2-request-an-access-token)
in the Getting started guide, for details.

### Initiate

Payment amounts must be in NOK, be non-zero *and* larger than 1 NOK (1 NOK = 100 øre).

Amounts are specified in minor units.
For Norwegian kroner (NOK) that means 1 kr = 100 øre.
Example: 499 kr = 49900 øre.

When you initiate a payment, it will normally only be *reserved* until you capture it.

This has some benefits:

* If a payment has been *reserved*, the merchant can
  make a
  [`PUT:/ecomm/v2/payments/acme-shop-123-order123abc/cancel`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
  call to immediately release the reservation.
* It is possible to reserve a higher amount and only
  capture a part of it (useful for electric car charging stations and similar variable-amount use cases).
  It is also possible to capture the full amount
  with multiple captures ("partial capture").

Detailed example

API Specification:
[`POST:/ecomm/v2/payments`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)

A minimal example:

```json
{
  "customerInfo": {},
  "merchantInfo": {
    "merchantSerialNumber": "123456",
    "callbackPrefix": "https://example.com/vipps/callbacks-for-payment-update-from-vipps",
    "fallBack": "https://example.com/vipps/fallback-result-page-for-both-success-and-failure/acme-shop-123-order123abc"
  },
  "transaction": {
    "orderId": "acme-shop-123-order123abc",
    "amount": 20000,
    "transactionText": "One pair of Vipps socks"
  }
}
```

An express payment example with more parameters provided:

```json
{
  "customerInfo": {
    "mobileNumber": "48059528"
  },
  "merchantInfo": {
    "authToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1Ni <snip>",
    "callbackPrefix": "https://example.com/vipps/callbacks-for-payment-update-from-vipps",
    "consentRemovalPrefix": "https://example.com/vipps/consents/",
    "fallBack": "https://example.com/vipps/fallback-result-page-for-both-success-and-failure/acme-shop-123-order123abc",
    "merchantSerialNumber": 123456,
    "shippingDetailsPrefix": "https://example.com/vipps/shipping/",
    "paymentType": "eComm Express Payment",
    "staticShippingDetails": [
      {
        "isDefault": "N",
        "priority": 1,
        "shippingCost": 99,
        "shippingMethod": "Walking",
        "shippingMethodId": "123abc"
      },
      {
        "isDefault": "Y",
        "priority": 2,
        "shippingCost": 199,
        "shippingMethod": "Running",
        "shippingMethodId": "321abc"
      }
    ]
  },
  "transaction": {
    "amount": 20000,
    "orderId": "acme-shop-123-order123abc",
    "timeStamp": "2018-12-12T11:18:38.246Z",
    "transactionText": "One pair of Vipps socks"
  }
}
```

The phone number is optional and should only be sent if it is already known.
Users should never be asked for the phone number, as they will
either be automatically app-switched to the Vipps app, or they will be presented
with the
[landing page](https://developer.vippsmobilepay.com/docs/knowledge-base/landing-page.md).
If the customer's phone number is needed by the merchant: Use `scope` and the
[Userinfo API](https://developer.vippsmobilepay.com/docs/APIs/userinfo-api/README.md).

**WARNING**

Do not send sensitive information in the `transactionText` field.
See:

* [`transactionText`](https://developer.vippsmobilepay.com/docs/knowledge-base/transactiontext.md) for recommendations of what to use in this field.
* [*Datatilsynet*, Norwegian Data Protection Authority](https://www.datatilsynet.no/rettigheter-og-plikter/personopplysninger/) for information about which types of information are sensitive (in Norwegian).

**TIP**

Be sure to implement
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/docs/APIs/ecom-api/vipps-ecom-api.md#get-payment-details).

If the [callback](#callbacks) fails, you will not automatically receive notification of order status.
The solution is to check with
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/docs/APIs/ecom-api/vipps-ecom-api.md#get-payment-details).

Additional implementation details:

* [callbacks](#callbacks)
* [rate limiting](#rate-limiting)
* [capturing the payment](#capture)

#### The Vipps deeplink URL

Vipps responds to the
[`POST:/ecomm/v2/payments`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
request with a URL.
The URL is normally a `https://` URL, which automatically opens the Vipps app, if the
app is installed (and the landing page, if not).

It is possible to perform manual handling of the interaction between the user's Vipps app and the Vipps backend, although this is not recommended. For details, see [General info: App flow recommendations](https://developer.vippsmobilepay.com/docs/knowledge-base/app-flow.md).

#### Payment identification

A payment is uniquely identified by the combination of `merchantSerialNumber`
and `orderId`:

* `merchantSerialNumber`: The merchant's Vipps ID. Example: `123456`.
* `orderId`: Must be unique for the `merchantSerialNumber`. Example: `acme-shop-123-order123abc`.
  See: [`orderId` recommendations](https://developer.vippsmobilepay.com/docs/knowledge-base/orderid.md).

#### Payment retries

If a user cancels or does not act on a payment, there is no way to "retry"
the payment.

The initiate call is not idempotent, so the closest to a "retry"
is to make a new initiate call with a new `orderId`. Vipps has no concept
of relation between orders, so the "retry" payment is in no way connected
to the first payment attempt.

#### Payment parameter recommendations

For some hints about what to use for the parameter values, see:

* [`orderId`](https://developer.vippsmobilepay.com/docs/knowledge-base/orderid.md)
* [`transactionText`](https://developer.vippsmobilepay.com/docs/knowledge-base/transactiontext.md)
* [URLs](https://developer.vippsmobilepay.com/docs/knowledge-base/url-validation.md)

#### The landing page

When a user is directed to the `url` sent in the response to
[`POST:/ecomm/v2/payments`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml),
they will either be taken to the Vipps app or to the
[Landing page](https://developer.vippsmobilepay.com/docs/knowledge-base/landing-page.md).

#### Other relevant information

See below for helpful information, for example:

* [Payment flows](#payment-flows)
* [Callbacks](#callbacks)
* [Errors](#errors)
* [Rate limiting](#rate-limiting)

### Reserve

When you initiate a payment, it will be *reserved* until you *capture* it:

* When a payment is reserved it means the customer has approved the payment.
  The funds are still in the customer's account, but not available to spend on other things.
* When a payment is captured it means the funds are moved from customer's account to merchant's account.
  * Captured funds will be settled to the merchant's settlement account after two business days. See
  [Settlements](https://developer.vippsmobilepay.com/docs/knowledge-base/settlements.md) for more details.

When the user confirms the purchase in Vipps, the payment status changes to `RESERVE`.
The respective amount will be reserved for future capturing.

### Capture

The eCom API supports *reserve capture* and *direct capture*:

* *Reserve capture* is the default, and works for all types of payments.
  When you initiate a payment, it will be reserved until you capture it.
  The capture can be done a few seconds later, or several days later.
* *Direct capture* is available in the
  [eCom API](https://developer.vippsmobilepay.com/docs/APIs/ecom-api/README.md)
  for historical reasons, but not available in the newer
  [ePayment API](https://developer.vippsmobilepay.com/docs/APIs/epayment-api/README.md).
  When *direct capture* is configured for a sales unit, all payment reservations
  will instantly be captured, without the need for a separate capture request.
  This is provided for legacy reasons and was intended for situations
  where the product or service is immediately provided to the customer, and
  there is absolutely no chance that the service is not available or sold out,
  e.g. digital services.
  We have since moved away from this, and strongly recommend that all integrations
  use the ePayment API and *reserve capture*.
  Direct capture requires additional compliance checks of the merchant.

**CAPTURE CONSIDERATIONS**

You should capture as soon as is legally possible, because some banks will release the funds after some days, making
it difficult to capture later.
If it's not captured within the
[payment capture deadlines](https://developer.vippsmobilepay.com/docs/knowledge-base/reserve-and-capture.md#capture-deadlines),
it will be automatically cancelled.

However, be aware that it isn't legal to capture before the
product or service is provided to the customer, as per the [capture regulations](https://developer.vippsmobilepay.com/docs/knowledge-base/reserve-and-capture.md#capture-regulations).

**TIP**

It's completely fine to use *reserve capture* almost exactly like
*direct capture*: Just do the capture immediately after the reservation.
The user experience is exactly the same.

Important things to consider for cancellations and refunds:

* If a payment has been *reserved* (as with "reserve capture"), the merchant can
  make a "cancel" API request to immediately release the reservation and make available
  in the customer's account.
  This may be useful even in situations where it's "impossible" that the goods/service
  is sold out.
* If a payment has been *captured* (as with "direct capture"), the merchant has to
  make a "refund" API request, and it then takes several days before the amount is
  available in the customer's account.
* With "reserve capture" it is possible to reserve a higher amount and only
  capture a part of it (useful for electric car charging stations and similar variable-amount use cases).
  It is also possible to capture the full amount with multiple captures ("partial capture").

Capture is done with
[`POST:/ecomm/v2/payments/{orderId}/capture`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml).

Use the idempotency key, `X-Request-Id`, in the capture call. Then, if a capture
request fails for any reason, it can be retried with the same idempotency key.
You can use any unique ID for your `X-Request-Id`.

See the [`X-Request-Id` in the capturePaymentUsingPOST specification](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) for details.

To perform a normal capture of the entire amount, `amount` can be
omitted from the API request (i.e., not sent at all), set to `null` or set to `0`.
When doing a
[partial capture](#partial-capture),
you need to specify the `amount`.

**Important**

It is important to check the response of the `/capture`
call. The capture is only successful when the response is `HTTP 200 OK`.

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

Types of capture

#### Reserve capture

*Reserve capture* is the normal flow.

When the end user approves an initiated payment, it will be reserved until you
capture it. When the order is reserved, the amount is marked as reserved by the
bank, but not transferred.

##### Partial capture

Partial capture is a reserve capture where you capture part(s) of the full amount. There can be multiple partial captures
until the amount is depleted.

Partial capture may be used in cases where a partial order is shipped or for other reasons.
It may be called as many times as required while there is a remaining amount that is reserved and has not yet been captured.

For this API, it is also possible to do a partial capture and send an optional parameter to explicitly free the remaining amount immediately.

#### Direct capture

When *direct capture* is activated, all payment reservations will instantly be captured.
This is intended for situations where the product or service is
immediately provided to the customer, and there is no chance that the service
is not available or sold out, e.g. digital services

*Direct capture* requires additional compliance checks.

A sales unit can only have one capture type, and it must be configured by Vipps MobilePay.
You can't turn *direct capture* on or off as a merchant.

We strongly recommend using "reserve capture" in all situations.

**Please note:** We only offer "direct capture" for merchants that use
Vipps MobilePay through a partner, and for merchants that have a Key Account Manager.
"Direct capture" must be requested by the partner from the partner manager,
or by KAM merchants from the Key Account Manager.

**Please note:** The ePayment API and Checkout API do *not* support "direct capture".

See the FAQ:

* [When should I use "direct capture"?](https://developer.vippsmobilepay.com/docs/knowledge-base/reserve-and-capture.md#when-should-i-use-direct-capture)
* [How can I check if I have "reserve capture" or "direct capture"?](https://developer.vippsmobilepay.com/docs/knowledge-base/reserve-and-capture.md#how-can-i-check-if-i-have-reserve-capture-or-direct-capture)

#### More information about capture

See:

* [General info: capture](https://developer.vippsmobilepay.com/docs/knowledge-base/reserve-and-capture.md)
* [General info: Capture regulations](https://developer.vippsmobilepay.com/docs/knowledge-base/reserve-and-capture.md#capture-regulations).
* [General info: Reserve capture vs. direct capture](https://developer.vippsmobilepay.com/docs/knowledge-base/reserve-and-capture.md#reserve-capture-vs-direct-capture)

### Cancel

Cancelling a payment releases the funds from the customer's bank account. This updates
the customer's bank statement and the payment overview in the Vipps  app.

The Cancel request allows the merchant to cancel a reserved or initiated transaction.

Please note that it is not possible to cancel a request that is over 180 days old.
Attempting to cancel an older payment will result in a `HTTP 400 Bad Request`.

The payment flow can be aborted under certain circumstances:

* When the user cancels (rejects) the initiated payment in Vipps.
* When the merchant cancels.
* Timeouts: If the user does not confirm in time.

After cancellation, the order gets a new status:

* If an order is cancelled by the merchant while it's in the `RESERVE` state, the status becomes `VOID`.
* If an order is cancelled by the merchant while it's in the `INITIATE` state, the status becomes `CANCEL`.
* If an order is cancelled by the user, the status becomes `CANCEL`.

Detailed example

[`PUT:/ecomm/v2/payments/{orderId}/cancel`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)

```json
{
  "merchantInfo": {
    "merchantSerialNumber": "123456"
  },
  "transaction": {
    "transactionText": "No socks for you!"
  }
}
```

Response:

```json
{
  "orderId": "acme-shop-123-order123abc",
  "transactionInfo": {
    "amount": 20000,
    "transactionText": "No socks for you!",
    "status": "Cancelled",
    "transactionId": "5001420063",
    "timeStamp": "2018-11-14T15:31:10.004Z"
  },
  "transactionSummary": {
    "capturedAmount": 0,
    "remainingAmountToCapture": 0,
    "refundedAmount": 0,
    "remainingAmountToRefund": 0
  }
}
```

#### Cancelling a pending order

If you wish to cancel a transaction before the customer can confirm the payment
in Vipps, you can send a
[`PUT:/ecomm/v2/payments/{orderId}/cancel`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
request while the transaction is in the `INITIATE` stage.

This may be useful in face-to-face situations where a customer's phone runs out
of battery, or if the customer suddenly changes their mind and wants to buy
more and the amount for the payment increases.
This should not be considered a consistent or guaranteed operation,
as the `/cancel` request depends on actions taken by the user in the app.

If the cancel request is successful, the payment state in the response will be: `CANCELLED`.
> **Warning**
> The [`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) endpoint however will return `CANCEL`, *not* `CANCELLED`.

A call to
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
for the same order will return the following, regardless of whether the
transaction has been reserved before the cancellation: `CANCEL`.
> **Note**
> If the order is in the state `RESERVED` and then cancelled by the merchant, the status will be `VOID`.

**Please note:** If the user is already in a 3-D Secure session, the payment
cannot be cancelled as described above.

#### Cancelling a partially captured order

If you wish to cancel an order that you have partially captured, send a
[`PUT:/ecomm/v2/payments/{orderId}/cancel`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
request with `shouldReleaseRemainingFunds: true` in the body.
The payment must be `RESERVED` for this to take effect.

If `shouldReleaseRemainingFunds` is not set, it will default to `false`.

When `shouldReleaseRemainingFunds` is set to `false`,
any request to cancel after a partial or full capture has been performed will be rejected.

This is a useful and recommended feature, as it releases any reserved balance
as soon as the card issuer and/or bank permits.

Detailed example

```json
{
  "merchantInfo": {
    "merchantSerialNumber": "123456"
  },
  "transaction": {
    "transactionText": "No socks for you!"
  },
  "shouldReleaseRemainingFunds": true
}
```

Response:

```json
{
  "orderId": "acme-shop-123-order123abc",
  "transactionInfo": {
    "amount": 20000,
    "transactionText": "No socks for you!",
    "status": "Cancelled",
    "transactionId": "5001420063",
    "timeStamp": "2018-11-14T15:31:10.004Z"
  },
  "transactionSummary": {
    "capturedAmount": 10000,
    "remainingAmountToCapture": 0,
    "refundedAmount": 0,
    "remainingAmountToRefund": 10000
  }
}
```

**Please note:** Once this operation has been performed, there will be zero
funds remaining to capture. Do not call this endpoint until you are sure you
have captured all you need.

### Refund

The eCom API supports refunds with
[`POST:/ecomm/v2/payments/{orderId}/refund`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml).

All integrations with the eCom API *must* support refunds.

#### How refunds work

Merchants can initiate a refund for a captured amount, either in full or partially:

* **Full refund:** Refund the entire captured amount.
* **Partial refund:** Specify an `amount` lower than the captured amount. The refunded amount cannot exceed the captured amount.
  See [Partial refunds](#partial-refunds).

Refunds can be processed up to **365 days** after the payment or reservation. Attempting to refund an older payment will return `HTTP 400 Bad Request`.

See [General info: Refunds](https://developer.vippsmobilepay.com/docs/knowledge-base/refund.md) for full details.

**TIP**

You can also perform refunds on the [business portal](https://portal.vippsmobilepay.com)

#### Processing time

* Typically: **2-3 banking days** for the funds to reach the customer's account.
* In some cases: Up to **10 days**, depending on the banks involved.

#### Additional notes

In a capture request, the merchant may also use the `X-Request-Id` header. See [Idempotency](#idempotency) for details.

Detailed example

[`POST:/ecomm/v2/payments/acme-shop-123-order123abc/refund`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)

```json
{
  "merchantInfo": {
    "merchantSerialNumber": "123456"
  },
  "transaction": {
    "amount": 20000,
    "transactionText": "Refund of Vipps socks"
  }
}
```

Response:

```json
{
  "orderId": "acme-shop-123-order123abc",
  "transaction": {
    "amount": 20000,
    "transactionText": "Refund of Vipps socks",
    "status": "Refund",
    "transactionId": "5600727726",
    "timeStamp": "2018-11-14T15:23:02.286"
  },
  "transactionSummary": {
    "capturedAmount": 20000,
    "remainingAmountToCapture": 0,
    "refundedAmount": 20000,
    "remainingAmountToRefund": 0
  }
}
```

##### Partial refunds

A partial refund is issued by specifying an `amount` lower than the captured amount.
The refunded amount can never exceed the captured amount.

**Example:**
A customer orders two items with a total cost of 1,000 NOK.
The merchant captures 1,000 NOK, but the customer decides to purchase only one of the items priced at 750 NOK.
The merchant performs a [partial capture](#partial-capture) of 750 NOK, and needs to refund the remaining 250 NOK.

---

###### What happens next

* **Short version:** The remaining amount is automatically released by the bank after a few days. See:
  [For how long is a payment reserved](https://developer.vippsmobilepay.com/docs/knowledge-base/reserve-and-capture.md#for-how-long-is-a-payment-reserved).

* **Long version:**
  It is possible to cancel the remaining reservation after a partial capture via the eCom API:
  Send a
  [`PUT:/ecomm/v2/payments/{orderId}/cancel`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
  request with `"shouldReleaseRemainingFunds": true` in the body.
  The payment must have status `RESERVED` for this to take effect. See:
  [eCom API: Cancelling a partially captured order](#cancelling-a-partially-captured-order).

---

###### Timelines and banking process

* A partial capture (e.g., 750 NOK of a 1,000 NOK payment) is typically confirmed in the bank within **3-10 days**, though it may take longer.
* Once completed, the bank makes the remaining funds (e.g., 250 NOK) available in the customer's account.
* This process is fully dependent on the customer's bank. Vipps MobilePay cannot speed it up.

---

###### Important notes about bank reservations

* Banks keep the original reservation period, regardless of the number of captures.
* A partial capture does **not** extend the reservation period.
* Banks count the reservation days from when the **original reservation** was made.
* The merchant must complete all captures **before** the reservation expires.
* If a partial capture has been made, the bank releases the remaining reserved amount.
* If no capture has been made, the entire reserved amount is released.

For details about cancelling the remaining reservation after a partial capture, see
[Cancelling a partially captured order](#cancelling-a-partially-captured-order).

### Get payment details

[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
retrieves the full history of a payment and the status of the operations.

#### Payment states

| # | From-state | To-state | Description                                                    | Operation |
|:--|:-----------|:---------|:---------------------------------------------------------------|:-|
| 0 | -          | Initiate | Payment initiation                                             | `INITIATE` |
| 1 |            | -        | The merchant has initiated the payment                         | `INITIATE` |
| - |            |          | The user has accepted the payment and amount has been reserved | `RESERVE` |
| - |            | Cancel   | The user has cancelled the order                               | `CANCEL` |
| 2 | Reserve    | Capture  | The merchant captures the payment and ships                    | `CAPTURE` |
| - |            | Cancel   | The merchant has cancelled the order                           | `VOID` |
| 3 | Capture    | --       | A final state: Payment fully processed                         | `CAPTURE` |
| - |            | Refund   | The merchant has refunded the money to the user                | `REFUND` |
| 4 | Cancel     | --       | A final state: Payment cancelled                               | - |
| 5 | Refund     | --       | A final state: Payment refunded                                | - |

#### Requests and responses

Please note that the response from
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
always contains *the entire history* of payments for the order, not just the current status.

**Important:** The `operationSuccess` field indicates whether an operation was successful or not.

| Response   | Description                                             |
|:-----------|:--------------------------------------------------------|
| `INITIATE` | Payment initiated by merchant                           |
| `RESERVE`  | Payment reserved by user accepting the payment in Vipps |
| `CAPTURE`  | Payment captured by merchant (after `RESERVE`)          |
| `REFUND`   | Payment refunded by merchant (after `CAPTURE`)          |
| `CANCEL`   | Payment cancelled by user in Vipps                      |
| `SALE`     | Payment captured with direct capture by merchant        |
| `VOID`     | Payment cancelled by merchant                           |

Detailed example

```json
{
  "orderId": "acme-shop-123-order123abc",
  "transactionSummary": {
    "capturedAmount": 20000,
    "remainingAmountToCapture": 0,
    "refundedAmount": 20000,
    "remainingAmountToRefund": 0,
    "bankIdentificationNumber": 492560
  },
  "transactionLogHistory": [
    {
      "amount": 20000,
      "transactionText": "Refund of Vipps socks",
      "transactionId": "5600727726",
      "timeStamp": "2018-11-14T15:23:02.286Z",
      "operation": "REFUND",
      "requestId": "1542208972",
      "operationSuccess": true
    },
    {
      "amount": 20000,
      "transactionText": "One pair of Vipps socks",
      "transactionId": "5001420058",
      "timeStamp": "2018-11-14T15:22:46.680Z",
      "operation": "CAPTURE",
      "requestId": "1542208966",
      "operationSuccess": true
    },
    {
      "amount": 20000,
      "transactionText": "One pair of Vipps socks",
      "transactionId": "5001420062",
      "timeStamp": "2018-11-14T15:21:22.126Z",
      "operation": "RESERVE",
      "requestId": "",
      "operationSuccess": true
    },
    {
      "amount": 20000,
      "transactionText": "One pair of Vipps socks",
      "transactionId": "5001420062",
      "timeStamp": "2018-11-14T15:21:04.697Z",
      "operation": "INITIATE",
      "requestId": "",
      "operationSuccess": true
    }
  ]
}
```

**Please note:**

* The `transactionSummary` will not be part of the response if
  the user does not react to the landing page or app-switch.
* The `bankIdentificationNumber` will only be part of `transactionSummary` in the
  response of the `GET:/ecomm/v2/payments/{orderId}/details` endpoint.
* If paymentType is set to `eComm Express Payment` you will get `shippingDetails`
  and `userDetails` in addition to `transactionLogHistory` and `transactionSummary`.
* If you initiate the payment with a `scope` to use with the
  [Userinfo API](https://developer.vippsmobilepay.com/docs/APIs/userinfo-api/README.md)
  you will also get a `userDetails` object containing the user's information:

```json
"userDetails": {
  "bankIdVerified": 0,
  "email": "user@example.com",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "mobileNumber": "12345678",
  "userId": "c06c4afe-d9e1-4c5d-939a-177d752a0944"
}
```

See
[General info: Polling guidelines](https://developer.vippsmobilepay.com/docs/knowledge-base/polling-guidelines.md).

## Integration details

### Fallback URL

The phone's operating system always opens URLs in the default browser.

This means that the `fallback` URL (the "result page") will be opened in
the default browser. We have no way to open the `fallback` URL in the
embedded browser on Facebook, Instagram, or similar apps. Similarly, there is no way
for us to open the `fallback` URL in the same tab that the user came from
before the app-switch.

This means that the merchant must be able to detect or recognize the user
when the `fallback` URL is opened, without relying on session cookies or similar browser state.

### Callbacks

**Important**

We strongly recommend implementing API callbacks, even if your integration does not use the data provided in the callback.
Callbacks ensure you are notified when the payment status changes without having to poll the API continuously.

Callbacks allow Vipps to send the merchant information about the payment.
Callbacks are made for the following events:

* Payment successful
* Payment failed
* Payment rejected
* Payment timed out

**Please note:** Callbacks offer a faster user experience than polling, but you
can't rely on callbacks alone. You must also poll
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml).

See [General info: Polling](https://developer.vippsmobilepay.com/docs/knowledge-base/polling-guidelines.md) for polling guidelines.

**Important:**

* Vipps makes *one* attempt at a callback and cannot guarantee that it succeeds,
  as it depends on network, firewalls and other factors that Vipps cannot control.
  If the communication is broken during the process for some reason and Vipps
  is not able to execute callback to the merchant's server, the callback will
  not be retried.
* Vipps callbacks require a response within 3 seconds; otherwise, they will time out.
* Callback URLs must use HTTPS.

The callback request body is different for the different payment types:

* `"eComm Regular Payment"`: The callback contains the order and transaction details.
* `"eComm Express Payment"`: The callback contains the order and transaction details
  *and in addition* the user details and shipping details.
* If [Profile sharing](#profile-sharing) is used: The callback is the same as for `"eComm Express Payment"`.

#### Do we need to support callbacks?

Please try to implement the API callbacks, even if you do not use the data
provided in the callback.

If it's not possible for your integration to support callbacks (no fixed hostname or IP address),
you must actively check the payment status.

This is also required if you do support callbacks.

#### Callbacks and fallbacks sequence

The sequence of callbacks and fallbacks cannot be guaranteed.
The order depends on factors such as user actions, network connectivity, and processing speeds.

Because of this, integrations **must not** rely on a specific sequence of events between callbacks and fallbacks.

#### Callback URLs

Callback URLs are generated by appending the `orderId` to the `callbackPrefix` provided in the
[`POST:/ecomm/v2/payments/{orderId}`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
call. The `callbackPrefix` is an API endpoint on the merchant's side.
See [API specifications](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) for details.

**Example**

If:

* `callbackPrefix` = `https://example.com/vipps/callback`
* `orderId` = `acme-shop-123-order123abc`

Then Vipps will append `/v2/payments/acme-shop-123-order123abc`
(note the leading slash)
to `https://example.com/vipps/callback` and make a callback to
`https://example.com/vipps/callback/v2/payments/acme-shop-123-order123abc`.

This is simply:

* `callbackPrefix` + `/v2/payments/` + `{orderId}`
* `https://example.com/vipps/callback` + `/v2/payments/` + `acme-shop-123-order123abc`

**Important:**

* URLs must be valid. For details, see [General info: URL validation](https://developer.vippsmobilepay.com/docs/knowledge-base/url-validation.md).
* Vipps does *not* support sending requests to all ports, so to be safe use
  common port numbers such as: 80, 443, 8080.
* Vipps does *not* support callback URLs that return `HTTP 301 Redirect`,
  `HTTP 302 Permanently Moved` or `HTTP 307 Temporary Redirect`.
  Vipps will not follow to a `Location`.
  The callback URLs *must* be directly reachable.

Detailed example

Below are two examples of payment update callbacks with
[`POST:[callbackPrefix]/v2/payments/{orderId}`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/transactionUpdateCallbackForRegularPaymentUsingPOST).

Example: `"eComm Regular Payment"` callback:

```json
{
  "merchantSerialNumber": 123456,
  "orderId": "acme-shop-123-order123abc",
  "transactionInfo": {
    "amount": 20000,
    "status": "RESERVED",
    "timeStamp": "2018-12-12T11:18:38.246Z",
    "transactionId": "5001420062"
  }
}
```

Example: `"eComm Express Payment"` callback:

```json
{
  "merchantSerialNumber": 123456,
  "orderId": "acme-shop-123-order123abc",
  "shippingDetails": {
    "address": {
      "addressLine1": "Dronning Eufemias gate 42",
      "addressLine2": "Att: Rune Garborg",
      "city": "OSLO",
      "country": "NO",
      "zipCode": "0191" // Postcode (apologies for not using postCode as elsewhere)
    },
    "shippingCost": 99,
    "shippingMethod": "By cannon",
    "shippingMethodId": "53DBB6BA-89DB-4997-B444-5E9F1681E3D3"
  },
  "transactionInfo": {
    "amount": 20000,
    "status": "RESERVE",
    "timeStamp": "2018-12-12T11:18:38.246Z",
    "transactionId": "5001420062"
  },
  "userDetails": {
    "firstName": "Ada",
    "lastName": "Lovelace",
    "email": "user@example.com",
    "mobileNumber": "12345678",
    "userId": "1234567"
  }
}
```

**NOTE**

Regular payments use `RESERVED`, but express payments use
`RESERVE`. We apologize for this, but it is technical debt, and it cannot be
corrected in this version of the API, as that would break backwards compatibility.

#### How to test your own callbacks

We recommend using [HTTPie](https://httpie.io) to test your callback implementation.

1. **Create a JSON payload**
   Use the callback request body format for
   [`POST:[callbackPrefix]/v2/payments/{orderId}`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/transactionUpdateCallbackForRegularPaymentUsingPOST)
   and save it as `callback.json`:

   ```json
     {
       "merchantSerialNumber": 123456,
       "orderId": "acme-shop-123-order123abc",
       "transactionInfo": {
         "amount": 20000,
         "status": "RESERVED",
         "timeStamp": "2018-12-12T11:18:38.246Z",
         "transactionId": "5001420062"
       }
     }
   ```

2. **Build your callback URL**

   Example:

   * `callbackPrefix` = `https://example.com/vipps/callback`
   * `orderId` = `acme-shop-123-order123abc`

   Final callback URL: `https://example.com/vipps/callback/v2/payments/acme-shop-123-order123abc`

3. **Send the test request**
   Test the callback by making a POST request to your callback URL with the JSON file as request body:

   ```text
   POST https://example.com/vipps/callback/v2/payments/acme-shop-123-order123abc < callback.json
   ```

4. **Verify the response**
   If the response is not `HTTP 200 OK`, your callback handling is not working and must be corrected.

##### Troubleshooting callbacks

* Ensure all URLs provided to Vipps are accessible from outside your environment.
* If no callback is received, check if your firewall is blocking requests. See: [Vipps request servers](https://developer.vippsmobilepay.com/docs/knowledge-base/servers.md#request-servers).
* Check your logs for the relevant `orderId` (e.g., `acme-shop-123-order123abc`) to identify issues.

#### Authorization for callbacks

If you provide the `authToken` when initiating a payment with
[`POST:/ecomm/v2/payments/{orderId}`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml),
the `authToken` will be sent as an `Authorization` header in the callback and
shipping details requests. Please note that this is unrelated to the
authentication required by the Vipps API.

API spec:
[`POST:[callbackPrefix]/v2/payments/{orderId}`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/transactionUpdateCallbackForRegularPaymentUsingPOST)

#### Vipps callback servers

The callbacks from Vipps are made from the servers described in
[Vipps request servers](https://developer.vippsmobilepay.com/docs/knowledge-base/servers.md#request-servers).

Please make sure that requests from these servers are allowed through firewalls and other network restrictions.

#### Callback statuses

These are the statuses provided by Vipps in the callbacks by
[`[callbackPrefix]/v2/payments/{orderId}`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/transactionUpdateCallbackForRegularPaymentUsingPOST):

| Callback type    | Response         | Description                            |
|:-----------------|:-----------------|:---------------------------------------|
| Regular checkout | `RESERVED`       | Payment reserved by user accepting the payment in Vipps. |
|                  | `SALE`           | Payment captured with direct capture by merchant (after `RESERVED`). |
|                  | `RESERVE_FAILED` | Reserve capture failed because of insufficient funds, invalid card or similar. |
|                  | `SALE_FAILED`    | Direct capture failed because of insufficient funds, invalid card or similar. |
|                  | `CANCELLED`      | Payment cancelled by the user in Vipps. |
|                  | `REJECTED`       | User did not act on the payment (timeout or similar inaction). |
| Express | `RESERVE`        | Payment reserved by user accepting the payment in Vipps (it *is* correct that this is different from `RESERVED` for regular checkout - sorry.) |
|                  | `SALE`           | Payment captured with direct capture, by merchant (after `RESERVE`). |
|                  | `RESERVE_FAILED` | Reserve failed because of insufficient funds, invalid card or similar. |
|                  | `SALE_FAILED`    | Direct failed because of insufficient funds, invalid card or similar. |
|                  | `CANCELLED`      | Payment cancelled by user in Vipps.    |
|                  | `REJECTED`       | User did not act on the payment (timeout or similar inaction). |

For a full history of a payment, use
[`[callbackPrefix]/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/api/ecom#tag/eCom-API/operation/getPaymentDetailsUsingGET).
See [Get payment details](#get-payment-details) for more information.

### Errors

#### eCom API HTTP response codes

This API returns the following HTTP statuses in the responses:

* `200 OK`
* `201 Created`
* `204 No Content`
* `400 Bad Request`
* `401 Unauthorized`
* `403 Forbidden`
* `404 Not Found`
* `409 Conflict`
* `429 Too Many Requests`
* `500 Server Error`

For more information about the HTTP response codes and JSON object in the responses, see
[Troubleshooting errors](https://developer.vippsmobilepay.com/docs/knowledge-base/errors.md), which covers both technical API errors and business reasons why payments
fail (expired cards, insufficient funds, and similar issues).

For details about the specific eCom errors and responses, see [Errors](#errors), as well as
the [ePayment OpenAPI specification](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml).

#### Error object in the response

See also [eCom API HTTP response codes](#ecom-api-http-response-codes).

```json
[
  {
    "errorGroup": "Payment",
    "errorMessage": "Refused by issuer because of expired card",
    "errorCode": "44"
  }
]
```

```json
{
  "statusCode": 401,
  "message": "Access denied due to invalid subscription key. Make sure to provide a valid key for an active subscription."
}
```

#### Error groups

| Error groups   | Description                                                  |
|:---------------|:-------------------------------------------------------------|
| Authentication | Authentication Failure because of wrong credentials provided |
| Payment        | Failure while doing a payment authorization                  |
| InvalidRequest | Request contains invalid parameters                          |
| VippsError     | Internal Vipps application error                             |
| User           | Error related to the Vipps user (Example: Not a Vipps user)  |
| Merchant       | Errors regarding the merchant                                |

#### Error codes

**Please note:** Vipps is only allowed to provide all of these errors through
the API, and that we have to send `VippsError` (99) in cases where we are not
allowed to provide more details. The same goes for errors from our PSP: We
may not be allowed to pass on the details to merchants.

In case of errors: Use
[Get payment details](#get-payment-details)
to retrieve all the information about the payment.

| Error group    | Error Code | Error Message (and some explaining text for some errors) |
|:---------------|:-----------|:-----------------------------------------------|
| Payment        | 41         | "The user does not have a valid card." The customer must add a card. |
| Payment        | 42         | "Refused by the issuer." The customer must contact the issuer, typically the bank. |
| Payment        | 43         | "Refused by the issuer: Invalid amount." |
| Payment        | 44         | "Refused by the issuer: Expired card."  The customer must add a card.|
| Payment        | 45         | "Reservation failed, reason unknown." Most common: The customer has not acted upon the payment. We can't know the reason for this. See [Timeouts](https://developer.vippsmobilepay.com/docs/knowledge-base/timeouts.md). |
| Payment        | 51         | "Cannot cancel an orderId that has already been captured." Cannot [cancel](#cancel), do a [refund](#refund) instead. |
| Payment        | 52         | "Cancellation failed, reason unknown." [Cancel](#cancel) failed.                           |
| Payment        | 53         | "Cannot cancel an orderId that is not reserved."                           |
  | Payment        | 61         | "The total capture amount exceeds the reserved amount. You cannot capture a higher amount than the user has accepted. Check the payment details". This is in rare cases caused by rounding errors on the merchant's side. Check the [payment details](#get-payment-details).|
| Payment        | 62         | "The amount you tried to capture is not reserved. The user must accept the payment before capture can be done." |
| Payment        | 63         | "Capture failed an unknown reason. Retrieve the payment details to see the full details." Use [payment details](#get-payment-details) to see the exact status. |
| Payment        | 71         | "Cannot refund a higher amount than the captured amount." This is in rare cases caused by rounding errors on the merchant's side. |
| Payment        | 72         | "Cannot refund a reserved order. The orderId can be canceled instead." [Cancel](#cancel) instead.|
| Payment        | 73         | "Cannot refund a canceled order."               |
| Payment        | 74         | "Refund failed during debit from the merchant's account." |
| Payment        | 93         | Captured amount must be the same in an idempotent retry. The same `Idempotency-Key` can't be used with different request payloads. |
| Payment        | 95         | "Payments can only be refunded up to 365 days after reservation. See the FAQ." or "Direct capture is not allowed. See the Checkout documentation." See [General info: reserve capture vs. direct capture](https://developer.vippsmobilepay.com/docs/knowledge-base/reserve-and-capture.md#reserve-capture-vs-direct-capture).|
| Payment        | 98         | "Payments can only be captured up to 180 days after reservation. See the FAQ." Payments can only be captured up to 180 days after reservation. See [General info: capture deadlines](https://developer.vippsmobilepay.com/docs/knowledge-base/reserve-and-capture.md#capture-deadlines).|
| Payment        | 1082       | This person is not BankID verified. This only applies for test users. |
| VippsError     | 91         | "The transaction is not allowed." Typically shown when attempting to capture a cancelled order. |
| VippsError     | 92         | "The transaction has already been processed." |
| VippsError     | 93         | "The capture request must be identical to the previous request(s) in an idempotent retry." or "The refund request must be identical to the previous request(s) in an idempotent retry." |
| VippsError     | 94         | Order locked and is already processing. This can occur for a short period of time if a bank has problems, and Vipps needs to wait and/or clean up. Retry the same request later, with the same idempotency key. |
| VippsError     | 98         | "Too many concurrent requests. See the FAQ." Used only to prevent obviously incorrect API use. See [Rate limiting](#rate-limiting).|
| VippsError     | 99         | "Internal error. In some cases, this is caused by an incorrect API request. Please check the request." In practice this is *always* due to a combination of a bad request from the merchant and inadequate validation at our end. See [HTTP 500 Internal Server Error](https://developer.vippsmobilepay.com/docs/knowledge-base/errors.md#http-500-internal-server-error). |
| user           | 81         | "The phone number is either incorrectly formatted (see the API specification), is not a Vipps user, or the user cannot pay businesses. Vipps cannot give more details." We can't give all the details, as we can't reveal if the user is blocked, is too young to pay businesses, or other reasons we cannot disclose. |
| user           | 82         | "The user's app version is not supported."  The user must upgrade the app.              |
| Merchant       | 31         | "The merchant is blocked. The merchant can contact customer service for details." We can't reveal the details. See [Merchant unavailable, deleted, deactivated or not allowed to make payments](https://developer.vippsmobilepay.com/docs/knowledge-base/errors.md#merchant-unavailable-deleted-deactivated-or-not-allowed-to-make-payments). |
| Merchant       | 32         | "The receiving limit of merchant is exceeded." The merchant has received more money than allowed. The merchant can contact customer service for details.   |
| Merchant       | 33         | "The merchant's payment request limit is exceeded." The merchant has requested more money than allowed. Can contact customer service for details.  |
| Merchant       | 34         | "Duplicate orderId. The orderId must be unique for each MSN. It has already been used for this MSN." The `orderId` has already been used for another payment for this MSN. The orderId must be unique for the MSN. Follow the [`orderId` recommendations](https://developer.vippsmobilepay.com/docs/knowledge-base/orderid.md). |
| Merchant       | 35         | "The orderId 'acme-shop-123-order123abc' does not exist for MSN 654321." Follow the [`orderId` recommendations](https://developer.vippsmobilepay.com/docs/knowledge-base/orderid.md). |
| Merchant       | 36         | "The merchant's agreement has not been signed." The merchant can contact customer service for details. |
| Merchant       | 37         | "The merchant and/or sales unit is unavailable, deleted, deactivated or blocked for payments." We can't reveal the details. See [Merchant unavailable, deleted, deactivated or not allowed to make payments](https://developer.vippsmobilepay.com/docs/knowledge-base/errors.md#merchant-unavailable-deleted-deactivated-or-not-allowed-to-make-payments) |
| Merchant       | 38         | "The sales unit is not allowed to skip the landing page. See the FAQ." See the [landing page](https://developer.vippsmobilepay.com/docs/knowledge-base/landing-page.md#skip-landing-page). |
| Merchant       | 39         | "The sales unit is not allowed to initiate long-living payments." |
| Merchant       | 51         | "The Nin scope is not allowed." See [Access to NIN](https://developer.vippsmobilepay.com/docs/APIs/userinfo-api/userinfo-api-guide.md#nin). |
| Merchant       | 97         | "The sales unit is not allowed to perform payments. See the FAQ." Typically shown for a merchant that has only applied for Login API, and has not been through the required compliance checks for making payments. See [Merchant not allowed for ecommerce payment](https://developer.vippsmobilepay.com/docs/knowledge-base/errors.md#merchant-not-allowed-for-ecommerce-payment). |
| InvalidRequest | -          | The field name will be the error code. Contains a description about what exactly the field error is. |

See also the [ePayment OpenAPI specification](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml).

### Troubleshooting errors

#### `Payment failed`

This error occurs in [Express](#express) if your integration responds incorrectly to:
[`[shippingDetailsPrefix]/v2/payments/{orderId}/shippingDetails`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/fetchShippingCostUsingPOST).

Verify that your response format and content are correct.

For a faster payment flow and better user experience, consider using
[static shipping methods](#shipping-and-static-shipping-details).

### Rate limiting

We have added rate limiting to our APIs (HTTP 429 Too Many Requests) to prevent fraudulent and wrongful behavior, and to increase the stability and security of our APIs. 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.

| API | Limit | Key | Explanation |
| --- | ----- | --- | ----------- |
| [Initiate](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) | 2 per minute   | orderId + MSN              | Two calls per minute for each unique orderId |
| [Cancel](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) | 5 per minute   | orderId + MSN              | Five calls per minute for each unique orderId |
| [Capture](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)     | 5 per minute   | orderId + MSN              | Five calls per minute for each unique orderId |
| [Refund](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)       | 5 per minute   | orderId + MSN              | Five calls per minute for each unique orderId |
| [~~Status~~](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)   | 120 per minute | orderId + subscription key | 120 calls per minute for each unique orderId |
| [Details](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)   | 120 per minute | orderId + subscription key | 120 calls per minute for each unique orderId |

**Please note:** The "Key" column is important. The above means that we allow two
[Initiate](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
calls per minute *per unique orderId* for that MSN. This
is to prevent too many initiate calls for the same payment. The overall limit
for number of *different* payments is *far* higher than 2. The same goes for
[Capture](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml):
You can make five capture calls per minute for
one unique `orderId`, and the limit for capture calls for different `orderId` values
is *far* higher.

### Partner keys

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

See:
[Partner keys](https://developer.vippsmobilepay.com/docs/partner/partner-keys.md).

### Idempotency

In a capture request, the merchant may also use the `X-Request-Id` header.
This header is an idempotency header ensuring that, if the merchant retries
a request with the same `X-Request-Id`, the retried request will not make
additional changes.

See the
[Idempotency header](https://developer.vippsmobilepay.com/docs/knowledge-base/http-headers.md#idempotency)
for more details.

Example Request:

[`POST:/ecomm/v2/payments/acme-shop-123-order123abc/capture`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)

```json
{
  "merchantInfo": {
    "merchantSerialNumber": "123456"
  },
  "transaction": {
    "amount": 20000,
    "transactionText": "Socks on the way! Tracking code: abc-tracking-123"
  }
}
```

Response:

```json
{
  "orderId": "acme-shop-123-order123abc",
  "transactionInfo": {
    "amount": 20000,
    "timeStamp": "2018-11-14T15:22:46.736Z",
    "transactionText": "Socks on the way! Tracking code: abc-tracking-123",
    "status": "Captured",
    "transactionId": "5001420058"
  },
  "transactionSummary": {
    "capturedAmount": 20000,
    "remainingAmountToCapture": 0,
    "refundedAmount": 0,
    "remainingAmountToRefund": 20000
  }
}
```

### Exception handling

The section below explains how Vipps handles different exception and errors.
See also [Troubleshooting errors](#errors).

#### Connection timeout

Defining a socket timeout period is the common measure to protect server
resources and is expected. However, the time needed to fulfil a service request
depends on several systems, which impose a longer timeout period than usually
required.

We recommend setting no less than 1 second socket connection timeout
and 5 seconds socket read timeout while communicating with Vipps.

A good practice is, if/when the socket read timeout occurs, to call
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
and check status of last transaction in transaction history prior
to executing the service call again.

#### Callback aborted or interrupted

If the communication is broken during the payment process for some reason,
either because of network problems, that the user abruptly closes the app or
something else, and Vipps is not able to execute callback, the callback will
not be retried.

In other words, if the merchant doesn't receive any confirmation on payment
request call within callback time frame, merchant must call
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
to get the response of the payment request.

#### PSP connection issues

In cases of communication problems with Vipps' PSP, the response from Vipps
will be an error (see [Troubleshooting errors](#errors)).

The merchant should then call
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
to check if the transaction request is processed before making a new call,
using same idempotency key (see [Idempotency](#idempotency)).

#### Clean-up strategies

Vipps recommends merchants implement a robust clean-up strategy for all orders
where the goods or services are not issued to the user.

An example case would be:

Bank X is having issues and transactions for their customers are slow (15+ seconds)
for a significant period of time. As a result of this the user decides to close
the Vipps app while the transaction is being processed, they do not then see the
final result in the Vipps app. This transaction will eventually result in a
successful reservation. The user then returns to the Merchants app, but as the
entry was not via an expected app-switch from the Vipps app, state is not correctly
resumed. The user attempts a second payment, which this time goes through in a
timely manner, and upon app-switch back the user is issued their goods/services.

The user has now two reservations and only received goods/services once. It is
the merchant's responsibility here to ensure the first order, for which no
goods/services were issued, should be cancelled to remove the reservation from
the customers account.

Vipps recommends polling
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
until either a `REJECT`, `USER_CANCEL`, `RESERVE` or `SALE` status is received,
and then performing the appropriate action based on the status of product issuing.
The user should also be notified that the merchant has issued any product to
ensure they do not naturally retry the purchase. This includes using SMS/email
strategy if it is unclear that the user is on the merchant's website/app to see confirmation.

#### Recommendations for handling very high traffic

At peak traffic, like Black Friday, it is especially important to have a
robust integration.

We strongly recommend using "reserve capture", and not "direct capture",
so it is possible to cancel a payment without making a refund.

* Make sure you poll
  [`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml),
  and do not rely on the callback from Vipps, and also don't rely on the user
  reaching the callback URL.
* Consider if you should cancel or complete the payment if the payment takes
  much longer than normal, so much that the user may give up.
  * If you sell a ticket: It may be good to always issue the ticket if the user
    has approved the payment, even if the processing of the payment takes much
    longer than normal and the user "gives up".
  * If you sell food at a restaurant: If the payment takes so long that the
    user gives up and tries to order again: It may be smart to cancel the
    previous order, even though the user did approve the payment.

It is also important that the merchant's and/or partner's systems are able to handle the peak traffic.

See also:

* [Exception handling](#exception-handling)
* [Clean-up strategies](#clean-up-strategies)

### App integration

Configure your app or website to trigger Vipps (sometimes called "app-switching").

This may be done in two ways:

1. From a mobile or desktop browser. See [Desktop flow](#desktop-flow)
2. From an iOS or Android native app

After the user has finished (or cancelled) the payment in the Vipps app, they should be returned to your app or website through the specified `fallBack` URL.

When the user arrives back in your app or website, perform a call to the [payment details endpoint](#get-payment-details) to check the state
of the transaction. While some state of the eCom operation *can* be derived from things like whether or not user returned successfully from the
Vipps app, the most reliable approach to know the state of the payment is always to query the eCom API once the user arrive back in the merchant app/website.

#### App switching

We recommend that you always use the universal links (starting with `https`) as they contain built-in logic to handle both desktop and mobile flow.
They are more secure than the deep link flow and provide a better user experience.

Please see [General info: App flow recommendations](https://developer.vippsmobilepay.com/docs/knowledge-base/app-flow.md) for more details.

Legacy information

*This information is outdated. We recommend that you use universal links now for improved security.*

**WARNING**

Please use universal links instead.

See [General info: App flow recommendations](https://developer.vippsmobilepay.com/docs/knowledge-base/app-flow.md).

1. The merchant must pass the URL scheme of the app into the `fallBack` field in the [initiate payment request](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml). See [App-switch on iOS](#app-switch-on-ios) and [App-switch on Android](#app-switch-on-android) for specifics.
2. The merchant app should open Vipps deeplink received from the [initiate payment request](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml).
3. Once the operation in Vipps is completed, Vipps will redirect the user to the deeplink specified in the `fallBack` field.
4. The merchant app should query the [payment details endpoint](#get-payment-details) for updated status on the payment once user returns from Vipps.

**INFO**

Vipps will append a status at the end of the fallback URL. For example, if your `fallBack` URL is `merchantApp://result?myAppData`, Vipps
will append the status like: `merchantApp://result?myAppData&status=301` or `merchantApp://result?myAppData&status=100`. **This status is deprecated, and should no longer be used.** Query the [payment details endpoint](#get-payment-details) for latest status instead.

#### App-switch on iOS

Vipps on iOS requires a URL scheme in order to support app-switch.

See the official Apple documentation:
[Defining a Custom URL Scheme for Your App](https://developer.apple.com/documentation/uikit/core_app/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app)

#### Switch from merchant app to Vipps

Below is sample code to open iOS Vipps application with `deeplinkURL`.

```java
NSString *url = deeplinkURL; // Use the deeplinkURL provided in the API response
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:url]]) {
  [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]]; }
else {
  // No Vipps app installed: Open app store page.
  // Once user installs Vipps, calling app needs to initiate deeplinking again in order to get the callback
  [[UIApplication sharedApplication] openURL:[NSURL URLWithString: @"https://itunes.apple.com/no/app/Vipps-by-dnb/id984380185"]];
}
```

Example of a `deeplinkURL`:

`vipps://?token=eyJraWQiOiJqd3RrZXkiLCJhbGciOiJSUzI1NiJ9.ey <snip>`

**Please note:** For production it's `vipps://`, but for our test environment it's `vippsMT://`.

##### Redirect back to the merchant app from Vipps app

Once the operation in Vipps is completed, Vipps will open the fallback URL.
For app-to-app integration, merchant app needs to be registered for a URL scheme
and pass the URL scheme in `fallBack` URL in the Vipps backend API.
The Vipps mobile application will use the URL to launch the merchant application.

#### App-switch on Android

Vipps is launched with a standard intent, using the URL returned from the eCom
API when the payment is created (e.g., `"url": "vipps://?token=eyJraWQiOiJqd3RrZXki.."`).

Example of how to open Vipps:

```kotlin
package com.example.app

import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri

class MyActivity : Activity() {
    companion object {
        const val vippsPackageName = "no.dnb.vipps"
        const val vippsRequestCode: Int = TODO("insert your own custom identifier for tracking vipps app intents")
    }

    // use deeplinkUrl from ecom response payload
    fun openVippsApp(deeplinkUrl: String) {
        try {
            val pm = applicationContext.packageManager
            val packageInfo = pm.getPackageInfo(vippsPackageName, PackageManager.GET_ACTIVITIES)
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse(deeplinkUrl))
            startActivityForResult(intent, vippsRequestCode)
        } catch (e: PackageManager.NameNotFoundException) {
            // No Vipps app installed! Open the Vipps page on Google Play
            val playStoreUrl = "https://play.google.com/store/apps/details?id=$vippsPackageName"
            val intent = Intent(Intent.ACTION_VIEW).setData(Uri.parse(playStoreUrl))
            startActivity(intent)
        }
    }
}
```

##### Switching back to the merchant app from Vipps app

Once the user has paid (or cancelled), Vipps supports two ways to return to the merchant native app:

1: Let Vipps deeplink back into the merchant app using their URL scheme (e.g., merchantapp://). This is the default/suggested approach.

2: Just close Vipps, fall back to the merchant app, pick up the thread again there in `onActivityResult()`.

In both cases, the merchant app should query the eCom API for updated status on the payment once user returns from Vipps.

##### Return to merchant app by actively deeplinking into it from Vipps

With this approach, the merchant app has to have its own URL scheme registered so Vipps can actively open the merchant app again after payment/cancellation.

In the example below, `MainActivity` is the receiving activity and Vipps opens it once the payment is done.

To receive a call back from Vipps to an activity, a filter has to be set for that activity. In the merchant app, set a filter in the Manifest file:

```xml
<activity android:name=".MainActivity" android:label="@string/app_name" android:launchMode="singleInstance">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="sampleApps" />
  </intent-filter>
</activity>
```

**Please note:** The scheme should be same specified in `fallBack` URL sent to the eCom API when the payment is created.

The Vipps application will send the result to the merchant app by
starting a new activity with the `fallBack` URL as a URL parameter in the intent.

The merchant app can make their receiving activity a `singleInstance`
to handle the response in the same activity.

The receiving `MainActivity` has to override the `onNewIntent` method to handle
result send by Vipps:

```kotlin
override fun onNewIntent(intent: Intent) {
      // Call the eCom API, check the status of the eCom payment
}
```

##### Redirect back to merchant app by simply closing Vipps

With this approach, the merchant app does not have to register/handle deeplink URLs.

In order to use this approach, when creating the payment, the merchant has to pass the fallback attribute like this:

`"fallBack": "INTENT"`

(and *only* "INTENT", with no additional parameters)

This will cause Vipps to simply close after a successful or cancelled eCom payment, and fall back to the calling merchant app.

The merchant app activity that resumes again (after Vipps closes) has to override `onActivityResult` method to pick up the thread again here. Example:

```kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    // Call the eCom API, check the status of the eCom payment
}
```

### Testing

To facilitate automated testing in the
[Merchant Test environment (MT)](https://developer.vippsmobilepay.com/docs/knowledge-base/test-environment.md)
the eCom API provides a "force approve" endpoint to avoid manual
payment confirmation in the Vipps app:
[`POST:/ecomm/v2/integration-test/payments/{orderId}/approve`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)

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

**Important:** All test users must manually approve at least one payment in
Vipps (using the app) before "force approve" can be used for that user.
If this has not been done, you will get an error.
This is because the user needs to be registered as
"BankID verified" in the backend, and this happens automatically in
the test environment when using Vipps (the app), but not with "force approve".

**Please note:** Express and `skipLandingPage` are
not supported by the force approve endpoint.

See
[General info: Recommendations regarding handling redirects](https://developer.vippsmobilepay.com/docs/knowledge-base/redirects.md).

## Express

**INFO**

All new integrations should use the
[ePayment API](https://developer.vippsmobilepay.com/docs/APIs/epayment-api/api-guide/features/express.md).

Using Vipps Hurtigkasse today?
Check out our updated [guide for moving from eCom to ePayment](https://developer.vippsmobilepay.com/docs/APIs/epayment-api/migration.md#express).

Express (also called *Vipps Hurtigkasse* ) allows the user to select a shipping option
and share their address with the merchant through the Vipps app.

Four-screen Vipps Express flow: (1) Product page with "Vipps Hurtigkasse" button. (2) Information sharing screen showing name, phone, email, and address with a "Share information" button. (3) Delivery options (pick-up point, home delivery, mailbox, in-store). (4) Payment summary showing 538 kr with Mastercard and delivery selection.

The user can get to the *Choose address* page through a *Change* button on the *Choose delivery option* page.

Two Vipps app screens: Left: delivery options with a "Change" button for the current address. Right: address picker showing Home and Work addresses with an "Other" option.

Express is designed for shipping products with a delivery address and a
shipping method. It's mandatory for the user to select the address and shipping method to ensure that
the item is delivered to the right place in the right way.

Vipps Hurtigkasse works this way, as seen from the user's side:

1. The user clicks the *Vipps Hurtigkasse* button.
2. The user consents to sharing address information in Vipps.
3. The user confirms the amount, delivery address and delivery method in Vipps.

**NOTE**

* If you only need the user's information, you should use
  a normal payment and the
  [Userinfo API](https://developer.vippsmobilepay.com/docs/APIs/userinfo-api/README.md).
* It is not possible to specify `scope`  with Express.
* If you only need the user's information, you should use
  [Profile sharing](#profile-sharing).
  You should avoid asking the customer in a pub for the shipping method for the drinks.

To perform an Express, specify
`"paymentType": "eComm Express Payment"` in the
[`POST:/ecomm/v2/payments`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
call, and support the
[`POST:[shippingDetailsPrefix]​/v2​/payments​/{orderId}​/shippingDetails`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/fetchShippingCostUsingPOST)
and
[`DELETE:[consentRemovalPrefix]/v2/consents/{userId}`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/removeUserConsentUsingDELETE)
endpoints.

Detailed Express example

```json
Request body:
{
  "merchantInfo": {
    "authToken": "261503c6-0f79-406e-8824-b378db16c988",
    "callbackPrefix":"https://example.com/callbacks-for-payment-update",
    "consentRemovalPrefix": "https://example.com/consent-removal",
    "fallBack": "https://example.com/fallback-result-page-for-both-success-and-failure/acme-shop-123-order123abc",
    "merchantSerialNumber": "123456",
    "paymentType": "eComm Express Payment",
    "shippingDetailsPrefix": "https://example.com/shipping",
    "staticShippingDetails": [
      {
          "isDefault": "Y",
          "priority": 1,
          "shippingCost": 59,
          "shippingMethod": "In the mailbox",
          "shippingMethodId": "shippingMethodId1"
      },
      {
          "isDefault": "N",
          "priority": 2,
          "shippingCost": 69,
          "shippingMethod": "Helthjem home delivery",
          "shippingMethodId": "shippingMethodId2"
      },
      {
          "isDefault": "N",
          "priority": 3,
          "shippingCost": 99,
          "shippingMethod": "PostNord home delivery",
          "shippingMethodId": "shippingMethodId3"
      },
      {
          "isDefault": "N",
          "priority": 4,
          "shippingCost": 99,
          "shippingMethod": "Pickup at post output",
          "shippingMethodId": "shippingMethodId4"
      }
    ]
  },
  "customerInfo": {
    "mobileNumber": "12345678"
  },
  "transaction": {
    "amount": 49900,
    "orderId": "2810171754486712992",
    "transactionText": "Express purchase of socks."
  }
}
```

### Endpoints required on the merchant side

The below endpoints are provided by the *merchant* and consumed by Vipps during
Express payments. These endpoints are not required when using
regular checkout.

These endpoints are to be implemented by merchants in order for Vipps
to make calls to them. The documentation is included in the API specification for
reference only - these endpoints are *not* callable at Vipps.

| Operation            | Description                                                                       | Endpoint |
|:---------------------|:----------------------------------------------------------------------------------|:-|
| Get shipping details | Used to fetch shipping information, **10-second timeout** | [`POST:[shippingDetailsPrefix]/v2/payments/{orderId}/shippingDetails`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/fetchShippingCostUsingPOST) |
| Transaction Update   | A callback to the merchant for receiving post-payment information.                | [`POST:[callbackPrefix]/v2/payments/{orderId}`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/transactionUpdateCallbackForRegularPaymentUsingPOST) |
| Remove user consent  | Used to inform merchant when the Vipps user removes consent to share information. | [`DELETE:[consentRemovalPrefix]//v2/consents/{userId}`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/removeUserConsentUsingDELETE) |

### Shipping and static shipping details

The shipping methods presented to the user in Vipps are fetched from the merchant's
[`POST:[shippingDetailsPrefix]​/v2​/payments​/{orderId}​/shippingDetails`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/fetchShippingCostUsingPOST)
endpoint.

If the shipping methods and cost can be known in advance, the `staticShippingDetails` field in
[`POST:​/ecomm​/v2​/payments`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
may be used to provide the shipping details up front, and thereby avoid an
extra round-trip between the Vipps backend and the merchant's server.

When using `staticShippingDetails` the shipping costs for the available
shipping methods are then sent directly, eliminating the need for the user to
first select shipping method and then for the merchant to calculate the cost for it.

We recommend using `staticShippingDetails` if possible, as it gives a faster
payment process and a better user experience. It also eliminates timeout problems
caused by delays in the merchant's or shipping partner's calculations of cost.

Detailed example of get shipping details

The
[`POST:[shippingDetailsPrefix]/v2/payments/{orderId}/shippingDetails`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/fetchShippingCostUsingPOST)
API endpoint *on the merchant's side* allows Vipps to get the shipping cost and
method based on the provided address and product details.
This endpoint is required for Express.

Request:

```json
{
  "addressId": 3960,
  "addressLine1": "Dronning Eufemias gate 42",
  "addressLine2": null,
  "country": "Norway",
  "city": "OSLO",
  "postCode": "0191",
  "addressType": "H"
}
```

Response:

```json
{
  "addressId": 3960,
  "orderId": "123456abc",
  "shippingDetails": [
    {
      "isDefault": "N",
      "priority": 1,
      "shippingCost": 99,
      "shippingMethod": "Walking",
      "shippingMethodId": "123abc"
    },
    {
      "isDefault": "Y",
      "priority": 2,
      "shippingCost": 199,
      "shippingMethod": "Running",
      "shippingMethodId": "321abc"
    }
  ]
}
```

Sequence diagram with and without `staticShippingDetails`:

```mermaid
sequenceDiagram
    autonumber
    Merchant->>+Vipps: Express payment initiated: 8000 NOK (possibly w/staticShippingDetails)
    Vipps->>+User: Please confirm sharing your name and address with Merchant
    Note over User: The user does not yet see the amount
    User->>+User: Selects one of the addresses in the user profile
    User->>+Vipps: Ok, I consent to sharing my name and this address
    opt Additional steps when NOT using staticShippingDetails
        Vipps->>+Merchant: What are the shipping methods and costs for the user's address?
        Merchant->>+Merchant: Calculates
        Merchant->>+Vipps: Sends a list of shipping methods and costs
    end
    Vipps->>+User: Please select one of these shipping methods
    User->>+Vipps: Selects a shipping method that costs 400 NOK
    Vipps->>+Vipps: Updates the payment amount: 8000 + 400 NOK
    Vipps->>+User: Please confirm the total sum: 8400 NOK
    Note over User: The user can now see the amount
    User->>+Vipps: Confirms payment
    Vipps->>+Vipps: Reserves 8400 NOK on the user's card
    Vipps->>+Merchant: Payment confirmed, 8400 NOK is reserved
    Note over Merchant: Merchant packages product and sends it
    Merchant->>+Vipps: Capture 8400 NOK
    Vipps->>+Merchant: Capture 8400 OK
    Merchant->>+Vipps: Adds order details and/or tracking link with the Order Management API
    Note over User: Can see the order details and tracking link in Vipps
    Merchant->>+User: Order confirmation email, package tracking details, and similar communications
```

  Merchant initiates an Express payment with Vipps for 8000 NOK, optionally including static shipping details.
  Vipps asks the user to confirm sharing their name and address with the merchant. The user does not yet see the total amount.
  User selects one of the addresses in their Vipps profile.
  User consents to sharing their name and selected address with Vipps.
  If static shipping details are not provided: Vipps asks the merchant for available shipping methods and costs for the user's address. The merchant calculates and returns the list.
  Vipps asks the user to select a shipping method.
  User selects a shipping method costing 400 NOK.
  Vipps updates the total payment amount to 8400 NOK (8000 + 400).
  Vipps asks the user to confirm the total sum of 8400 NOK. The user can now see the amount.
  User confirms the payment.
  Vipps reserves 8400 NOK on the user's card and notifies the merchant that payment is confirmed.
  Merchant packages the product and sends it, then captures 8400 NOK via Vipps. Vipps confirms the capture.
  Merchant adds order details and/or a tracking link using the Order Management API. The user can now see the order details and tracking link in Vipps.
  Merchant sends an order confirmation email, package tracking details, and similar communications to the user.

To get information about the payment, use [callbacks](#callbacks) and polling.

### Remove user consent

Vipps complies with GDPR, and requires the user's consent before any information
is shared with the merchant.

For Express, you must provide a URL (`consentRemovalPrefix`)
that Vipps can call to delete the data. Vipps allows the user to later
remove this consent (via the Vipps app: *Profile* > *Security* > *Access to your information* > *Companies that remember you*).

**NOTE**

The personal information shared with the merchant is available for 7 days after [reservation](#reserve).
After this, all customer information will be replaced with `"[Expired]"` in subsequent calls to
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml).

The
[`DELETE:[consentRemovalPrefix]/v2/consents/{userId}`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/removeUserConsentUsingDELETE)
API endpoint *on the merchant's side* allows Vipps to send an end user's consent
removal request to the merchant.
This endpoint is required for Express.

When receiving this request,
you are obliged to handle the user details as per the GDPR guidelines.
The request path will include a `userId` that Vipps will have provided as
part of callback and also made accessible through [`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml).

### Troubleshooting Express

#### Express fails

* Often caused by slow server response when Vipps retrieves shipping methods.
* Solution: Speed up your server or include shipping methods in the initial payment request.

Tip:  For non-shipping scenarios (bars, restaurants), use [Profile Sharing](#profile-sharing) instead.

Details:

When using Express, Vipps makes a callback to the
merchant's server to retrieve the merchant's shipping methods for the user's
address. Vipps sends the user's address (with the user's consent) to the
merchant, and the merchant responds with the shipping methods and cost.

If the merchant's server is slow, or has a slow internet connection,
the delay may cause Vipps Hurtigkasse to fail due to a timeout.

The solution to this is a faster server and internet connection, or to provide
the shipping methods as part of the payment initiation. See:
[Endpoints required on the merchant side](#endpoints-required-on-the-merchant-side).

We strongly recommend checking the full history of every Vipps payment with
the API: You can see if a payment has been actively rejected, if the user has
not taken any action.
See: [Get payment details](#get-payment-details).

#### `Payment failed` message

* Caused by incorrect response to
  [`[shippingDetailsPrefix]/v2/payments/{orderId}/shippingDetails`](https://developer.vippsmobilepay.com/api/ecom#tag/Merchant-Endpoints/operation/fetchShippingCostUsingPOST).

Verify that your response format and content are correct.

For a faster payment flow and better user experience, consider using
[static shipping methods](#shipping-and-static-shipping-details).

## Profile sharing

You can ask for the user's profile information as part of the payment flow.

To request profile information for a user, the merchant can add a `scope`
parameter to the initiate call:
[`POST:/ecomm/v2/payments`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml).

See [Userinfo API guide](https://developer.vippsmobilepay.com/docs/APIs/userinfo-api/userinfo-api-guide.md) for more details.

### Profile sharing call-by-call guide

Scenario: You want to complete a payment and get the name and phone number of
a customer. Details about each step are described in the sections below.

1. Retrieve the access token:
   [`POST:/accesstoken/get`](https://developer.vippsmobilepay.com/redocusaurus/access-token-swagger-id.yaml).
2. Add scope to the transaction object and include the scope you wish to get
   access to (valid scope) before calling
   [`POST:/ecomm/v2/payments`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
   Include the scopes you need access to (e.g., "`name address email phoneNumber birthDate`"), separated by spaces.
3. The user consents to the information sharing and performs the payment in Vipps.
4. Retrieve the `sub` by calling
   [`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml)
5. Using the `sub` from step 4, call
   [`GET:/vipps-userinfo-api/userinfo/{sub}`](https://developer.vippsmobilepay.com/redocusaurus/userinfo-swagger-id.yaml)
   to retrieve the user's information.

An example similar to this, but with ePayment, is shown in the
[Userinfo quick start guide](https://developer.vippsmobilepay.com/docs/APIs/userinfo-api/userinfo-api-quick-start.md).

**Please note:** The `sub` is added asynchronously, so if the `/details` request
is made within (milli)seconds of the payment approval in the app, it may not be
available. If that happens, simply make another `/details` request.
See
[Polling guidelines](https://developer.vippsmobilepay.com/docs/knowledge-base/polling-guidelines.md)
for more recommendations.

### Get profile information

Once the user completes the session, a unique identifier `sub` can be retrieved from the
[`GET:/ecomm/v2/payments/{orderId}/details`](https://developer.vippsmobilepay.com/redocusaurus/ecom-swagger-id.yaml) endpoint.

Example `sub` format:

```json
"sub": "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 Userinfo API:
[`GET:/vipps-userinfo-api/userinfo/{sub}`](https://developer.vippsmobilepay.com/redocusaurus/userinfo-swagger-id.yaml)

For more details, see:
[Userinfo API](https://developer.vippsmobilepay.com/docs/APIs/userinfo-api/userinfo-api-guide.md).

### Troubleshooting profile sharing

#### Missing `sub` field

If you use the correct `scope` in the payment initiation, but don't get the
`sub` in the response for `/details`:

Check that your integration follows the [`orderId` recommendations](https://developer.vippsmobilepay.com/docs/knowledge-base/orderid.md).

Very short `orderId` values can cause database indexing issues that may lead to internal timeouts.
In such cases, the response may be sent without the `sub` value.

## One-time payment QR codes

The one-time payment QR feature allows you to display a QR code that opens a payment request directly from a customer-facing screen.
This removes the need for the customer to manually enter their mobile number.

This is built-in to the [ePayment API](https://developer.vippsmobilepay.com/docs/APIs/epayment-api/api-guide/features/qr-payments.md), but you can combine this with the eCom API as follows:

1. Initiate an eCom payment (`skipLandingPage` must be set to `false`).
2. Receive the payment URL in the response.
3. Create a [`POST:/qr/v1`](https://developer.vippsmobilepay.com/redocusaurus/qr-swagger-id.yaml) request with the `url` set to the payment URL.
4. In the response, you will get a URL to a QR code in PNG format.
5. Display the QR code on the customer-facing screen.
6. The customer scans the QR code with the Vipps app, or their phone's camera app.
7. The customer pays (or cancels the payment) in the app.
8. The `fallbackURL` is triggered and shown on the customer's mobile phone.

**NOTE**

* The customer-facing screen will not display the fallback page.
  We recommend showing the payment verification result on-screen in another way, and also displaying an error message if something goes wrong.
* If the POS solution cannot handle a `fallbackURL`, you may set it to the merchant's website.

For troubleshooting, see:

* [Callbacks](#callbacks)
* [Error codes](#error-codes)

> **Full site overview:** For every page in this documentation, read [https://developer.vippsmobilepay.com/llms.txt](https://developer.vippsmobilepay.com/llms.txt).
