Backend API
HTTP endpoints used by the SDK and payment providers.
Host (production, planned): https://api.suioutkit.xyz
Host (current): https://api.staging.suioutkit.xyz
Base path: /v1/checkout (unless noted). Note: a few endpoints live under /v1/payments (SSE) or the root (/health).
These routes are called by the SDK during checkout. Merchants integrating the SDK do not need to implement these endpoints - use initCheckout, openModal, and related SDK methods. The API reference below is useful when building a custom UI or integrating directly with the hosted service.
Session
POST /v1/checkout/session
Create a checkout session.
{
"amount": 29.99,
"currency": "USD",
"merchantAddress": "0x...",
"coinType": "0x2::sui::SUI",
"settlementToken": "USDC",
"metadata": {}
}
settlementToken accepts a string ("DEEP") or array (["SUI","USDC"]). When omitted, the backend uses DEFAULT_COIN.
Returns session with token, nonce, estimatedRate, coinType, supportedCoins (includes category per entry), settlementToken (normalized to string[]), resolvedCurrency, currencySymbol, status.
Cross-region payments
When a customer’s currency differs from the merchant’s settlement currency, the backend supports cross-region payments. For example, a USD-settled merchant can accept NGN from a Nigerian customer - the customer is charged in NGN, and settlement uses the merchant’s USD amount. The modal shows a “Pay in {local currency}” label to indicate the local payment rail.
Charge
POST /v1/checkout/charge
Start a payment flow. Body:
{
"token": "session-token-from-create",
"method": "bank_transfer",
"phoneNumber": "+234..."
}
Methods: bank_transfer | opay | ussd | stripe
Returns a provider-specific payload: virtual account details and validated FX rate for bank_transfer, an opayAuthorizationUrl redirect for opay, USSD bank code and instructions for ussd, or a Stripe clientSecret and public key for stripe.
Common response codes for this endpoint:
200- success with provider payload400- bad request (missing token or method)404- session token/nonce not found or expired409- treasury insufficient for the validated settlement amount
Status
GET /v1/checkout/status/:nonce
{
"status": "PENDING" | "PROCESSING" | "SETTLED" | "EXPIRED",
"txDigest": "...",
"walrusBlobId": "...",
"error": "..."
}
GET /v1/checkout/validate/:nonce
Pre-flight FX and settlement amount preview.
Crypto
POST /v1/checkout/crypto/intent
Body: { "token", "method?", "coinType?" } - prepares wallet/outPay intent for the specified (or default) coin. Also runs encode-only Walrus invoice preparation (~1s) and returns walrusBlobId in the response so the client can display the receipt ID up front.
POST /v1/checkout/crypto/confirm
Body: { "nonce", "txDigest", "method?" } - verifies the on-chain payment (via waitForTransaction, which polls until the fullnode has indexed the digest). After verification it enqueues the Walrus receipt upload to the background queue rather than uploading inline.
OPay Callback
GET /v1/checkout/opay/callback
Browser redirect target after OPay authorization completes. Flutterwave redirects the user back to this route with query parameters (status, tx_ref, transaction_id, etc.). The route renders an HTML page that posts a suioutkit_opay_complete message back to the opening window (via window.opener.postMessage) and auto-closes after 1.5s. It does not verify or settle the transaction itself - verification and settlement are handled by the background transaction polling started at charge time and the Flutterwave webhook.
The callback URL is constructed from the PUBLIC_URL environment variable (e.g. https://api.suioutkit.xyz/v1/checkout/opay/callback). For local development, set PUBLIC_URL=http://localhost:5000.
Webhooks (server only)
| Endpoint | Provider |
|---|---|
POST /v1/checkout/webhook |
Flutterwave |
POST /v1/checkout/stripe-webhook |
Stripe |
Webhooks are server-to-server callbacks and must be handled on a secure backend (not in-browser). Important notes:
- Flutterwave:
POST /v1/checkout/webhook- validated by theverif-hashheader (seebackend/.envFLW_HASH). - Stripe:
POST /v1/checkout/stripe-webhook- requires the raw request body and thestripe-signatureheader forstripe.webhooks.constructEvent()verification.
Keep signing secrets and provider keys on your server; do not expose them to client-side code.
SSE
GET /v1/payments/stream/:nonce
Server-Sent Events for live status updates. Note this route lives under /v1/payments rather than /v1/checkout.
Health
GET /health
{ "status": "healthy", "service": "..." }
See the SDK docs (/docs/guides/sdk) for examples showing how the client uses these routes (including SSE and polling). If you plan to run a local backend, see backend/.env.example for operator environment variables and required credentials.