Skip to main content

Usage

Initialization

The SDK exposes the same OozooPayClient class regardless of how it was loaded.

npm (ES Module)

import { OozooPayClient } from '@team-oozoo/oozoo-pay';

const client = new OozooPayClient('pk_xxxxxxxxxxxxxxxx');

CDN (Standalone global)

<script src="https://cdn.oozoopay.com/latest/standalone.global.js"></script>
<script>
// Either await OozooPay.load(...) or new OozooPay.OozooPayClient(...)
const client = await OozooPay.load('pk_xxxxxxxxxxxxxxxx');
</script>

Both patterns produce the same client instance.

ArgumentTypeRequiredDescription
clientKeystringClient Key (pk_xxx)

Request a Payment

Call pay() to open the checkout overlay. When the user clicks "Pay", the SDK invokes your onCreateInvoice callback — your merchant server must create an invoice via the HMAC API and return the resulting invoiceId.

try {
await client.pay({
price: 100,
unit: 'usd',
successUrl: 'https://your-shop.com/payment/success',
failUrl: 'https://your-shop.com/payment/fail',
onCreateInvoice: async ({ price, unit, chainId, tokenAddress, sender }) => {
const res = await fetch('/api/create-invoice', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ price, unit, chainId, tokenAddress, sender }),
});
const { invoiceId } = await res.json();
return invoiceId;
},
});
} catch (err) {
// err.code === 'UNSUPPORTED_TOKEN' etc.
console.error(err);
}

pay() options

OptionTypeRequiredDescription
pricenumberPayment price
unit'usd'Currency unit (default: 'usd')
successUrlstringRedirect URL on payment success
failUrlstringRedirect URL on cancel/failure (omit to just close the modal)
onCreateInvoicefunctionInvoice creation callback (see below)
chainIdstringPrefill — preselect a chain in the checkout (must be paired with tokenAddress)
tokenAddressstringPrefill — preselect a token in the checkout (must be paired with chainId)

onCreateInvoice callback

Invoked when the user clicks "Pay" after selecting a token + network. Your callback receives the user's selection and must return an invoiceId (UUID) created on your server.

ParameterTypeDescription
pricenumberPayment price
unit'usd'Currency unit
chainIdstringSelected blockchain network chain ID
tokenAddressstringSelected token contract address
senderstringPayer's wallet address

See Server Integration for the server-side implementation.

Preselect a Token & Chain (Prefill)

Pass chainId + tokenAddress together to open the checkout with that token preselected. The user can still switch to another token before confirming.

await client.pay({
price: 100,
unit: 'usd',
successUrl: 'https://your-shop.com/payment/success',
failUrl: 'https://your-shop.com/payment/fail',

// Preselect: Ethereum Sepolia USDC
chainId: '11155111',
tokenAddress: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',

onCreateInvoice: async ({ chainId, tokenAddress, sender }) => {
// ...
},
});

Behavior

CaseResult
Neither chainId nor tokenAddress providedDefault — user picks any available token in the checkout
Only one of them providedPrefill is ignored; user picks as usual
Both provided and match a token the merchant activatedToken is preselected; user can still switch
Both provided but no matchpay() Promise rejects with code: 'UNSUPPORTED_TOKEN' (see below)

Match rules:

  • chainId is compared as a string, exactly.
  • tokenAddress is compared case-insensitively.
  • For native coins (ETH, BNB, KAIA, etc.), use the EVM native sentinel 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE or the SVM native sentinel So11111111111111111111111111111111111111111.
Pick valid values from GET /api/tokens

Your merchant server can call GET /api/tokens (HMAC-authenticated) to list the chain/token combinations enabled for this merchant. Pass that to your client to populate a dropdown — and you'll never hit UNSUPPORTED_TOKEN.

Error Handling

pay() and transfer() return a Promise<void>:

  • resolves on success (after successUrl redirect fires) and on user cancel (backdrop click / ESC / explicit close)
  • rejects with an ApiError when the checkout signals an error
try {
await client.pay({ ... });
} catch (err) {
// err.name === 'ApiError'
// err.code === 'UNSUPPORTED_TOKEN' | 'PAYMENT_FAILED' | ...
// err.message — human-readable
}

Error codes

codeCause
UNSUPPORTED_TOKENPrefill (chainId + tokenAddress) didn't match any token enabled by this merchant.
PAYMENT_FAILEDBlockchain transaction failed (signed but reverted or RPC error).
TRANSFER_FAILEDSame as above, in a transfer() flow.

If a failUrl is configured, the browser is also redirected there with ?code=...&message=... query params. The Promise still rejects, so a try/catch is the safest place to react.

Payment Flow

  1. Initialize the SDK with your Client Key.
  2. Call pay() to open the checkout overlay (optionally prefill a token).
  3. User confirms token & network in the UI (preselected if you prefilled).
  4. onCreateInvoice is invoked — your server creates an invoice via HMAC API.
  5. Blockchain transaction — user signs and broadcasts from their wallet.
  6. Payment confirmed on-chain — user is redirected to successUrl.
  7. Webhook delivered — your server receives invoice.confirmed and finalizes the order.
Finalize via webhook, not redirect

Do not finalize orders based solely on the successUrl redirect — users can navigate away or close the tab before redirect fires. Always wait for the invoice.confirmed webhook on your server.

Request a Withdrawal

transfer() opens the same overlay but in payout direction. It creates a withdrawal request — funds are not sent on-chain immediately. The request lands in the Merchant Admin Dashboard with status REQUESTED, and an admin must approve it (with 2FA) for the on-chain transfer to actually fire.

Use this for user-initiated withdrawals, off-flow refunds, or reward payouts where you want a human-in-the-loop approval step.

await client.transfer({
price: 100,
unit: 'usd',
successUrl: 'https://your-shop.com/withdrawal/success',
failUrl: 'https://your-shop.com/withdrawal/fail',

// Prefill is supported here too
chainId: '11155111',
tokenAddress: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238',

onCreateInvoice: async ({ price, unit, chainId, tokenAddress, receiver }) => {
const res = await fetch('/api/create-withdrawal', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ price, unit, chainId, tokenAddress, receiver }),
});
const { withdrawalId } = await res.json();
return withdrawalId;
},
});

transfer() options

Same shape as pay()chainId/tokenAddress prefill works the same way. Only the callback parameter differs (receiver instead of sender).

onCreateInvoice callback (transfer)

ParameterTypeDescription
pricenumberWithdrawal amount
unit'usd'Currency unit
chainIdstringSelected blockchain network chain ID
tokenAddressstringSelected token contract address
receiverstringRecipient's wallet address (entered by the user in the UI)

The server-side endpoint differs — it must call POST /api/invoices/withdrawals and return withdrawalId instead of invoiceId. See Server Integration → Create Withdrawal for details.