API Documentation

Accept non-custodial stablecoin payments on any chain. Integrate with our REST API in minutes.

Base URL

https://api.payzap.cc

Auth

Bearer <JWT>

Format

JSON

Quick Start

1Sign in at payzap.cc/dashboard with your wallet
2Create a product with a name and price
3Share the payment link or create sessions via API
4Set up webhooks to get notified when payments are confirmed

Authentication

All 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...
GET/v1/auth/nonce

Get 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..."
  }
}
POST/v1/auth

Authenticate with a wallet signature. Returns JWT access + refresh tokens.

Request body

FieldTypeReqDescription
walletAddressstringyesYour wallet address
chainenumyes"evm" | "ton" | "tron" | "solana"
signaturestringyesSigned nonce message
noncestringyesNonce from GET /v1/auth/nonce

Example response

{
  "success": true,
  "data": {
    "accessToken": "eyJhbG...",
    "refreshToken": "eyJhbG...",
    "merchant": { "id": "...", "plan": "free" }
  }
}
POST/v1/auth/refresh

Refresh an expired access token.

Request body

FieldTypeReqDescription
refreshTokenstringyesRefresh token from auth response

Products

Products represent items or services you sell. Each product has a price and generates a payment link.

POST/v1/productsAUTH

Create a new product.

Request body

FieldTypeReqDescription
namestringyesProduct name (1-255 chars)
descriptionstringnoDescription (up to 2000 chars)
priceAmountnumberyesPrice in USD (max 1,000,000). Note: returned as string in API responses
priceCurrencystringnoCurrency code (default: "USD")
acceptedChainsstring[]no"evm" | "ton" | "tron" | "solana" | "binance_pay" | "bybit_pay"
successUrlstringnoURL to redirect customer after successful payment
metadataobjectnoArbitrary JSON metadata
GET/v1/productsAUTH

List your products.

Query params

ParamTypeDescription
limitnumber1-100 (default: 20)
offsetnumberOffset (default: 0)
GET/v1/products/:idAUTH

Get a single product by ID.

PATCH/v1/products/:idAUTH

Update a product. All fields optional.

Request body

FieldTypeReqDescription
namestringnoNew name
priceAmountnumbernoNew price
activebooleannoEnable/disable
acceptedChainsstring[]noAccepted payment methods
successUrlstring|nullnoSuccess redirect URL (null to clear)
DELETE/v1/products/:idAUTH

Deactivate a product (soft delete).

Payments

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.

POST/v1/payments/session

Create a payment session. Returns a session with wallet address and payment link. Public endpoint — no auth required.

Request body

FieldTypeReqDescription
productIduuidyesProduct ID
chainenumyes"evm" | "ton" | "tron" | "solana" | "binance_pay" | "bybit_pay"
assetenumyes"USDT" | "USDC" | "DAI" | "BUSD"
amountnumbernoOverride product price (dynamic pricing)
customerRefstringnoYour internal customer/order ID
successUrlstringnoOverride product success URL for this session
metadataobjectnoArbitrary 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_..."
  }
}
GET/v1/payments/session/:id

Get session status. Use for polling from your frontend. Public — no auth.

GET/v1/paymentsAUTH

List your completed payments.

Query params

ParamTypeDescription
limitnumber1-100 (default: 20)
offsetnumberOffset (default: 0)
GET/v1/payments/:idAUTH

Get a single payment by ID.

Every product gets a payment link at https://payzap.cc/pay/<product_id>. Customize the checkout experience with query parameters:

ParamTypeDescription
success_urlstringRedirect URL after successful payment (overrides product setting)
amountnumberOverride product price (dynamic pricing)
refstringCustomer reference / order ID
themestringUI 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.

Embed Widget

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.

Custom Checkout

Build your own payment UI using the PayZap API. Two endpoints are all you need.

Flow

1Create a payment session via POST /v1/payments/session — you get the merchantWallet, amount, and asset
2User sends tokens to merchantWallet using your UI (wagmi, ethers, viem, TonConnect, etc.)
3Poll 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

StatusDescription
pendingWaiting for payment
confirmingTransaction detected, waiting for block confirmations
completedPayment confirmed on-chain
expiredSession timed out (30 min)
failedTransaction 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.

