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
clientKeystring✓Client 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
pricenumber✓Payment price
unit'usd'Currency unit (default: 'usd')
successUrlstring✓Redirect URL on payment success
failUrlstringRedirect URL on cancel/failure (omit to just close the modal)
onCreateInvoicefunction✓Invoice 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.