Widget integration

The widget is Cryptunnel's hosted checkout: you create a payment through the API, redirect the buyer to the returned URL, and Cryptunnel handles currency choice, the exact crypto amount, on-chain detection and the result screen. This is the recommended integration for most shops.

1. Create a payment

curl -X POST https://api.cryptunnel.io/v1/payments/widget \
  -H 'content-type: application/json' \
  -H 'x-merchant-id: <your merchant id>' \
  -H 'x-api-key: ct_live_...' \
  -d '{
    "amount": 100,
    "currency": "USD",
    "external_id": "order-1042",
    "callback_url": "https://your-shop.example/webhooks/cryptunnel",
    "success_url": "https://your-shop.example/thanks",
    "fail_url": "https://your-shop.example/payment-failed",
    "metadata": {
      "description": "PRO subscription",
      "callback_params": { "customer_id": "77" }
    }
  }'
FieldNotes
amount, currencyThe invoice price in fiat: USD or EUR. The buyer pays the live-converted crypto amount.
external_idYour own order id, unique per merchant. Echoed back in every response and webhook.
callback_urlWhere the webhook goes. Optional if a merchant-level callback URL is set in the cabinet.
success_url / fail_urlThe widget offers these redirects after the payment settles. Optional.
metadata.descriptionShown to the buyer on the checkout page. Optional.
metadata.callback_paramsYour key-value pairs, merged into the webhook payload. Optional.
fee_payerpayer (fee added on top for the buyer) or payee (fee absorbed by you).
is_testSandbox payment on testnets - see Test your integration.

The response contains the checkout URL:

{
  "id": "n9cdFaTccYbXecVekHKW8Q",
  "external_id": "order-1042",
  "status": "created",
  "url": "https://pay.cryptunnel.io/n9cdFaTccYbXecVekHKW8Q"
}

2. Redirect the buyer

Send the buyer to url - as a browser redirect, a button, or a link in a chat message. On the checkout page the buyer picks a currency and network from your attached wallets, sees the exact crypto amount, and sends the transfer.

3. Track the status

The payment moves through a simple lifecycle:

StatusMeaning
createdWaiting for the buyer to pick a currency
pendingCurrency picked, waiting for the on-chain transfer
confirmedTransfer detected and confirmed - the terminal success state
confirmed_manualConfirmed by a manual transaction rescan
failedThe payment failed
expiredThe buyer never completed it; expired payments free their external_id for reuse

Two ways to learn about the outcome:

  • Webhook (recommended): a signed POST to your callback_url on terminal statuses - see Webhook verification.
  • Polling: GET /v1/payments/{id} with the same auth headers.

Unpaid payments expire automatically, so an abandoned checkout never blocks the order id forever.


Did this page help you?