PonponPay
متوسط15 دقيقة قراءة

API Key Mode - HTTP API Integration

Use API Key on server-side to create and manage orders via HTTP API. Suitable for scenarios requiring full control over payment flow.

Get Your API Key

  1. Log in to PonponPay merchant dashboard
  2. Go to "API Keys" page
  3. Click "Create Key"
  4. 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.

API Reference

Basic Information

Base URLhttps://api.ponponpay.com/pay
AuthenticationBearer Token (API Key)
Data FormatJSON

Authentication

All API requests must include the API Key in the header:

HeaderValue
AuthorizationBearer YOUR_API_KEY
Content-Typeapplication/json

Create Order

POST /order/add

Request Parameters

ParameterTypeRequiredDescription
currencystringCurrency (USDT/USDC/BUSD)
networkstringNetwork (tron/ethereum/bsc/polygon/solana)
amountnumberAmount
mch_order_idstringMerchant order ID (max 32 chars, auto-generated if not provided)
notify_urlstringWebhook callback URL
redirect_urlstringRedirect URL after payment

Response Parameters

ParameterTypeDescription
trade_idstringPonponPay transaction ID
currencystringCurrency
networkstringBlockchain network
amountnumberOrder amount
actual_amountnumberActual payment amount (4 decimals)
addressstringPayment wallet address
expiration_timenumberExpiration timestamp (seconds)
payment_urlstringPayment page URL

Code Examples

curl -X POST https://api.ponponpay.com/pay/order/add \
  -H "Authorization: Bearer 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.ponponpay.com/status/PP202412110001"
  }
}

Query Order

POST /order/detail

Request Parameters

ParameterTypeRequiredDescription
trade_idstring*PonponPay transaction ID
mch_order_idstring*Merchant order ID

* Either trade_id or mch_order_id is required

Response Example

curl -X POST https://api.ponponpay.com/pay/order/detail \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "trade_id": "PP202412110001"
  }'

Webhook Callbacks

When order status changes, we send HTTP POST request to your configured Webhook URL.

Status Types

status ValueDescription
1Pending payment
2Payment successful
3Order expired
4Order cancelled
5Manual recharge (handled as paid)

Verify Webhook Signature

Use the examples on this page together with the latest official docs as the source of truth. The protocol may evolve, so keep your server-side verifier updated. Never store API keys or verification secrets in frontend code.

Recommended Verification Flow (Server-side)

  1. Accept callbacks on server-side only. Do not verify in frontend code.
  2. Validate source and freshness first; reject expired or replayed requests.
  3. Use the official plugin or your internal wrapper to verify request integrity.
  4. Process business logic only after verification and enforce idempotency by order number.
  5. Return 401 for auth failures, 400 for bad payload, and 200/OK on success.
import crypto from 'crypto';
import express from 'express';
import { markEventProcessed } from './idempotency-store';

const app = express();
const apiKey = process.env.PONPONPAY_API_KEY || '';
const nonceStore = new Map(); // 生产建议换 Redis

app.use(express.json({
  type: 'application/json',
  verify: (req, _res, buf) => {
    req.rawBody = buf.toString('utf8');
  }
}));

function verifyPonponPayWebhook(req) {
  const prefix = String(req.headers['x-key-prefix'] || '');
  const timestamp = String(req.headers['x-timestamp'] || '');
  const nonce = String(req.headers['x-nonce'] || '');
  const signature = String(req.headers['x-signature'] || '').toLowerCase();
  const rawBody = req.rawBody || '';

  if (!apiKey || !prefix || !timestamp || !nonce || !signature) return false;
  if (!/^\d+$/.test(timestamp)) return false;

  const ts = Number(timestamp);
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - ts) > 300) return false;
  if (prefix !== apiKey.slice(0, 12)) return false;
  if (!/^[A-Za-z0-9]{16,128}$/.test(nonce)) return false;

  const nonceKey = `${timestamp}:${nonce}`;
  if (nonceStore.has(nonceKey)) return false;
  nonceStore.set(nonceKey, Date.now());
  setTimeout(() => nonceStore.delete(nonceKey), 10 * 60 * 1000);

  const keyHash = crypto.createHash('sha256').update(apiKey).digest('hex');
  const payload = `${timestamp}\n${nonce}\n${rawBody}`;
  const expected = crypto.createHmac('sha256', keyHash).update(payload).digest('hex');

  const a = Buffer.from(signature, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(a, b);
}

app.post('/webhook', express.json({ type: 'application/json' }), async (req, res) => {
  if (!verifyPonponPayWebhook(req)) {
    return res.status(401).send('Unauthorized');
  }

  const event = req.body;
  if (!event?.order_no || event?.status === undefined) {
    return res.status(400).send('Bad Request');
  }

  // 幂等保护:相同订单事件只处理一次
  const firstTime = await markEventProcessed(event.order_no, event.status);
  if (!firstTime) {
    return res.status(200).send('OK');
  }

  switch (Number(event.status)) {
    case 2:
    case 5:
      await handleOrderPaid(event.data);
      break;
    case 3:
      await handleOrderExpired(event.data);
      break;
    case 4:
      await handleOrderCancelled(event.data);
      break;
    default:
      break;
  }

  res.status(200).send('OK');
});

Error Codes

CodeDescription
0Success
10001Invalid parameters
10002Invalid signature
10003Order not found
10004Merchant disabled
10005Invalid 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