Accept non-custodial stablecoin payments on any chain. Integrate with our REST API in minutes.
Base URL
https://api.payzap.ccAuth
Bearer <JWT>Format
JSONAll authenticated endpoints require a JWT in the Authorization header. Get a token by signing a nonce with your wallet.
Auth header format
Authorization: Bearer eyJhbGciOiJIUzI1NiIs.../v1/auth/nonceGet a one-time nonce for wallet signature authentication. Nonces expire after 5 minutes.
Example response
{
"success": true,
"data": {
"nonce": "a1b2c3d4...",
"message": "Sign in to PayZap\n\nNonce: a1b2c3d4..."
}
}/v1/authAuthenticate with a wallet signature. Returns JWT access + refresh tokens.
Request body
| Field | Type | Req | Description |
|---|---|---|---|
| walletAddress | string | yes | Your wallet address |
| chain | enum | yes | "evm" | "ton" | "tron" | "solana" |
| signature | string | yes | Signed nonce message |
| nonce | string | yes | Nonce from GET /v1/auth/nonce |
Example response
{
"success": true,
"data": {
"accessToken": "eyJhbG...",
"refreshToken": "eyJhbG...",
"merchant": { "id": "...", "plan": "free" }
}
}/v1/auth/refreshRefresh an expired access token.
Request body
| Field | Type | Req | Description |
|---|---|---|---|
| refreshToken | string | yes | Refresh token from auth response |
Products represent items or services you sell. Each product has a price and generates a payment link.
/v1/productsAUTHCreate a new product.
Request body
| Field | Type | Req | Description |
|---|---|---|---|
| name | string | yes | Product name (1-255 chars) |
| description | string | no | Description (up to 2000 chars) |
| priceAmount | number | yes | Price in USD (max 1,000,000). Note: returned as string in API responses |
| priceCurrency | string | no | Currency code (default: "USD") |
| acceptedChains | string[] | no | "evm" | "ton" | "tron" | "solana" | "binance_pay" | "bybit_pay" |
| successUrl | string | no | URL to redirect customer after successful payment |
| metadata | object | no | Arbitrary JSON metadata |
/v1/productsAUTHList your products.
Query params
| Param | Type | Description |
|---|---|---|
| limit | number | 1-100 (default: 20) |
| offset | number | Offset (default: 0) |
/v1/products/:idAUTHGet a single product by ID.
/v1/products/:idAUTHUpdate a product. All fields optional.
Request body
| Field | Type | Req | Description |
|---|---|---|---|
| name | string | no | New name |
| priceAmount | number | no | New price |
| active | boolean | no | Enable/disable |
| acceptedChains | string[] | no | Accepted payment methods |
| successUrl | string|null | no | Success redirect URL (null to clear) |
/v1/products/:idAUTHDeactivate a product (soft delete).
Payment sessions are created when a customer initiates checkout. The session tracks the payment lifecycle from pending to confirmed.
Dynamic pricing: Pass the amount field when creating a session to override the product price. Useful for marketplaces, carts, and pay-what-you-want.
/v1/payments/sessionCreate a payment session. Returns a session with wallet address and payment link. Public endpoint — no auth required.
Request body
| Field | Type | Req | Description |
|---|---|---|---|
| productId | uuid | yes | Product ID |
| chain | enum | yes | "evm" | "ton" | "tron" | "solana" | "binance_pay" | "bybit_pay" |
| asset | enum | yes | "USDT" | "USDC" | "DAI" | "BUSD" |
| amount | number | no | Override product price (dynamic pricing) |
| customerRef | string | no | Your internal customer/order ID |
| successUrl | string | no | Override product success URL for this session |
| metadata | object | no | Arbitrary JSON metadata |
Example response
{
"success": true,
"data": {
"id": "sess_...",
"productId": "...",
"merchantWallet": "0x...",
"amount": "49.00",
"asset": "USDT",
"chain": "evm",
"status": "pending",
"expiresAt": "2026-03-17T12:30:00Z",
"paymentUrl": "https://payzap.cc/pay/prod_..."
}
}/v1/payments/session/:idGet session status. Use for polling from your frontend. Public — no auth.
/v1/paymentsAUTHList your completed payments.
Query params
| Param | Type | Description |
|---|---|---|
| limit | number | 1-100 (default: 20) |
| offset | number | Offset (default: 0) |
/v1/payments/:idAUTHGet a single payment by ID.
Share a hosted payment page with your customers. Supports query parameters for customization.
Every product gets a payment link at https://payzap.cc/pay/<product_id>. Customize the checkout experience with query parameters:
| Param | Type | Description |
|---|---|---|
| success_url | string | Redirect URL after successful payment (overrides product setting) |
| amount | number | Override product price (dynamic pricing) |
| ref | string | Customer reference / order ID |
| theme | string | UI theme: "dark" (default) or "light" |
Example
https://payzap.cc/pay/prod_abc123?success_url=https://myshop.com/thanks&ref=order_456
Success redirect: After payment, the customer is automatically redirected to the success URL with query parameters: session_id, tx_hash, amount, asset, status.
Add a payment button to any website with a single script tag. No framework required.
Include the widget script and add a button with a data-payzap attribute pointing to your product ID:
<script src="https://payzap.cc/v1.js"></script> <button data-payzap="prod_abc123"> Pay $49.00 </button>
JavaScript API
For programmatic control, use PayZap.open():
PayZap.open({
productId: 'prod_abc123',
amount: 99.00, // optional: override price
ref: 'order_789', // optional: your internal reference
theme: 'dark', // optional: 'dark' | 'light'
onSuccess: (data) => {
console.log('Paid!', data.sessionId, data.txHash);
},
onClose: () => {
console.log('Widget closed');
}
});Note: The API returns priceAmount as a string (e.g. "49.00") due to PostgreSQL numeric precision. Always use parseFloat() or Number() before arithmetic, and Intl.NumberFormat for display formatting.
Build your own payment UI using the PayZap API. Two endpoints are all you need.
Flow
POST /v1/payments/session — you get the merchantWallet, amount, and assetmerchantWallet using your UI (wagmi, ethers, viem, TonConnect, etc.)GET /v1/payments/session/:id until status is "completed" (or set up a webhook)Step 1 — Create session
const res = await fetch('https://api.payzap.cc/v1/payments/session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
productId: 'prod_abc123',
chain: 'evm',
asset: 'USDT',
network: 'base', // optional: specific EVM network
amount: 49.00, // optional: override product price
customerRef: 'order_789', // optional: your internal reference
}),
});
const { data: session } = await res.json();
// session.id — session ID (for polling)
// session.merchantWallet — send tokens here
// session.amount — amount to send (string, e.g. "49.00")
// session.asset — token symbol (e.g. "USDT")
// session.expiresAt — session expires (30 min)Step 2 — Send tokens (EVM example with viem)
import { parseUnits } from 'viem';
// ERC-20 transfer to merchant wallet
const tx = await walletClient.writeContract({
address: USDT_CONTRACT, // token contract on chosen network
abi: [{
name: 'transfer',
type: 'function',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
outputs: [{ type: 'bool' }],
}],
functionName: 'transfer',
args: [
session.merchantWallet,
parseUnits(session.amount, 6), // 6 decimals for USDT/USDC
],
});Step 3 — Poll for confirmation
async function waitForPayment(sessionId) {
while (true) {
const res = await fetch(
`https://api.payzap.cc/v1/payments/session/${sessionId}`
);
const { data } = await res.json();
if (data.status === 'completed') {
return { txHash: data.txHash, explorerUrl: data.txExplorerUrl };
}
if (data.status === 'expired' || data.status === 'failed') {
throw new Error(`Payment ${data.status}`);
}
await new Promise(r => setTimeout(r, 3000)); // poll every 3s
}
}Session statuses
| Status | Description |
|---|---|
| pending | Waiting for payment |
| confirming | Transaction detected, waiting for block confirmations |
| completed | Payment confirmed on-chain |
| expired | Session timed out (30 min) |
| failed | Transaction failed or reverted |
Important: amount is returned as a string (e.g. "49.00"). Use parseFloat() for arithmetic and Intl.NumberFormat for display.
Tip: You don't need auth to create sessions or poll status. These are public endpoints — safe to call from the browser. Use webhooks for server-side confirmation.
Get real-time notifications when payment events occur. Webhooks are signed with HMAC-SHA256 for verification.
Events
| Event | Description |
|---|---|
| payment.completed | Payment confirmed on-chain |
| payment.pending | Transfer detected, waiting for confirmations |
| payment.failed | Payment failed or was rejected |
| payment.expired | Session expired without payment |
Verifying webhook signatures
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(body))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your handler:
const sig = req.headers['x-payzap-signature'];
if (!verifyWebhook(req.body, sig, 'whsec_...')) {
return res.status(401).send('Invalid signature');
}/v1/webhooksAUTHCreate a webhook endpoint. The secret is returned only once — store it securely.
Request body
| Field | Type | Req | Description |
|---|---|---|---|
| url | string | yes | HTTPS endpoint URL |
| events | string[] | no | Event filter (default: all events) |
Example response
{
"success": true,
"data": {
"id": "whk_...",
"url": "https://example.com/webhook",
"events": ["payment.completed", "payment.failed"],
"secret": "whsec_..."
}
}/v1/webhooksAUTHList your webhook endpoints.
/v1/webhooks/:idAUTHUpdate a webhook URL or event filter.
/v1/webhooks/:idAUTHDelete a webhook endpoint.
/v1/webhooks/:id/testAUTHSend a test webhook event to verify your endpoint.
Manage your merchant profile, wallets, and API keys.
/v1/merchantAUTHGet your merchant profile, wallets, and usage.
/v1/merchant/walletsAUTHList your connected wallets.
/v1/merchant/walletsAUTHAdd a new wallet address.
Request body
| Field | Type | Req | Description |
|---|---|---|---|
| chain | enum | yes | "evm" | "ton" | "tron" | "solana" |
| address | string | yes | Wallet address |
/v1/merchant/wallets/:idAUTHRemove a wallet.
/v1/merchant/api-keysAUTHCreate an API key. The raw key is returned only once.
Request body
| Field | Type | Req | Description |
|---|---|---|---|
| name | string | no | Label for this key |
/v1/merchant/api-keysAUTHList API keys (without secret values).
/v1/merchant/api-keys/:idAUTHRevoke an API key.
Configure which payment methods and EVM networks are available on your checkout pages.
Available methods: evm, ton, tron, solana, binance_pay, bybit_pay. Exchange pay methods require configured exchange credentials.
/v1/merchant/payment-configAUTHGet your enabled payment methods and EVM networks.
Example response
{
"success": true,
"data": {
"enabledMethods": ["evm", "ton", "binance_pay"],
"evmNetworks": ["ethereum", "base", "arbitrum"]
}
}/v1/merchant/payment-configAUTHUpdate enabled payment methods and EVM networks.
Request body
| Field | Type | Req | Description |
|---|---|---|---|
| enabledMethods | string[] | yes | "evm" | "ton" | "tron" | "solana" | "binance_pay" | "bybit_pay" |
| evmNetworks | string[] | no | "ethereum" | "base" | "arbitrum" | "polygon" | "bsc" | "optimism" |
Connect Binance Pay or Bybit Pay to accept payments via exchange checkout. Customers pay using their exchange app.
/v1/merchant/exchange-credentialsAUTHList your exchange API credentials (secrets are masked).
Example response
{
"success": true,
"data": [
{
"id": "...",
"provider": "binance_pay",
"apiKey": "abc***xyz",
"merchantIdExt": "123456",
"active": true
}
]
}/v1/merchant/exchange-credentialsAUTHAdd or update exchange API credentials. Required to accept Binance Pay or Bybit Pay.
Request body
| Field | Type | Req | Description |
|---|---|---|---|
| provider | enum | yes | "binance_pay" | "bybit_pay" |
| apiKey | string | yes | Exchange API key |
| apiSecret | string | yes | Exchange API secret |
| merchantIdExt | string | no | Binance merchant ID (required for Binance Pay) |
/v1/merchant/exchange-credentials/:providerAUTHRemove exchange credentials. Provider: "binance_pay" or "bybit_pay".
All errors follow a consistent format. HTTP status codes are used meaningfully.
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Product not found"
}
}| Status | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | Invalid request body or params |
| 401 | UNAUTHORIZED | Missing or invalid JWT |
| 404 | NOT_FOUND | Resource does not exist |
| 429 | RATE_LIMITED | Too many requests |
| 500 | INTERNAL_ERROR | Server error |
Integrate AI agent payments via the HTTP 402 protocol.
x402 Documentation