How to create an Invoice Payment Widget via API
Overview
Invoice Payment Widget lets the merchant create a hosted Calypso checkout page through API.
After the widget is created, use the returned idempotencyKey as widgetKey in the payment URL:
https://pay.calypso.money/pay?widgetKey={idempotencyKey}
Widget type
| Widget type | Description |
|---|---|
| SINGLE_FIAT | Creates a payment widget for one fiat-denominated payment. The customer pays in one of the allowed cryptocurrencies. |
Integration flow
Common setup
-
Create a
SINGLE_FIATpayment widget through Payment Widget API. -
Save the returned
idempotencyKey,widgetType,widgetState,externalId, andinvoices. - Build the widget URL and redirect the customer to it or embed it into the merchant website with an iframe.
-
When Calypso sends
INVOICE_CREATE_INVOICE, store the invoice identifiers from the webhook or from the widget response. -
Use
paymentStatusfromreturnUrlonly for customer-facing page state.
Successful payment flow
Use this flow when the customer pays through the widget and Calypso can complete the payment without manual intervention.
-
Optional: Calypso may send
INVOICE_MEM_POOL_FOUNDwhen the customer transaction is detected in the blockchain mempool. This webhook is not guaranteed: on fast blockchains, the mempool status may be skipped. -
When Calypso sends
INVOICE_FUNDS_RECEIVED_FOR_INVOICE, record that funds were received on the invoice address. This webhook confirms the deposit, but it is not always the final payment result. -
If Calypso sends
INVOICE_PAID, mark the customer-facing payment as successful. -
INVOICE_TRANSLATION_TO_ACCOUNT_COMPLETEDis sent when funds are credited to the merchant account balance. Use it for reconciliation, not as the customer-facing payment confirmation.
If needed, verify the current invoice status by using [POST] invoice.
No payment or failed processing flow
Use this flow when the customer does not complete the widget payment in time or invoice processing cannot be completed.
-
INVOICE_EXPIREDis sent if the invoice created from the widget expires before a successful payment is completed. Stop accepting payment for this widget invoice and create a new widget if the customer still needs to pay. -
INVOICE_FAILEDis sent if invoice processing fails because Calypso cannot complete the payment flow for this widget invoice. Stop the current payment flow and verify the invoice state through [POST] invoice.
Compliance corner case
Use this flow only when the transaction is routed to compliance review.
-
Optional:
INVOICE_PENDING_COMPLIANCEis sent only if the transaction is routed to compliance review under the relevant compliance conditions. If the transaction automatically passes compliance, this webhook is not sent; continue waiting for the next invoice status webhook. -
If Calypso sends
INVOICE_COMPLIANCE_DECLINED, treat it as a terminal negative compliance result and do not mark the widget payment as paid.
Intervention corner case
Use this flow when funds were received, but the widget invoice requires merchant action, for example because of underpay or overpay.
-
After
INVOICE_FUNDS_RECEIVED_FOR_INVOICE, Calypso may sendINVOICE_PENDING_INTERVENTION. -
Check the invoice details by using [POST] invoice and review
state,interventionType,amount,totalDebitAmount, andisInterventionResolved. -
Do not mark the widget payment as paid until the intervention is resolved and the invoice reaches
PAID.
Sequence
Names of requests on the scheme indicate Payment Widget API methods and invoice webhook events.
Widget lifetime
The widget can have an expiration date set during creation.
For each created widget, the lifetime of invoices created from the widget does not exceed one day. If the widget expiration time is less than one day, invoices created from this widget expire after the same shorter period.
Common request fields
The Payment Widget API creation method uses the standard request wrapper.
| Field | Type | Required | Description |
|---|---|---|---|
| account | string | Yes | Merchant account ID. The request will be sent for this account. |
| timestamp | number | Yes | Current Unix UTC timestamp in milliseconds. The value must not be less than 3 minutes in the past or greater than 3 minutes in the future. |
| payload | object | Yes | Object with payment widget creation data. |
Create SINGLE_FIAT widget
Use [POST] payment-widget/single-fiat/create.
| Payload field | Type | Required | Description |
|---|---|---|---|
| fiatCurrency | string | Yes | Fiat currency of the widget amount. |
| fiatAmount | number | Yes | Fiat amount used for invoices created from this widget. |
| description | string | Yes | Payment widget description. |
| cryptoCurrencies | array | Yes | Cryptocurrencies available for payment. |
| expiration | string | Yes | Widget expiration date and time. |
| logoUrl | string | No | Merchant logo URL shown on the widget page. PNG format is recommended. |
| returnUrl | string | No | URL where the customer can return from the widget page. |
| supportUrl | string | No | Merchant support URL shown on the widget page. |
| idempotencyKey | string | No | External ID in UUID format. |
| externalId | string | No | Merchant-side widget ID. All invoices created from this widget receive the same externalId. |
Request example
{
"account": "0x0000000000000000000000000000000000000000",
"timestamp": 1761650432438,
"payload": {
"fiatCurrency": "USD",
"fiatAmount": 100,
"description": "Order #1001",
"cryptoCurrencies": [
"USDT_TRX",
"USDT_BSC"
],
"expiration": "2026-06-30T12:00:00Z",
"returnUrl": "https://merchant.com/checkout",
"supportUrl": "https://merchant.com/support",
"idempotencyKey": "123e4567-e89b-12d3-a456-426614174000",
"externalId": "order-1001"
}
}
Response fields
| Field | Type | Description |
|---|---|---|
| idempotencyKey | string | Widget key used to open the widget page. |
| cryptoCurrencies | array | Cryptocurrencies available for payment. |
| widgetType | string | Widget type: SINGLE_FIAT. |
| widgetState | string | Current widget status. |
| fiatCurrency | string | Fiat currency for SINGLE_FIAT widget. |
| fiatAmount | number | Fiat amount for SINGLE_FIAT widget. |
| description | string | Payment widget description. |
| logoUrl | string | Merchant logo URL. |
| returnUrl | string | Merchant return URL. |
| supportUrl | string | Merchant support URL. |
| expiration | string | Widget expiration date and time. |
| externalId | string | Merchant-side widget ID. |
| invoices | array | Invoices created from this widget. |
| createdDate | string | Widget creation date and time. |
Invoice object inside invoices
| Field | Type | Description |
|---|---|---|
| invoiceId | string | Calypso invoice ID created from the widget. |
| cryptoCurrency | string | Cryptocurrency selected for the invoice. |
| invoiceIdempotencyKey | string | Invoice idempotencyKey. Use it to check invoice details through Invoice API. |
| invoiceExternalId | string | Merchant-side invoice ID. |
Widget statuses
| Status | Description |
|---|---|
| IN_PROGRESS | Widget is active and can be used for payment. |
| COMPLETED | Widget payment flow was completed. |
| ARCHIVED | Widget was archived and is no longer active. |
Invoice payment status is tracked separately through invoice state and Invoice webhooks.
Widget URL
Use the returned widget idempotencyKey as widgetKey.
https://pay.calypso.money/pay?widgetKey={idempotencyKey}
Optional URL parameters:
| Parameter | Required | Description |
|---|---|---|
| widgetKey | Yes | Widget idempotencyKey returned by Payment Widget API. |
| theme | No | Widget color theme: light or dark. The default value is light. |
| logo | No | Shows or hides the logo on the widget page: true or false. |
URL example
https://pay.calypso.money/pay?widgetKey=123e4567-e89b-12d3-a456-426614174000&logo=true&theme=light
iframe integration
You can embed the payment widget into the merchant website with an iframe.
Add clipboard-read and clipboard-write to the allow attribute so the widget copy buttons can copy the payment address and amount.
<iframe
src="https://pay.calypso.money/pay?widgetKey=123e4567-e89b-12d3-a456-426614174000&logo=true&theme=light"
title="Calypso payment widget"
width="100%"
height="720"
style="border: 0;"
allow="clipboard-read; clipboard-write"
></iframe>
returnUrl payment status
When Calypso redirects the customer to returnUrl, it can append the paymentStatus query parameter.
paymentStatus value | Meaning | Recommended merchant page |
|---|---|---|
| SUCCESS | Invoice payment reached a successful final status. | Success page or order confirmation. |
| FAILED | Invoice payment was declined, archived, or failed. | Failed or expired payment page. |
| absent | No status-specific redirect was applied. | Standard return page. |
paymentStatus is a redirect parameter, not the invoice state. Use Invoice webhooks or [POST] invoice as the source of truth for backend order processing.
Recommended handling
| Case | Recommendation |
|---|---|
| Customer opens widget | Show the widget through redirect or iframe. |
Customer returns to returnUrl | Read paymentStatus and show a success or failed page. |
| Payment status changes | Update order status through Invoice webhooks. |
| Intervention is required | Use the intervention flow for INVOICE_PENDING_INTERVENTION. |
| Widget expires or is archived | Stop showing the widget and create a new one if payment is still required. |
Related docs
| Document | Link |
|---|---|
| Invoice Payment Widget webhooks | Invoice Payment Widget webhooks |
| Manage invoice interventions | How to manage interventions via API |
| Payment Widget API reference | Payment Widget API |