Sign in
Payments and Payouts/Payment API

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

FieldTypeRequiredDescriptionValues
amountdecimalyesPayment amount in the currency, e.g. 100.00
currencystringyesFiat 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_idstringyesYour order ID, e.g. ORDER-12345 (up to 128 chars)
to_currencystringnoPreselected enabled cryptocurrency; TON is normalized to GRAM
networkstringno*Canonical network code (required if to_currency is set or currency is a cryptocurrency)
url_returnstringnoRedirect URL after payment, e.g. https://your-site.com/return
url_successstringnoAlternative to url_return
url_callbackstringyesPublic HTTP(S) URL for webhook notifications, e.g. https://your-site.com/webhook. Private, loopback, and otherwise unsafe targets are rejected.
invite_codestringnoReferrer code
fee_splitdecimalnoShare 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_markupdecimalnoMarkup or discount on the invoice amount, −99 to 100 (%). Overrides the project-level setting. Example: 5 (+5%) or -10 (10% discount).
descriptionstringnoOptional invoice description (max 200 chars). Shown to the payer on the payment page. Example: Premium plan — Order #12345.
ttl_secondsintnoInvoice 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

JSON
{
  "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.url to 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 (when network is set together with to_currency, or when currency is a cryptocurrency); otherwise null.
  • txid, payment_amountnull until the customer pays. Filled in once the transaction is detected on-chain. Listen for the payment_status: paid webhook to know when.
  • exchange_ratenull if conversion isn't applicable yet (e.g. fiat → crypto rate hasn't been locked). Filled in once a payer currency is chosen.
Credentials
RequestPOST/v1/payment
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"
Response
Click Try it to see the response here.

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.

JSON
{
  "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.

JSON
{
  "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_amount and payer_currency — the payment instruction;
  • network and address — 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:

JSON
{
  "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:

  1. Insert your local payment attempt and unique order_id in one database transaction.
  2. Send the signed API request.
  3. Persist the returned uuid and full response.
  4. If the HTTP result is lost, retry the identical request or query /v1/payment/info by order_id.
  5. Never create a second local order merely because the upstream request timed out.

Payment edge cases

SituationCorrect handling
address / qr is nullThe 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 errorRead the field-level errors; do not retry unchanged input.
HTTP 429Retry with jittered exponential backoff and keep the same order_id.
HTTP 503 / direction_disabledRefresh /v1/directions; hide the direction temporarily or retry later.
Client request timeoutTreat the result as unknown. Query by order_id before creating anything else.
underpaid_checkStore the partial event and await a top-up or later status. Do not credit twice when more txids arrive.
underpaidFinal underpayment state. Apply your configured fulfillment/manual-review policy to the actual credited amount.
overpaidSuccessful payment with excess funds. Fulfill idempotently and retain the actual amounts for reconciliation/refund policy.
aml_lockDo not fulfill or release funds automatically; route to compliance/support workflow.
cancelInvoice 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

FieldTypeRequiredDescriptionValues
uuidstringyes*Payment UUID (from result.uuid on creation)
order_idstringyes*Your order ID

At least one of uuid or order_id is required.

RequestPOST/v1/payment/info
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"
Response
Click Try it to see the response here.

Payment list

Get a list of all payments with filtering and pagination.

Request parameters

FieldTypeRequiredDescriptionValues
statusstringnoFilter by list-supported payment status (see References). aml_lock can appear in payment info/webhooks but is not accepted by this list filter.
date_fromdatenoStart date (YYYY-MM-DD), e.g. 2026-01-01
date_todatenoEnd date (YYYY-MM-DD), e.g. 2026-01-31
pageintnoPage number, default 1
per_pageintnoItems per page, default 15, max 5000
RequestPOST/v1/payment/list
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"
Response
Click Try it to see the response here.