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" }
}
}'| Field | Notes |
|---|---|
amount, currency | The invoice price in fiat: USD or EUR. The buyer pays the live-converted crypto amount. |
external_id | Your own order id, unique per merchant. Echoed back in every response and webhook. |
callback_url | Where the webhook goes. Optional if a merchant-level callback URL is set in the cabinet. |
success_url / fail_url | The widget offers these redirects after the payment settles. Optional. |
metadata.description | Shown to the buyer on the checkout page. Optional. |
metadata.callback_params | Your key-value pairs, merged into the webhook payload. Optional. |
fee_payer | payer (fee added on top for the buyer) or payee (fee absorbed by you). |
is_test | Sandbox 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:
| Status | Meaning |
|---|---|
created | Waiting for the buyer to pick a currency |
pending | Currency picked, waiting for the on-chain transfer |
confirmed | Transfer detected and confirmed - the terminal success state |
confirmed_manual | Confirmed by a manual transaction rescan |
failed | The payment failed |
expired | The 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_urlon 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.
Updated 13 days ago
Did this page help you?
