LayerHopDocsLaunch app (opens in a new tab)

Checkout (pay with any token)

Accept crypto payments in USDC or USDT while your customers pay with any token on any of 70 chains. Create a checkout session, send the payer to the hosted page, and get a checkout.session.completed webhook once LayerHop has verified the delivery on chain.

Checkout lets you get paid in a stablecoin you choose (USDC or USDT on Ethereum, Arbitrum, Base, Optimism, BNB Chain, Polygon or Solana) while your customer pays with any token on any chain LayerHop routes. You create a checkout session for an amount, send the customer to its hosted page, and LayerHop routes their payment so that at least the amount due lands in your wallet. When the delivery is verified on chain, the session becomes paid and you receive a checkout.session.completed webhook.

LayerHop never holds the funds: the payer's wallet sends them, and the route delivers straight to your address.

How it works#

  1. Your server creates a session with an API key: amount, settlement token and chain, your payout address. You get back a hosted url (https://layerhop.com/pay/cs_…). You can also create payment links by hand in the dashboard under Payments.
  2. Your customer opens the link, connects an EVM or Solana wallet and picks what to pay with. The page asks LayerHop for a route that delivers the full amount to your address and shows exactly what they pay.
  3. The payer signs one transaction (plus an approval for some ERC-20 tokens). The session moves to processing.
  4. LayerHop tracks the payment and, once it has landed, checks the settlement chain for a transfer of at least the amount due to your address. Only then is the session paid and the webhook sent. The page sends the customer back to your successUrl.
Session status Meaning
open Waiting for payment. A failed or refunded payment attempt returns the session here so the customer can try again.
processing A payment was sent and is being tracked.
paid Delivery to your address was verified on the settlement chain. Fulfil the order.
expired Not paid before expiresAt (default 60 minutes), or you expired it.
needs_review The payment completed but the delivery could not be verified automatically. Rare; contact dev@layerhop.com with the session id.

1. Create a session#

Terminal
curl -X POST https://api.layerhop.com/v1/checkout/sessions \
  -H "x-api-key: $LAYERHOP_API_KEY" -H "content-type: application/json" \
  -d '{
    "amount": "49000000",
    "settleChain": "base",
    "settleToken": "USDC",
    "recipient": "0xYourPayoutAddress",
    "description": "Pro plan, 1 month",
    "reference": "order_1042",
    "successUrl": "https://shop.example.com/thanks",
    "cancelUrl": "https://shop.example.com/cart"
  }'

With the TypeScript SDK:

TypeScript
import { LayerHop } from "@layerhop/sdk";

const lh = new LayerHop({ apiKey: process.env.LAYERHOP_API_KEY });
const session = await lh.checkout.create({
  amount: "49000000", // 49 USDC (6 decimals)
  settleChain: "base",
  settleToken: "USDC",
  recipient: "0xYourPayoutAddress",
  description: "Pro plan, 1 month",
  reference: "order_1042",
  successUrl: "https://shop.example.com/thanks",
});
// Redirect the customer to session.url
Field Notes
amount Integer string in base units of the settlement token (USDC and USDT have 6 decimals, except 18 on BNB Chain).
settleChain, settleToken One of the supported pairs: USDC on Ethereum, Arbitrum, Base, Optimism, BNB Chain, Polygon or Solana; USDT on Ethereum, Arbitrum, BNB Chain or Solana.
recipient Your payout address on the settlement chain. LayerHop sets it on every route; the payer cannot change it.
description, merchantName Shown to the payer.
reference, metadata For your records. Returned to you and in webhooks, never shown to the payer.
successUrl, cancelUrl Where the hosted page sends the payer. ?session_id=cs_… is appended to successUrl.
expiresInMinutes 5 to 10080 (7 days). Default 60.

2. Send the customer to the hosted page#

Redirect to session.url, link to it, or show it as a QR code. The page works on desktop and mobile with any EVM or Solana wallet, shows the amount due and the expiry, and walks the payer through approval, signing and confirmation. Sessions can be paid once.

3. Fulfil on the webhook#

Add a webhook endpoint subscribed to checkout.session.completed (or to all events) and verify the signature as usual. The event carries the session:

JSON
{
  "id": "evt_…",
  "type": "checkout.session.completed",
  "livemode": true,
  "createdAt": "2026-10-18T10:03:12.000Z",
  "data": {
    "session": {
      "id": "cs_01k7t9m2c4x8q6r0v3b5n7p9d",
      "status": "paid",
      "amount": "49000000",
      "settlement": { "chainId": 8453, "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "symbol": "USDC", "decimals": 6 },
      "recipient": "0xyourpayoutaddress",
      "reference": "order_1042",
      "payment": { "payer": "0x…", "srcChainId": 42161, "srcToken": "0x0000000000000000000000000000000000000000", "srcAmount": "15623001000000000", "srcTxHash": "0x…", "dstTxHash": "0x…", "paidAt": "2026-10-18T10:03:12.000Z" }
    }
  }
}

Match the session to your order with reference (or the session id) and fulfil it. The other checkout events are checkout.session.processing (a payment was sent), checkout.session.payment_failed (the attempt failed or was refunded; the session is open again) and checkout.session.expired. You can also poll GET /v1/checkout/sessions/{id}.

Tip

Treat paid as final. Do not fulfil on processing: a cross-chain payment can still be refunded.

What the payer pays#

  • Same token, same chain (for example USDC on Base for a USDC-on-Base session): a plain token transfer of exactly the amount, with no LayerHop fee.
  • Anything else: a swap or bridge route through LayerHop's providers. The route guarantees at least the amount due after slippage, so the payer sends slightly more than the face value (usually under 1%, capped at about 3%), and you may receive a little extra. The standard LayerHop API fee (0.20%) and the provider's fees are included in what the payer sees before signing.

How LayerHop verifies payments#

A session is only marked paid after LayerHop checks the settlement chain:

  • the delivering transaction succeeded and contains a transfer of the settlement token to your recipient totalling at least amount (EVM: Transfer logs; Solana: the token balance change of your account);
  • it was mined after the session was created;
  • for a direct transfer, it came from the payer;
  • it has not already paid another session (a transaction can pay only one session).

If any check fails the session goes to needs_review instead of paid.

Test mode#

Create sessions with an lh_test_ key (or with Test mode on in the dashboard). Test sessions (cs_test_…) are paid through the sandbox: the hosted page shows a Test mode badge, nothing moves on chain, and the session is paid about 10 to 20 seconds after the payment is reported. Webhooks go to your test endpoints only. Use the magic amounts to rehearse a failed or refunded payment.

Build your own payment page#

The hosted page uses public endpoints you can call yourself:

  1. GET /v1/checkout/sessions/{id} to show the amount and status (no key needed; reference and metadata are omitted).
  2. POST /v1/checkout/sessions/{id}/quote with { srcChain, srcToken, sender }. Every quote's dstAmountMin is at least the amount due, and srcAmount is what the payer sends.
  3. POST /v1/orders with the quoteId, sign the transaction in the payer's wallet, then POST /v1/orders/{quoteId}/submitted with the hash.
  4. Poll the session until it is paid.

With the SDK: lh.checkout.get(id), lh.checkout.quote(id, { srcChain, srcToken, sender }), lh.buildTx(quoteId), lh.reportSubmitted(quoteId, txHash).

Last updated