Webhooks

Get real-time notifications when payment events occur. Webhooks are signed with HMAC-SHA256 for verification.

Events

EventDescription
payment.completedPayment confirmed on-chain
payment.pendingTransfer detected, waiting for confirmations
payment.failedPayment failed or was rejected
payment.expiredSession 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');
}
POST/v1/webhooksAUTH

Create a webhook endpoint. The secret is returned only once — store it securely.

Request body

FieldTypeReqDescription
urlstringyesHTTPS endpoint URL
eventsstring[]noEvent filter (default: all events)

Example response

{
  "success": true,
  "data": {
    "id": "whk_...",
    "url": "https://example.com/webhook",
    "events": ["payment.completed", "payment.failed"],
    "secret": "whsec_..."
  }
}
GET/v1/webhooksAUTH

List your webhook endpoints.

PATCH/v1/webhooks/:idAUTH

Update a webhook URL or event filter.

DELETE/v1/webhooks/:idAUTH

Delete a webhook endpoint.

POST/v1/webhooks/:id/testAUTH

Send a test webhook event to verify your endpoint.

Merchant

Manage your merchant profile, wallets, and API keys.

GET/v1/merchantAUTH

Get your merchant profile, wallets, and usage.

GET/v1/merchant/walletsAUTH

List your connected wallets.

POST/v1/merchant/walletsAUTH

Add a new wallet address.

Request body

FieldTypeReqDescription
chainenumyes"evm" | "ton" | "tron" | "solana"
addressstringyesWallet address
DELETE/v1/merchant/wallets/:idAUTH

Remove a wallet.

POST/v1/merchant/api-keysAUTH

Create an API key. The raw key is returned only once.

Request body

FieldTypeReqDescription
namestringnoLabel for this key
GET/v1/merchant/api-keysAUTH

List API keys (without secret values).

DELETE/v1/merchant/api-keys/:idAUTH

Revoke an API key.

Payment Config

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.

GET/v1/merchant/payment-configAUTH

Get your enabled payment methods and EVM networks.

Example response

{
  "success": true,
  "data": {
    "enabledMethods": ["evm", "ton", "binance_pay"],
    "evmNetworks": ["ethereum", "base", "arbitrum"]
  }
}
PUT/v1/merchant/payment-configAUTH

Update enabled payment methods and EVM networks.

Request body

FieldTypeReqDescription
enabledMethodsstring[]yes"evm" | "ton" | "tron" | "solana" | "binance_pay" | "bybit_pay"
evmNetworksstring[]no"ethereum" | "base" | "arbitrum" | "polygon" | "bsc" | "optimism"

Exchange Credentials

Connect Binance Pay or Bybit Pay to accept payments via exchange checkout. Customers pay using their exchange app.

GET/v1/merchant/exchange-credentialsAUTH

List your exchange API credentials (secrets are masked).

Example response

{
  "success": true,
  "data": [
    {
      "id": "...",
      "provider": "binance_pay",
      "apiKey": "abc***xyz",
      "merchantIdExt": "123456",
      "active": true
    }
  ]
}
POST/v1/merchant/exchange-credentialsAUTH

Add or update exchange API credentials. Required to accept Binance Pay or Bybit Pay.

Request body

FieldTypeReqDescription
providerenumyes"binance_pay" | "bybit_pay"
apiKeystringyesExchange API key
apiSecretstringyesExchange API secret
merchantIdExtstringnoBinance merchant ID (required for Binance Pay)
DELETE/v1/merchant/exchange-credentials/:providerAUTH

Remove exchange credentials. Provider: "binance_pay" or "bybit_pay".

Errors

All errors follow a consistent format. HTTP status codes are used meaningfully.

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Product not found"
  }
}
StatusCodeMeaning
400VALIDATION_ERRORInvalid request body or params
401UNAUTHORIZEDMissing or invalid JWT
404NOT_FOUNDResource does not exist
429RATE_LIMITEDToo many requests
500INTERNAL_ERRORServer error

Looking for x402 protocol docs?

Integrate AI agent payments via the HTTP 402 protocol.

x402 Documentation