Checkout
Take card, bank and crypto payments from your own site with one hosted link.
Your server creates a checkout link, the buyer pays on a page we host, and a signed webhook tells you when the money has arrived. Checkout uses the same API key, webhook endpoint and event feed as payouts. Its contract is its own file, partner-checkout.openapi.yaml.
How it works
Section titled “How it works”- Set up verification, card payments, an API key and a webhook endpoint (Set up checkout).
- Create a product with one fixed price, or put a one-off amount on the link itself.
- Publish a link and redirect the buyer to its
shareUrl. - Fulfil on
checkout_payment.paid, joined to your order byclientReferenceId. - Reverse the fulfilment on
checkout_payment.refundedorcheckout_payment.reversed. A paid card payment can still be refunded or charged back.
Payment methods
Section titled “Payment methods”A link without methods takes cards, and the card page also offers Apple Pay,
Google Pay and PayPal. Pass methods to offer bank or crypto, each naming
the account or address to pay into.
Cards are processed by Whop and are in pilot, opening to verified businesses a
few at a time. Buyers see WHOP* and your business name on their statement.
Bank and stablecoin payments go live once business verification is approved.
Where the money lands
Section titled “Where the money lands”paid means the acceptor has the money. Card payments collect in your card
balance with the processor, which you withdraw from Checkout → Balance. Bank
and crypto payments arrive in the account or address the link names.
Checkout payments never touch your Avvio ledger. They get no
GET /balance_transactions row, card money never appears in GET /balance,
and settledAt is always null in this version.
Link states
Section titled “Link states”A link’s status is spelled cancelled, unlike a payout’s canceled.
status |
Meaning |
|---|---|
draft |
Not publicly readable. Edit it with PATCH or delete it |
sent |
Live. shareUrl serves the page |
cancelled |
Paused. The page answers 410 and every rail behind it stops. Permanent in this version |
expired |
Past its expiresAt. The page answers 410 from that instant, and the stored status follows within a minute. Terminal |
Publish a draft with POST /links/{linkId}/publish. A live link’s price is
fixed, because a buyer may already be looking at it. The only allowed PATCH
sets expiresAt alone (a later instant, or null); anything else is a 400.
To change the price, pause the link and create another. A link that has been
live cannot be deleted, and an expired one cannot be revived. API-made links
carry source: "api", and GET /links?source=api (or dashboard) filters on
it.
Refunds and disputes
Section titled “Refunds and disputes”Treat paid as provisional. Card payments can be refunded at any time and
charged back weeks later. Bank and stablecoin payments cannot be reversed at
the source; send the payer a payout to return one. Two changes raise no event.
An open dispute sets disputeSubstatus until it resolves. A bank deposit short
of the total stays pending with a reviewReason until someone accepts it in
the dashboard. See Refund a payment.
A link without code
Section titled “A link without code”Create and publish a link in the dashboard and paste shareUrl anywhere.
Appending ?client_reference_id=<value> carries a per-buyer reference on the
card rail only, and only best effort. The hosted-fallback and 3DS-restore paths
drop it, and a bank payer types the link’s slug instead. One link per order,
with its own clientReferenceId, is the join that works on every rail.
Embedding
Section titled “Embedding”Embedding is not supported in this version, so redirect to shareUrl and
back. The payer page sends X-Frame-Options: DENY and
Content-Security-Policy: frame-ancestors 'none'. The card form is already the
processor’s iframe, 3-D Secure and Apple Pay need the top window, browsers
partition framed storage, and with no origin allowlist a framed card page
invites clickjacking.
If you know Stripe
Section titled “If you know Stripe”| Stripe | Here |
|---|---|
POST /, session.url |
POST / with publish: true, shareUrl |
| Payment Links | A link created in the dashboard, or without publish |
success_url, cancel_url |
successUrl, cancelUrl |
client_reference_id, metadata |
clientReferenceId, metadata (same alphabets and limits) |
checkout.session.completed |
checkout_ |
POST /v1/refunds, refund.reason |
POST …/, reason (requested_, duplicate, fraudulent, or our own other) |
charge.refunded |
checkout_ or checkout_ |
charge.dispute.closed (lost) |
checkout_ |
payment_ |
POST / (permanent) |
expires_at |
expiresAt on the link |
line_items, price_data |
productId, or currency + items |
payment_ |
methods (omitted means card) |
| Amounts in minor units | Decimal strings on links and events; base units with decimals on payment rows |
There are no subscriptions and no embeddable checkout.
Testing
Section titled “Testing”A test key pays links with a simulated buyer instead of test card numbers, and the total picks the outcome. That includes chargebacks, which no card processor’s sandbox can produce (Environments & sandbox).
Integration guides
Section titled “Integration guides”Was this page helpful?