# CoverPay documentation (full)
Base URL https://api.coverpayme.com. Index: https://coverpayme.com/llms.txt
---
# CoverPay quickstart
CoverPay is one checkout for Klarna, Affirm, Afterpay and Zip, smart-routed for each order, with CoverPay's own pay-in-4 filling in when no provider approves. It works like Plaid Link: your server creates a **link token**, your page or app opens **Link** with it, and your server exchanges the **public token** Link hands back.
The amount, order and customer live on the link token, created with your secret key. Nothing about the payment comes from the browser or the app, so it cannot be changed there.
## Flow
1. Server: `POST /v1/link/token/create` with your secret key and the order amount from your own catalog. Returns `link_token`.
2. Client: open Link with only the `link_token` (Web, React or iOS SDK).
3. The shopper picks a plan and approves with the provider. Link calls `onSuccess` with a `public_token`.
4. Server: `POST /v1/link/public_token/exchange` returns the payment.
5. Webhooks are the source of truth for whether an order is paid.
Base URL: `https://api.coverpayme.com`. Auth: header `x-api-key: cp_test_…` (sandbox) or `cp_live_…` (production). Money is integer cents. Machine-readable spec: https://coverpayme.com/openapi.yaml
## 1. Get your keys
Create a test key (`cp_test_…`) in the dashboard at https://app.coverpayme.com/api-keys. Keep it on your server. Test keys work only in sandbox; live keys only in production.
## 2. Create a link token (server)
```js
const res = await fetch('https://api.coverpayme.com/v1/link/token/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.COVERPAY_SECRET_KEY },
body: JSON.stringify({
amount: 12000, // cents, from YOUR catalog or cart, never from the client
currency: 'USD',
merchantName: 'My Store',
orderId: 'order_123', // comes back on the exchange and in webhooks
customerId: 'user_456',
customerEmail: 'buyer@example.com',
}),
});
const { link_token, expiration } = await res.json(); // "lt_test_…"
```
## 3. Open Link (client)
Web (`npm install @coverpay/sdk`):
```js
import { CoverPayLink } from '@coverpay/sdk';
const callbacks = {
onSuccess: (publicToken, metadata) => { /* POST publicToken to your server */ },
onExit: (error, metadata) => { /* closed (error null) or failed (error.code, error.message) */ },
};
CoverPayLink.handleRedirect(callbacks); // once on every load of the checkout page
const link = CoverPayLink.create({ token: linkToken, ...callbacks });
button.addEventListener('click', () => link.open());
```
React (`@coverpay/react`):
```tsx
const { open, ready } = useCoverPayLink({ token: linkToken, onSuccess, onExit });
return ;
```
iOS (Swift, `CoverPaySDK`):
```swift
CoverPayLink.open(linkToken: linkToken, from: self) { result in
switch result {
case .success(let payment): sendToServer(payment.publicToken)
case .failure(.userCancelled): break
case .failure(let error): show(error)
}
}
```
## 4. Exchange the public token (server)
```js
const res = await fetch('https://api.coverpayme.com/v1/link/public_token/exchange', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.COVERPAY_SECRET_KEY },
body: JSON.stringify({ public_token: publicToken }),
});
const payment = await res.json();
// { payment_id, status: "captured", provider: "zip", amount: 12000, currency: "USD", order_id: "order_123", plan? }
```
| Field | Meaning |
|---|---|
| `payment_id` | CoverPay payment id; also in your webhooks. |
| `status` | `captured` or `authorized`: the shopper's plan went through. |
| `order_id` | Your order id from the link token. Check it matches the order you are completing. |
| `plan` | Only when `provider` is `coverpay`: the 4-payment schedule and your payout. |
Exchanging the same public token again is safe: you get the same payment back and no money moves. Other businesses cannot exchange your tokens (404). Public tokens expire after 30 minutes.
## 5. Listen for webhooks
Register an endpoint for `session.captured` and `session.authorized`. When one arrives, read the payment back with your key (`GET /v1/bnpl/sessions/{payment_id}`), check order id and amount, then mark the order paid. See webhooks.md.
---
# CoverPay Link SDKs
All three SDKs open CoverPay's checkout from a **link token** your server created. The SDK never holds a secret key and never sets the amount. `onSuccess` returns a **public token**; your server exchanges it (`POST /v1/link/public_token/exchange`). Exactly one of `onSuccess` / `onExit` runs per open.
Providers (Klarna, Affirm, Afterpay, Zip) and CoverPay's own plan take the shopper's whole page to approve, then send them back to the page that opened Link. Web and React handle that return; call it on every load of the checkout page.
## Web / JavaScript: `@coverpay/sdk`
```js
import { CoverPayLink } from '@coverpay/sdk';
const callbacks = {
onSuccess: (publicToken, metadata) => exchangeOnYourServer(publicToken),
onExit: (error, metadata) => {},
};
CoverPayLink.handleRedirect(callbacks); // once on page load; returns true if it handled a return
const link = CoverPayLink.create({ token: linkToken, ...callbacks, onEvent: (name, metadata) => {} });
payLaterButton.addEventListener('click', () => link.open());
```
| Option | Type | Description |
|---|---|---|
| `token` | string | Required. `lt_test_…` or `lt_live_…`. Anything else throws immediately. |
| `onSuccess` | `(publicToken, metadata) => void` | Required. `publicToken` is `null` only when the provider approved but CoverPay had not confirmed yet: do not charge again, wait for your webhook. |
| `onExit` | `(error, metadata) => void` | Shopper closed Link (`error` null) or it failed (`error.code`, `error.message`). |
| `onEvent` | `(name, metadata) => void` | Funnel events: `OPEN`, `OFFERS_LOADED`, `OFFER_SELECTED`, `STEP_CHANGE`, `CLOSE`. |
Handler methods: `open()`, `exit()` (close as if the shopper did), `destroy()` (remove without callbacks).
`onSuccess` metadata:
```js
{ provider: "zip", // klarna | affirm | afterpay | zip | coverpay
sessionId: "pi_3UO3…", // CoverPay payment id (also returned by the exchange)
status: "succeeded", // succeeded | requires_capture | processing
paymentIntentId: "pi_3UO3…" // Stripe PaymentIntent for Stripe-backed providers, else null
}
```
The return result is in the URL fragment (`#coverpay_status=success&coverpay_public_token=public-sandbox-…&coverpay_provider=zip`), so it never reaches server logs. The public token is useless without your secret key.
Deprecated: `CoverPay.create({ clientId }).open({ amount })` works only in sandbox (amount from the browser). `checkEligibility` always rejects: call `POST /v1/checkout/verify-eligibility` from your server.
## React: `@coverpay/react`
```tsx
import { useCoverPayLink } from '@coverpay/react';
function PayLaterButton({ linkToken }: { linkToken: string | null }) {
const { open, ready, error } = useCoverPayLink({
token: linkToken,
onSuccess: (publicToken, metadata) => { /* POST to your server */ },
onExit: (error, metadata) => {},
});
return ;
}
```
Returns `open()` (call from a click), `ready` (true once a valid token is set), `exit()`, `error` (set if the token is not a CoverPay link token). The hook handles the redirect return on mount, so mount it on the page that opened Link.
## iOS: `CoverPaySDK` (Swift Package Manager, iOS 14+)
```swift
import CoverPaySDK
CoverPayLink.open(linkToken: linkToken, from: self) { result in
switch result {
case .success(let payment): // payment.publicToken, .provider, .sessionId, .status
api.completeOrder(publicToken: payment.publicToken, orderId: orderId)
case .failure(.userCancelled): break
case .failure(let error): showError(error.localizedDescription)
}
}
```
SwiftUI: `.coverPayLink(isPresented: $showCheckout, linkToken: linkToken) { result in … }`.
`publicToken` is nil only if the provider approved but CoverPay had not confirmed yet: rely on your webhook. The completion runs exactly once; swiping the sheet down counts as a cancel. `checkEligibility` on iOS fails immediately: call `POST /v1/checkout/verify-eligibility` from your server. `open(params:)` (amount from the app) is deprecated and sandbox-only.
## Server: exchange
```
POST https://api.coverpayme.com/v1/link/public_token/exchange
x-api-key: cp_test_…
{ "public_token": "public-sandbox-…" }
```
See quickstart.md and openapi.yaml.
---
# CoverPay webhooks
Webhooks are the source of truth for whether an order is paid. Link's `onSuccess` and the public token exchange are fast paths; a webhook still arrives if the shopper closed their browser or your server missed the answer. Register endpoints in the dashboard (https://app.coverpayme.com, Webhooks).
## Events
| Event | When |
|---|---|
| `session.authorized` | The shopper approved a plan; funds are authorized (captured later). |
| `session.captured` | The order is paid. Mark it paid (once per `payment_id`). |
| `session.declined` | The provider declined. |
| `session.cancelled` | The payment was cancelled. |
| `session.expired` | The checkout expired unpaid. |
| `credit.installment_paid` | CoverPay plan: a later payment (2 to 4) was collected. |
| `credit.installment_failed` | CoverPay plan: a payment failed; it retries after 24 hours. |
## Payload
```json
{
"id": "evt_3UO1…",
"type": "session.captured",
"created": "2026-10-07T21:05:00.000Z",
"data": {
"payment_id": "pi_3UO1…",
"order_id": "order_123",
"amount": 12000,
"currency": "USD",
"provider": "zip",
"session_status": "captured"
}
}
```
Treat the event as a hint. Before marking an order paid, read the payment back with your key: `GET /v1/bnpl/sessions/{payment_id}`. Check the order id and amount match your order.
## Verify the signature
Every delivery has `X-CoverPay-Signature-V2: t=,v1=`, an HMAC-SHA256 of `"."` with your endpoint's signing secret. Verify over the raw body before parsing, and reject timestamps more than 5 minutes old. The older `X-CoverPay-Signature` (HMAC of the body without a timestamp) is still sent; prefer V2.
```js
import crypto from 'crypto';
function verifyCoverPay(rawBody, header, secret) {
if (!header) return false;
const parts = header.split(',').map((p) => p.trim().split('='));
const t = Number(parts.find(([k]) => k === 't')?.[1]);
const v1s = parts.filter(([k]) => k === 'v1').map(([, v]) => v);
if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return v1s.some((v) => v.length === expected.length && crypto.timingSafeEqual(Buffer.from(v), Buffer.from(expected)));
}
```
## Delivery and retries
Answer 2xx once you have recorded the event. Any other answer, or no answer within the timeout, is retried after 1 minute, 5 minutes and 30 minutes. Events can arrive more than once and out of order: record each `payment_id` once.
## Checklist
- Verify `X-CoverPay-Signature-V2` before parsing; return 401 if it fails.
- Read the payment back with your key; check order id and amount.
- Record once per `payment_id` (the same order can also arrive through the public token exchange).
- Return 2xx quickly; do slow work afterwards.
---
# CoverPay errors
Errors are JSON: `{ "error": "human message", "code": "MACHINE_CODE" }`. Validation failures add `details`.
| HTTP | `code` | Meaning | What to do |
|---|---|---|---|
| 400 | `INVALID_LINK_TOKEN` | Link token missing or malformed. | Create a new one on your server. |
| 400 | `INVALID_PUBLIC_TOKEN` | `public_token` missing or does not start with `public-`. | Send the token Link returned. |
| 400 | (no code) `Invalid parameters` | Request body failed validation; see `details`. | Fix the fields listed. |
| 401 | `INVALID_LINK_TOKEN` | Link token not valid for this use. | Create a new one. |
| 401 | (no code) | Missing or invalid API key. | Send `x-api-key` with a key that matches the environment. |
| 404 | `INVALID_PUBLIC_TOKEN` | Not found, not yours, or the payment is not completed. | Do not retry; the same answer is given for other businesses' tokens. |
| 410 | `EXPIRED_LINK_TOKEN` | Link token expired (30 minutes for checkout). | Create a new one. |
| 410 | `USED_LINK_TOKEN` | Link token already used or finished. | Create a new one for a new attempt. |
| 410 | `EXPIRED_PUBLIC_TOKEN` | Public token older than 30 minutes. | Read the payment from your webhook or `GET /v1/bnpl/sessions/{payment_id}` instead. |
Link `onExit(error)` codes include `PAYMENT_FAILED` when the shopper's approval fails at the provider.
---
# CoverPay sandbox testing
Test keys (`cp_test_…`) create sandbox link tokens (`lt_test_…`). Payments go through Stripe's test mode: no real money moves.
## Provider payments
Pick Klarna, Affirm, Afterpay or Zip in Link. You land on the provider's test page.
| Provider | Test page | To approve | To fail |
|---|---|---|---|
| Affirm, Afterpay, Zip | Stripe's test payment page | Authorize test payment | Fail test payment |
| Klarna | Klarna's playground | Follow the test login | Cancel |
Approving returns `onSuccess` with a public token; failing returns `onExit` with `PAYMENT_FAILED`. Each provider shows only for amounts in its range.
## CoverPay's own plan
CoverPay fills in when no provider approves: pay in 4, every 2 weeks, 0% APR, up to $500 per shopper (minus what they still owe), from $10. To see it, make every provider decline with a sandbox-only flag on the link token:
```json
POST /v1/link/token/create
{ "amount": 12000, "currency": "USD", "merchantName": "My Store", "orderId": "order_1",
"customerId": "user_1", "metadata": { "coverpay_test": "providers_decline" } }
```
Pick CoverPay, then pay the first payment on Stripe's page with a test card:
| Card | What happens |
|---|---|
| `4242 4242 4242 4242` | Succeeds now and later. |
| `4000 0000 0000 0341` | Saves, then every later charge fails. |
Any future expiry, any CVC, ZIP 94103.
## Collecting the rest without waiting
Payments 2 to 4 are charged automatically when due. In sandbox:
```
POST /v1/credit/loans/{payment_id}/make-due # next payment due now; the collector charges it
POST /v1/credit/loans/{payment_id}/collect-next # or charge it immediately
GET /v1/credit/loans/{payment_id} # plan, schedule and your payout
```
A failed payment marks the plan `behind` and is retried after 24 hours, up to 3 times.
## Your payout
When payment 1 succeeds, CoverPay pays you the plan amount minus your CoverPay fee (0% in sandbox) through Stripe Connect. Set up payouts once with `POST /v1/payouts/onboarding` (Stripe-hosted, test values accepted) and check them with `GET /v1/payouts`.
## Things that should be refused
| Try | Expect |
|---|---|
| Change the amount in the browser before paying | The payment is for the link token amount anyway |
| Open Link with an API key instead of a link token | Throws before opening |
| Exchange a public token with another business's key | 404 |
| Reuse a finished link token | 410 `USED_LINK_TOKEN` |
| Send a webhook without a valid signature | 401 |
Use test cards and Stripe's test identity values only. Never real personal or bank details.
---
# OpenAPI spec (openapi.yaml)
```yaml
openapi: 3.1.0
info:
title: CoverPay API
version: "1.0"
summary: One integration for Klarna, Affirm, Afterpay and Zip, smart-routed, with CoverPay's own pay-in-4 as the fallback.
description: |
CoverPay works like Plaid Link. Your server creates a **link token** with your secret key, your page or app
opens **Link** with that token, and your server exchanges the **public token** Link returns for the payment.
Flow:
1. `POST /v1/link/token/create` (server, secret key). Amount, order and customer live on the token.
2. Open Link in the browser or app with only the `link_token` (Web, React or iOS SDK).
3. Link calls `onSuccess` with a `public_token`. Send it to your server.
4. `POST /v1/link/public_token/exchange` (server, secret key) returns the payment.
5. Webhooks are the source of truth for whether an order is paid.
Rules that hold everywhere:
- Amounts are integer **cents**. The amount always comes from the link token, never from the client.
- Secret keys (`cp_test_…`, `cp_live_…`) belong on your server only. Test keys work only in sandbox, live keys only in production.
- Errors look like `{ "error": "message", "code": "MACHINE_CODE" }`.
- Human docs: https://coverpayme.com/docs. Plain-text docs for AI tools: https://coverpayme.com/llms.txt.
contact:
url: https://coverpayme.com/docs
servers:
- url: https://api.coverpayme.com
description: Production API (test keys are accepted for sandbox payments)
security:
- ApiKey: []
tags:
- name: Link
description: Link tokens and public token exchange (Plaid-style).
- name: Payments
description: Read a payment back after it completes.
- name: CoverPay plans
description: CoverPay's own pay-in-4 plan, used when no provider approves.
- name: Payouts
description: Merchant payouts through Stripe Connect.
paths:
/v1/link/token/create:
post:
tags: [Link]
operationId: createLinkToken
summary: Create a link token
description: |
Server-side. Creates a short-lived token that carries the amount, order and customer. Pass only the
returned `link_token` to the client. Default lifetime is 30 minutes for `checkout`.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/LinkTokenCreateRequest"
examples:
checkout:
value:
amount: 12000
currency: USD
merchantName: My Store
orderId: order_123
customerId: user_456
customerEmail: buyer@example.com
sandboxProvidersDecline:
summary: Sandbox only. Makes every provider decline so CoverPay's own plan is offered.
value:
amount: 12000
orderId: order_1
customerId: user_1
metadata:
coverpay_test: providers_decline
responses:
"200":
description: Link token created.
content:
application/json:
schema:
type: object
required: [link_token, expiration, request_id]
properties:
link_token:
type: string
description: "`lt_test_…` or `lt_live_…`. Safe to give to the client."
expiration:
type: string
format: date-time
request_id:
type: string
format: uuid
"400":
$ref: "#/components/responses/InvalidParameters"
"401":
$ref: "#/components/responses/Unauthorized"
/v1/link/token/invalidate:
post:
tags: [Link]
operationId: invalidateLinkToken
summary: Invalidate an unused link token
description: Server-side. Cancels a token that has not been used. A token that is already used returns 410 `USED_LINK_TOKEN`.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [linkToken]
properties:
linkToken:
type: string
responses:
"200":
description: Token invalidated.
"400":
$ref: "#/components/responses/InvalidParameters"
"410":
description: The token was already used.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/v1/link/public_token/exchange:
post:
tags: [Link]
operationId: exchangePublicToken
summary: Exchange a public token for the payment
description: |
Server-side, with your secret key. Trade the `public_token` that Link's `onSuccess` returned for the payment.
Safe to repeat: if your server lost the answer, exchange the same token again and you get the same payment
back (`already_exchanged: true`). No money moves on an exchange. Public tokens expire after 30 minutes.
Another business's key gets 404, the same as a token that does not exist.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [public_token]
properties:
public_token:
type: string
description: "Starts with `public-` (for example `public-sandbox-…`)."
responses:
"200":
description: The payment.
content:
application/json:
schema:
$ref: "#/components/schemas/ExchangeResponse"
"400":
description: Missing or malformed `public_token` (`INVALID_PUBLIC_TOKEN`).
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"404":
description: Token not found, not yours, or not completed (`INVALID_PUBLIC_TOKEN`).
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"410":
description: Public token expired (`EXPIRED_PUBLIC_TOKEN`).
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/v1/bnpl/sessions/{sessionId}:
get:
tags: [Payments]
operationId: getPayment
summary: Read a payment
description: |
Server-side. The `payment_id` from the exchange and from webhooks is this `sessionId`. Before marking an
order paid from a webhook, read the payment back here and check that `orderId` and `totalAmount` match your order.
parameters:
- name: sessionId
in: path
required: true
schema:
type: string
responses:
"200":
description: The payment.
content:
application/json:
schema:
$ref: "#/components/schemas/Payment"
"404":
description: Not found for this business.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/v1/credit/loans/{id}:
get:
tags: [CoverPay plans]
operationId: getPlan
summary: Read a CoverPay plan and its schedule
description: Server-side. `id` is the `payment_id` of a payment whose `provider` is `coverpay`.
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
"200":
description: The plan.
content:
application/json:
schema:
$ref: "#/components/schemas/Plan"
/v1/credit/loans/{id}/make-due:
post:
tags: [CoverPay plans]
operationId: makePlanPaymentDue
summary: Sandbox only. Make the next plan payment due now
description: The collector then charges it within a few minutes. Sandbox keys only.
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
"200":
description: The next payment is now due.
/v1/credit/loans/{id}/collect-next:
post:
tags: [CoverPay plans]
operationId: collectNextPlanPayment
summary: Sandbox only. Charge the next plan payment immediately
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
"200":
description: The charge was attempted.
/v1/payouts/onboarding:
post:
tags: [Payouts]
operationId: startPayoutOnboarding
summary: Start Stripe-hosted payout onboarding
description: Set up where CoverPay pays you (Stripe Connect Express). Do this once per business.
responses:
"200":
description: Onboarding details, including a Stripe-hosted link to finish.
/v1/payouts:
get:
tags: [Payouts]
operationId: listPayouts
summary: List payouts
responses:
"200":
description: Payouts for this business.
webhooks:
paymentEvent:
post:
summary: A payment or plan event
description: |
Register an endpoint in the dashboard (https://app.coverpayme.com, Webhooks).
**Verify the signature** over the raw body before parsing. Header
`X-CoverPay-Signature-V2: t=,v1=`, where `v1` is HMAC-SHA256 of `"."` with your
endpoint's signing secret. Reject timestamps more than 5 minutes old. The older `X-CoverPay-Signature`
(HMAC of the body without a timestamp) is still sent; prefer V2.
**Delivery.** Answer 2xx once recorded. Any other answer, or a timeout, is retried after 1 minute,
5 minutes and 30 minutes. Events can arrive more than once and out of order: record each `payment_id` once.
**Treat the event as a hint.** Read the payment back (`GET /v1/bnpl/sessions/{payment_id}`) and check the
order id and amount before marking an order paid.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/WebhookEvent"
example:
id: evt_3UO1
type: session.captured
created: "2026-10-07T21:05:00.000Z"
data:
payment_id: pi_3UO1
order_id: order_123
amount: 12000
currency: USD
provider: zip
session_status: captured
responses:
"200":
description: Recorded.
components:
securitySchemes:
ApiKey:
type: apiKey
in: header
name: x-api-key
description: Your secret key, `cp_test_…` (sandbox) or `cp_live_…` (production). Server-side only.
responses:
InvalidParameters:
description: Invalid parameters.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
Unauthorized:
description: Missing or invalid API key.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
schemas:
Error:
type: object
required: [error]
properties:
error:
type: string
code:
type: string
description: |
Machine-readable code, for example INVALID_LINK_TOKEN, EXPIRED_LINK_TOKEN, USED_LINK_TOKEN,
INVALID_PUBLIC_TOKEN, EXPIRED_PUBLIC_TOKEN.
details:
type: array
items: {}
LinkTokenCreateRequest:
type: object
required: [amount]
properties:
amount:
type: integer
minimum: 1
description: Cents. Take it from your own catalog or cart, never from the client.
currency:
type: string
default: USD
merchantName:
type: string
orderId:
type: string
description: Your order id. It comes back on the exchange and in webhooks.
customerId:
type: string
customerEmail:
type: string
format: email
providers:
type: array
items:
type: string
description: Limit the providers offered.
mode:
type: string
enum: [choice, auto]
default: choice
description: "`choice` shows the shopper the offers; `auto` routes to the best one."
scenario:
type: string
enum: [full, bucketing, downpayment, coverpay]
default: full
allowedDomains:
type: array
items:
type: string
metadata:
type: object
additionalProperties: true
description: "Sandbox flag `coverpay_test: providers_decline` makes every provider decline."
purpose:
type: string
enum: [checkout, update, eligibility, terms]
default: checkout
description: Default lifetime is 30 minutes for `checkout` and `update`, 4 hours for `eligibility` and `terms`.
expiresInSeconds:
type: integer
minimum: 60
maximum: 86400
checkout_customization_name:
type: string
description: Named checkout configuration (for A/B testing).
ExchangeResponse:
type: object
properties:
payment_id:
type: string
description: The CoverPay payment id. The same id appears in webhooks.
status:
type: [string, "null"]
description: "`captured` or `authorized` means the shopper's plan went through."
provider:
type: [string, "null"]
description: klarna, affirm, afterpay, zip or coverpay.
amount:
type: integer
description: Cents, from the link token.
currency:
type: string
order_id:
type: [string, "null"]
description: Your order id from the link token. Check it matches the order you are completing.
customer_id:
type: [string, "null"]
completed_at:
type: [string, "null"]
format: date-time
already_exchanged:
type: boolean
plan:
$ref: "#/components/schemas/Plan"
request_id:
type: string
format: uuid
Payment:
type: object
properties:
sessionId:
type: string
provider:
type: string
status:
type: string
description: "Lifecycle: created, pending, authorized, captured, refunded; or cancelled, declined, expired."
orderId:
type: [string, "null"]
totalAmount:
type: integer
description: Cents.
currency:
type: string
checkoutUrl:
type: [string, "null"]
authorizationId:
type: [string, "null"]
captureId:
type: [string, "null"]
capturedAmount:
type: [integer, "null"]
createdAt:
type: string
format: date-time
expiresAt:
type: [string, "null"]
format: date-time
Plan:
type: object
description: A CoverPay pay-in-4 plan. Only present when `provider` is `coverpay`.
properties:
id:
type: string
status:
type: string
description: A failed payment marks the plan `behind`; it retries after 24 hours, up to 3 times.
amount:
type: integer
description: Cents.
currency:
type: string
order_id:
type: [string, "null"]
customer_id:
type: [string, "null"]
merchant_owed:
type: integer
description: Cents owed to the merchant.
installments:
type: array
items:
type: object
properties:
n:
type: integer
due_at:
type: string
format: date-time
amount:
type: integer
status:
type: string
paid_at:
type: [string, "null"]
format: date-time
payout:
type: [object, "null"]
properties:
status:
type: string
net:
type: integer
fee:
type: integer
transfer_id:
type: [string, "null"]
error:
type: string
WebhookEvent:
type: object
required: [id, type, created, data]
properties:
id:
type: string
type:
type: string
enum:
- session.authorized
- session.captured
- session.declined
- session.cancelled
- session.expired
- credit.installment_paid
- credit.installment_failed
description: |
session.captured: the order is paid. session.authorized: funds authorized, captured later.
credit.installment_paid / credit.installment_failed: CoverPay plan payments 2 to 4.
created:
type: string
format: date-time
data:
type: object
properties:
payment_id:
type: string
order_id:
type: string
amount:
type: integer
currency:
type: string
provider:
type: string
session_status:
type: string
```