API Key Mode - HTTP API Integration
Use an API Key from your server to access merchant business APIs, including orders, wallets, notifications, and merchant configuration. Account profile and login-security APIs continue to require JWT.
Get Your API Key
- Log in to PolyPay merchant dashboard
- Go to "API Keys" page
- Click "Create Key"
- Choose an expiration time. New API Keys expire after 7 days by default. If you choose never expires, the API Key will not expire automatically; existing API Keys never expire by default.
- Securely save the generated API Key
⚠️ API Key is only shown once. Save it immediately! If lost, you need to regenerate. Never expose API Key in frontend code, Git repos, or logs.
Plan Quotas and Billing Scope
- Subscription plans apply per merchant. Monthly successful orders, wallet addresses, notification quotas, and available channels are isolated for each merchant; switching merchants neither inherits nor consumes another merchant plan.
- See the public pricing page. PolyPay does not take a cut of payments; you pay for plan quotas and metered usage.
- The first merchant created under an account keeps the Free live-payment entitlement; disabling another merchant does not transfer it. Additional merchants need Pay-as-you-go, Pro, or Business enabled before they can create live orders or Checkout.
- Account balance is stored per user and split into paid balance and bonus credit; bonus credit is spent first. Mixed subscription checkout reserves the available balance and sends the remainder to crypto checkout, releasing the hold on failure or expiry. Below Available Plans, the paginated balance records include recharge and consumption ledger entries plus pending, completed, or rejected withdrawal requests with their status.
- Both paid and bonus balances can be withdrawn as USDT. The receiving address can be selected from the current merchant’s enabled, network-matched wallets that support USDT, or entered manually. The backend prices the fee from the selected network’s live fast rate plus a 20% volatility buffer, reprices it on submission, and freezes it with the withdrawal amount using bonus balance first. The backend never initiates an on-chain payout; operations completes the debit or restores the original balance buckets from the bot message.
- Paid recurring plans such as Pro and Business can enable balance auto-renewal independently per merchant. At expiry, PolyPay deducts that merchant’s next cycle from the shared account balance; insufficient balance leaves only that merchant’s plan expired.
- Pay-as-you-go is enabled independently per merchant. That merchant’s orders, notifications, and wallet addresses are charged from the shared account balance from the first usage, without enabling it for other merchants.
- Payment Webhook callbacks are used for order status synchronization and do not count toward external alert notification quota.
- Telegram, Email, WhatsApp, notification Webhook, WeCom, Discord, and other business alerts count toward the current merchant’s plan quota; in-app notifications and system subscription expiry notices do not.
- Subscription expiring and expired notices are sent by PolyPay with fixed system copy and cannot be customized in notification templates.
- Custom Domain and AI Agent Payments require an active subscription, and are also available on Pay-as-you-go. When a subscription expires or becomes inactive, configurations are retained but frozen: they cannot be managed, the custom domain hostname is removed from PolyPay Cloudflare routing while your DNS record can stay in place, and x402 verify/settle runtime calls are rejected until an active subscription is restored. After restoration, PolyPay recreates the custom hostname automatically.
API Reference
Basic Information
| SDK-compatible Base URL | https://api.polypay.ai/api/v1/pay/sdk |
| Merchant API Base URL | https://api.polypay.ai/api/v1/pay |
| Authentication | X-API-Key (recommended) or Bearer Token (legacy-compatible) |
| Data Format | JSON |
Authentication
Merchant business APIs accept either a dashboard JWT or an API Key. For server-side API Key calls, use either header below; X-API-Key is explicit and recommended:
| Header | Value |
|---|---|
X-API-Key | YOUR_API_KEY (recommended) |
Authorization | Bearer YOUR_API_KEY (legacy-compatible) |
Content-Type | application/json |
Redirect to Hosted Checkout
If you want PolyPay to show payment-method selection, request the checkout_url below with your API Key and redirect the customer to it. Without currency/network, PolyPay creates a Method pending order. The payment URL itself is the complete public payment entry; the client does not need to store a checkout session. The page may submit only currency and network, while amount, merchant order ID, and callback URLs are always read from the stored order. The URL uses /pay/{trade_id}, and selecting or changing a payment method preserves that trade_id and URL. Supplying both currency and network skips selection and opens the payment page directly. After expiry, request a new checkout_url with the same merchant order ID.
POST /order/checkout
curl -X POST https://api.polypay.ai/api/v1/pay/sdk/order/checkout \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"mch_order_id": "ORDER_001",
"amount": 10.00,
"notify_url": "https://your-site.com/webhook",
"redirect_url": "https://your-site.com/success",
"locale": "en"
}'Response example:
{
"code": 0,
"message": "",
"data": {
"checkout_url": "https://checkout.polypay.ai/en/pay/202607311785497228532519443",
"payment_url": "https://checkout.polypay.ai/en/pay/202607311785497228532519443"
}
}Create Order
POST /order/add
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
currency | string | ✅ | Currency (USDT/USDC/BUSD) |
network | string | ✅ | Network (tron/ethereum/bsc/polygon/solana) |
amount | number | ✅ | Amount |
mch_order_id | string | ❌ | Merchant order ID (max 32 chars, auto-generated if not provided) |
notify_url | string | ❌ | Webhook callback URL |
redirect_url | string | ❌ | Redirect URL after payment |
Response Parameters
| Parameter | Type | Description |
|---|---|---|
trade_id | string | PolyPay transaction ID |
currency | string | Currency |
network | string | Blockchain network |
amount | number | Order amount |
actual_amount | number | Actual payment amount (4 decimals) |
address | string | Payment wallet address |
expiration_time | number | Expiration timestamp (seconds) |
payment_url | string | Payment page URL |
Code Examples
curl -X POST https://api.polypay.ai/api/v1/pay/sdk/order/add \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"currency": "USDT",
"network": "tron",
"amount": 100.00,
"mch_order_id": "ORDER_123456",
"notify_url": "https://your-site.com/webhook",
"redirect_url": "https://your-site.com/success"
}'Response Example
{
"code": 0,
"message": "success",
"data": {
"trade_id": "PP202412110001",
"currency": "USDT",
"network": "tron",
"amount": 100.00,
"actual_amount": 100.0001,
"address": "TXxx...xxx",
"expiration_time": 1704067200,
"payment_url": "https://checkout.polypay.ai/status/PP202412110001"
}
}Query Order
POST /order/detail
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
trade_id | string | * | PolyPay transaction ID |
mch_order_id | string | * | Merchant order ID |
* Either trade_id or mch_order_id is required
Response Example
curl -X POST https://api.polypay.ai/api/v1/pay/sdk/order/detail \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"trade_id": "PP202412110001"
}'Cancel Order
POST /order/cancel-by-trade-id
When your platform cancels, voids, or replaces an unpaid order, synchronize the cancellation with PolyPay. A successful cancellation immediately releases the reserved receiving-address and payable-amount pair, preventing a later same-amount order from being adjusted because of the stale reservation (commonly by 0.01 for stablecoins). The request is safe to retry; paid or expired orders cannot be cancelled.
curl -X POST https://api.polypay.ai/api/v1/pay/order/cancel-by-trade-id \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"trade_id": "PP202412110001"
}'Webhook Callbacks
We send HTTP POST requests to your configured Webhook URL when an order is created and when its status changes.
Webhook Payload Example
{
"order_no": "ORDER_123456",
"status": 2,
"amount": 100.0001,
"currency": "USDT",
"currency_name": "usdt",
"network": "Tron",
"contract_addr": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"hash": "abc123...",
"wallet_address": "TXxx...xxx",
"environment": "production",
"event_id": "evt_xxx"
}network is the payment chain; currency_name is the lowercase currency name such as usdt/usdc; hash is the on-chain transaction hash. contract_addr is an empty string for native assets or assets without a configured contract.
Status Types
| status Value | Description |
|---|---|
1 | Pending payment |
2 | Payment successful |
3 | Order expired |
4 | Order cancelled |
5 | Manual recharge (handled as paid) |
6 | On-chain confirming (treated as pending for merchants; no paid webhook is sent) |
7 | Marked paid (handled as paid) |
Verify Webhook v2 Signature
Webhook v2 uses a PolyPay platform Ed25519 signing key and does not depend on the merchant API Key. Verify the raw request body on your server with the public key obtained from the platform JWKS.
Verification Flow (Server-side)
- Require X-Webhook-Signature-Version to be v2 and read the key ID, merchant ID, environment, timestamp, nonce, and v2 signature headers.
- Require the merchant ID and environment to match server-side expectations and enforce a 300-second timestamp window.
- Select the Ed25519 public key by key ID from /api/v1/pay/public/webhook-jwks and verify the signature over the raw request body.
- Atomically consume the nonce in shared storage such as Redis and reject replays.
- Parse the event only after successful verification and process it idempotently by environment and event_id.
<?php
use PolyPay\PolyPay;
use PolyPay\WebhookHandler;
use PolyPay\Exception\SignatureException;
$polypay = new PolyPay(getenv('POLYPAY_API_KEY') ?: '');
try {
$event = $polypay
->webhookV2('MCH_YOUR_ID', 'production')
->handle();
$status = WebhookHandler::resolveStatus($event);
if ($status === 'paid') {
handleOrderPaidIdempotently($event);
}
http_response_code(200);
echo 'OK';
} catch (SignatureException $e) {
http_response_code($e->getHttpStatus());
echo 'Unauthorized';
}For the complete header contract, signed payload, and Node.js example, see Webhook Security.
Error Codes
| Code | Description |
|---|---|
0 | Success |
10001 | Invalid parameters |
10002 | Invalid signature |
10003 | Order not found |
10004 | Merchant disabled |
10005 | Invalid API Key |
Security Best Practices
- Protect API Key: Never expose API Key in frontend code, Git repos, or logs
- Use Environment Variables: Store API Key in environment variables
- Verify Webhook Signatures: Always verify Webhook request signatures
- Use HTTPS: Ensure your Webhook endpoint uses HTTPS
- Implement Idempotency: Webhooks may be sent multiple times, ensure idempotent logic