# 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 <button disabled={!ready} onClick={() => open()}>Pay later</button>;
```

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.
