suioutkit
v1.2.1
Published
Premium Universal Payment Gateway SDK for instant settlement on Sui
Maintainers
Readme
Browser SDK for SuiOutKit checkout: create sessions, open a ready-made payment modal, or build a custom UI with helpers.
Defaults to the hosted SuiOutKit API at https://api.suioutkit.xyz (mode: "live"). Switch with mode: "test" or mode: "local" for development. All routes under /v1/. The SDK does not perform settlement, treasury checks, or provider calls itself.
| Resource | Link | |----------|------| | Monorepo overview | /README.md | | Documentation | /docs/README.md | | Live demo | /demo/demo.html | | Backend & operator setup | /docs/developer-guide.md |
Table of contents
- Install
- Quick start
- Configuration
- API reference
- Payment methods
- Custom UI (no modal)
- Load from your backend
- Backend endpoints used by the SDK
- Troubleshooting
- Publishing
- License
Install
npm install suioutkit
# or
yarn add suioutkitPeer environment: Node 18+ to build; in the browser, any modern ESM-capable environment.
Quick start
React or bundler (recommended)
import { SuiOutKit } from "suioutkit";
const sdk = new SuiOutKit({
merchantAddress: "0xYOUR_MERCHANT_SUI_ADDRESS",
// mode optional - defaults to "live" (https://api.suioutkit.xyz, mainnet)
});
export function PayButton() {
async function handlePay() {
const session = await sdk.initCheckout({
amount: 45000,
currency: "NGN",
metadata: { orderId: "ORDER-123" },
});
sdk.openModal(session, () => {
console.log("Modal closed");
});
}
return <button type="button" onClick={handlePay}>Pay now</button>;
}One-line button binding
sdk.wrapButton("#pay-btn", {
amount: 45000,
currency: "NGN",
metadata: { sku: "PRO-PLAN" },
});Updates the button label (e.g. Pay ₦45,000) and opens the modal on click.
Vanilla HTML (serve SDK bundle)
For simple demos you can serve the built SDK bundle from any static host. Build the SDK with npm run build in sdk/ and serve sdk/dist/index.js from your server. See the Developer Guide for recommended local and production setups: /docs/developer-guide.md.
Configuration
new SuiOutKit(config)
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| merchantAddress | string | Yes | Sui address that receives settlement |
| mode | "local" \| "test" \| "live" | No | Default: "live". "local" → http://localhost:5000 (testnet), "test" → https://api.staging.suioutkit.xyz (testnet), "live" → https://api.suioutkit.xyz (mainnet). |
| backendUrl | string | No | Override API origin (no trailing slash). Takes precedence over mode. |
The SDK automatically sets window.SuiOutKitNetwork to the correct Sui network based on mode - no manual <script> tag needed.
API reference
initCheckout(options)
Creates a checkout session on the backend.
const session = await sdk.initCheckout({
amount: 45000, // integer in major units (e.g. 45000 NGN)
currency: "NGN", // e.g. "NGN"
coinType?: "0x2::sui::SUI", // optional: override settlement coin
metadata?: { orderId: "ORDER-123" },
});Returns CheckoutSession (includes at least):
| Field | Description |
|-------|-------------|
| token | Opaque session token for charge/crypto calls |
| nonce | Public session id for status polling |
| amount, currency | Checkout totals |
| merchantAddress | Normalized Sui address |
| coinType | Settlement coin type (from backend config) |
| supportedCoins | Array of { symbol, type, decimals } for available settlement coins |
| estimatedRate | FX preview (NGN → token) when applicable |
| packageId, cryptoRegistryId, cryptoRegistryName | On-chain config for crypto paths |
Throws if the backend returns a non-OK response.
openModal(session, options?)
Opens the built-in checkout modal (bank transfer, OPay, Stripe, Sui wallet, outPay).
const modal = sdk.openModal(session, {
onClose: () => console.log("closed"),
onPaymentComplete: (result) => console.log(result.txDigest, result.walrusBlobId),
redirectUrl: "/thank-you",
autoCloseOnSuccess: true,
});Returns SuiOutKitModal (internal handle). The modal loads styles from {backendUrl}/style.css automatically.
Options (SuiOutKitModalOptions):
| Option | Type | Description |
|--------|------|-------------|
| onClose | () => void | Called when the user dismisses the overlay |
| onPaymentComplete | (result: PaymentResult) => void | Called after on-chain settlement with { nonce, txDigest, walrusBlobId } |
| redirectUrl | string | Redirect browser here after successful payment |
| autoCloseOnSuccess | boolean | Auto-close the modal after settlement instead of showing success panel |
Note: Theme, logo, and
allowedMethodscustomization are not exposed onopenModaltoday. Use custom UI or extend the modal in source if you need that.
wrapButton(selector, options)
Binds checkout to a DOM button.
| Argument | Type | Description |
|----------|------|-------------|
| selector | string | CSS selector (e.g. "#pay-btn") |
| options.amount | number | Checkout amount |
| options.currency | string | e.g. "NGN" |
| options.coinType | string | Optional settlement coin type override |
| options.metadata | object | Optional passthrough to initCheckout |
confirmCryptoPayment(nonce, txDigest, method?)
After the user pays via wallet/outPay in a custom flow, submit the transaction digest for backend verification and Walrus receipt handling.
const result = await sdk.confirmCryptoPayment(
session.nonce,
txDigest,
"sui_wallet" // or "outpay"
);
if (result.status === "success") {
console.log(result.txDigest, result.walrusBlobId);
}The built-in modal calls this internally for crypto paths.
Helper exports
For custom UIs without the modal:
import {
SuiOutKit,
request,
formatNgn,
toTokenUnits,
formatToken,
createPolling,
} from "suioutkit";| Export | Description |
|--------|-------------|
| request(url, options?) | fetch wrapper with timeout and JSON parsing |
| formatNgn(amount) | Format NGN with locale / ₦ fallback |
| toTokenUnits(baseUnits, decimals?) | Convert base units to float (default 9 decimals) |
| formatToken(amount, decimals?, digits?) | Display-friendly token amount string |
| createPolling(fn, intervalMs) | { start(), stop() } interval helper |
Example - poll settlement status:
import { createPolling, request } from "suioutkit";
const poll = createPolling(async () => {
const status = await request(`${backendUrl}/v1/checkout/status/${nonce}`);
if (status.status === "SETTLED") {
poll.stop();
console.log(status.txDigest, status.walrusBlobId);
}
}, 3000);
poll.start();Example - Server-Sent Events (React hook in repo, not published from package entry today):
The backend exposes GET /v1/payments/stream/:nonce. You can use EventSource directly or copy usePaymentStatus.ts into your app.
Payment methods
The modal orchestrates these charge methods against the backend:
| Method | Provider | Notes |
|--------|----------|--------|
| bank_transfer | Flutterwave | Virtual account details shown in modal |
| opay | Flutterwave | Requires phoneNumber at charge time |
| stripe | Stripe | Card element; NGN minimum enforced server-side |
| sui_wallet | Sui + Payment Kit | Wallet connect via dApp Kit |
| outpay | Payment Kit QR | outPay flow |
Fiat methods depend on backend env configuration (Flutterwave / Stripe keys). Crypto methods require registry IDs on the backend.
Custom UI (no modal)
initCheckout→ keepsession.tokenandsession.nonce.- Optional:
GET /v1/checkout/validate/:noncefor FX/settlement preview. POST /v1/checkout/chargewith{ token, method, phoneNumber? }.- Poll
GET /v1/checkout/status/:nonceor use SSE/v1/payments/stream/:nonce. - For crypto:
POST /v1/checkout/crypto/intent→ wallet PTB →confirmCryptoPayment.
Charge and crypto endpoints are documented in Backend API.
Load from your backend
In production, either bundle the SDK with your app (npm install suioutkit) or serve the built sdk/dist statically from your web host. See the Developer Guide for recommended deployment patterns and backend integration: /docs/developer-guide.md.
Backend endpoints used by the SDK
All paths are relative to backendUrl.
| Method | Path | Used by |
|--------|------|---------|
| POST | /v1/checkout/session | initCheckout |
| POST | /v1/checkout/charge | Modal (fiat) |
| GET | /v1/checkout/status/:nonce | Modal polling |
| GET | /v1/checkout/validate/:nonce | Modal pre-flight |
| POST | /v1/checkout/crypto/intent | Modal (crypto) |
| POST | /v1/checkout/crypto/confirm | Modal / confirmCryptoPayment |
| GET | /v1/payments/stream/:nonce | Optional SSE (custom UI) |
Webhooks (/v1/checkout/webhook, /v1/checkout/stripe-webhook) are server-to-provider only.
Troubleshooting
| Symptom | Likely cause |
|---------|----------------|
| Failed to initialize checkout session | Backend down, CORS, or missing merchantAddress |
| 409 Treasury insufficient | Operator vault underfunded for FX settlement amount |
| Modal stuck on “waiting for settlement” | Webhook not reaching backend (Flutterwave hash, Stripe CLI, or ngrok) |
| Stripe card errors on small NGN amounts | Backend enforces ~$0.50 USD equivalent minimum |
| Crypto connect fails | Wrong mode or registry env on backend |
| Styles missing | Backend not serving /style.css or wrong backendUrl |
See also Developer Guide - Troubleshooting.
Security
- Only
merchantAddress,mode, andbackendUrlbelong in browser code. - Never embed operator keys, Flutterwave secrets, or Stripe secret keys in the client.
- Use HTTPS for the API endpoint in production (default when
mode: "live").
Report vulnerabilities through your project’s private security channel (do not file public issues with key material).
License
GPL-3.0 - Copyright (c) 2026 The3rdWebLabs / @CYBWithFlourish
