Payment API
Create and manage cryptocurrency payment sessions with the 2328.io Payment API.
The Payment API lets you create payment sessions, redirect customers to a hosted checkout, and track payment status.
Create payment
Creates a payment session and returns a URL for the customer to pay.
Request parameters
| Field | Type | Required | Description | Values |
|---|---|---|---|---|
amount | decimal | yes | Payment amount in the currency, e.g. 100.00 | |
currency | string | yes | Fiat currency (USD, EUR, RUB, …) or an enabled cryptocurrency. Use /v1/directions as the live source of truth. TON is accepted as a legacy input alias for canonical GRAM. | |
order_id | string | yes | Your order ID, e.g. ORDER-12345 (up to 128 chars) | |
to_currency | string | no | Preselected enabled cryptocurrency; TON is normalized to GRAM | |
network | string | no* | Canonical network code (required if to_currency is set or currency is a cryptocurrency) | |
url_return | string | no | Redirect URL after payment, e.g. https://your-site.com/return | |
url_success | string | no | Alternative to url_return | |
url_callback | string | yes | Public HTTP(S) URL for webhook notifications, e.g. https://your-site.com/webhook. Private, loopback, and otherwise unsafe targets are rejected. | |
invite_code | string | no | Referrer code | |
fee_split | decimal | no | Share of the merchant fee passed to the payer, 0–100 (%). 0 = merchant pays fully, 100 = payer pays fully. Overrides the project-level setting. Example: 30 (payer covers 30% of the fee). | |
price_markup | decimal | no | Markup or discount on the invoice amount, −99 to 100 (%). Overrides the project-level setting. Example: 5 (+5%) or -10 (10% discount). | |
description | string | no | Optional invoice description (max 200 chars). Shown to the payer on the payment page. Example: Premium plan — Order #12345. | |
ttl_seconds | int | no | Invoice lifetime in seconds, from 300 (5 minutes) to 86400 (24 hours). After this period the invoice expires and can no longer be paid. Default: 3600 (1 hour). Example: 3600. |
Response
{
"state": 0,
"result": {
"uuid": "abc123-def456-...",
"order_id": "ORDER-12345",
"amount": "100.00",
"currency": "USD",
"amount_usd": "100.00",
"exchange_rate": null,
"url": "https://2328.io/pay/abc123-def456-...",
"tg_deeplink": "https://t.me/my2328bot?start=pay_abc123-def456-...",
"expires_at": "2026-01-11T21:00:00Z",
"created_at": "2026-01-11T20:00:00Z",
"payer_currency": "USDT",
"payer_amount": "100.50",
"network": "TRX-TRC20",
"address": "TXYZabc123...",
"payment_status": "check",
"txid": null,
"payment_amount": null,
"qr": "data:image/png;base64,iVBORw0..."
}
}- Redirect the customer to
result.urlto complete payment. tg_deeplink— Telegram bot deeplink for payment via Telegram MiniApp.qr— Base64-encoded QR code (data URI) of the deposit address. Present when an address is already assigned (whennetworkis set together withto_currency, or whencurrencyis a cryptocurrency); otherwisenull.txid,payment_amount—nulluntil the customer pays. Filled in once the transaction is detected on-chain. Listen for thepayment_status: paidwebhook to know when.exchange_rate—nullif conversion isn't applicable yet (e.g. fiat → crypto rate hasn't been locked). Filled in once a payer currency is chosen.
curl -X POST https://api.2328.io/api/v1/payment \
-H "Content-Type: application/json" \
-H "User-Agent: MyShop/1.0 (+https://myshop.example)" \
-H "project: YOUR_PROJECT_UUID" \
-H "sign: YOUR_HMAC_SIGNATURE"Hosted checkout, H2H, and exact crypto amounts
The same endpoint supports three distinct invoice shapes. Pick one deliberately; do not mix their amount semantics.
Hosted checkout with payer choice
Send amount, currency, order_id, and url_callback, but omit to_currency and network. The response contains result.url; address, qr, and sometimes payer fields remain null until the payer selects a direction on the hosted page.
{
"amount": "125.00",
"currency": "EUR",
"order_id": "ORDER-2026-1042",
"url_callback": "https://merchant.example/webhooks/2328",
"url_return": "https://merchant.example/orders/ORDER-2026-1042"
}Direct-address H2H invoice
Send both to_currency and network. 2328.io creates the blockchain invoice during the API call, so a successful response can be rendered inside your checkout without redirecting the customer.
{
"amount": "100.00",
"currency": "USD",
"to_currency": "USDT",
"network": "TRX-TRC20",
"order_id": "ORDER-2026-1043",
"url_callback": "https://merchant.example/webhooks/2328"
}Render these values exactly as returned:
payer_amountandpayer_currency— the payment instruction;networkandaddress— the only destination for this invoice;qr— a data URI for the same address;expires_at— the invoice deadline;url— a useful hosted fallback when the custom checkout cannot complete.
Never generate or substitute an address, reuse an address from another invoice, or calculate payer_amount from a public spot price. The API response is authoritative.
Invoice for an exact crypto amount
Put the cryptocurrency in currency when the invoice itself is denominated in crypto:
{
"amount": "25.000000",
"currency": "USDT",
"network": "TRX-TRC20",
"order_id": "ORDER-2026-1044",
"url_callback": "https://merchant.example/webhooks/2328"
}The requested crypto value is preserved in payer_currency / payer_amount. The service can also maintain a USD valuation internally for accounting and rate fields; do not replace the exact crypto instruction with that valuation. Preserve returned decimal strings, including trailing precision.
For a cryptocurrency with only one supported network, the network may be selected automatically. Supplying network explicitly is still recommended for a deterministic integration. For multi-network assets such as stablecoins, always send it.
Idempotency and retries
order_id is scoped to the authenticated merchant project and acts as the creation idempotency key. If a payment already exists, the API returns that session with state: 0.
A retry with the same order_id does not mean “update this invoice.” Changed amount, currency, callback, markup, TTL, or direction fields may be ignored because the existing session is returned. Persist the first request and reject conflicting retries in your own application.
Recommended creation algorithm:
- Insert your local payment attempt and unique
order_idin one database transaction. - Send the signed API request.
- Persist the returned
uuidand full response. - If the HTTP result is lost, retry the identical request or query
/v1/payment/infobyorder_id. - Never create a second local order merely because the upstream request timed out.
Payment edge cases
| Situation | Correct handling |
|---|---|
address / qr is null | The payer direction has not been initialized. Redirect to url, or create a new correctly specified H2H invoice with a new order_id. |
HTTP 400 validation error | Read the field-level errors; do not retry unchanged input. |
HTTP 429 | Retry with jittered exponential backoff and keep the same order_id. |
HTTP 503 / direction_disabled | Refresh /v1/directions; hide the direction temporarily or retry later. |
| Client request timeout | Treat the result as unknown. Query by order_id before creating anything else. |
underpaid_check | Store the partial event and await a top-up or later status. Do not credit twice when more txids arrive. |
underpaid | Final underpayment state. Apply your configured fulfillment/manual-review policy to the actual credited amount. |
overpaid | Successful payment with excess funds. Fulfill idempotently and retain the actual amounts for reconciliation/refund policy. |
aml_lock | Do not fulfill or release funds automatically; route to compliance/support workflow. |
cancel | Invoice expired or was cancelled. Do not infer that a late on-chain transfer is impossible; reconcile any later event with support. |
The browser return URL is navigation only. A customer can open it without paying, close it after paying, or replay it later. Only a verified API/webhook state may settle the merchant order.
Payment info
Get the current payment status by uuid or order_id.
Request parameters
| Field | Type | Required | Description | Values |
|---|---|---|---|---|
uuid | string | yes* | Payment UUID (from result.uuid on creation) | |
order_id | string | yes* | Your order ID |
At least one of uuid or order_id is required.
curl -X POST https://api.2328.io/api/v1/payment/info \
-H "Content-Type: application/json" \
-H "User-Agent: MyShop/1.0 (+https://myshop.example)" \
-H "project: YOUR_PROJECT_UUID" \
-H "sign: YOUR_HMAC_SIGNATURE"Payment list
Get a list of all payments with filtering and pagination.
Request parameters
| Field | Type | Required | Description | Values |
|---|---|---|---|---|
status | string | no | Filter by list-supported payment status (see References). aml_lock can appear in payment info/webhooks but is not accepted by this list filter. | |
date_from | date | no | Start date (YYYY-MM-DD), e.g. 2026-01-01 | |
date_to | date | no | End date (YYYY-MM-DD), e.g. 2026-01-31 | |
page | int | no | Page number, default 1 | |
per_page | int | no | Items per page, default 15, max 5000 |
curl -X POST https://api.2328.io/api/v1/payment/list \
-H "Content-Type: application/json" \
-H "User-Agent: MyShop/1.0 (+https://myshop.example)" \
-H "project: YOUR_PROJECT_UUID" \
-H "sign: YOUR_HMAC_SIGNATURE"