审批优先 · 每笔成功收款只收 30%
首页
行业
多肽CBD保健品电子烟成人外汇博彩仿牌
定价 API 文档 关于 常见问题 登录 打开 Telegram 申请

在 CoinPay 上接入。

创建支付、把客户送到托管收银台,在钱到账时验签 webhook。接口很小,状态清楚,不靠猜。

支付 API

CoinPay API 用来创建支付、获取托管收银台地址,并接收带签名的 webhook 事件。一个订单对应一笔支付。接口在本地和线上环境都可以联调。

本地 http://127.0.0.1:8791/api/v1
生产 https://api.coinpayusd.com/v1

商户站:https://coinpayusd.com · API Key 在商户审核通过后发放。

认证

在服务端请求里带上 X-Api-Key。不要把 Key 放进前端代码、截图或客服对话。

HTTP
X-Api-Key: cp_live_...
前缀环境用途
cp_live_生产真实收款和线上 webhook 事件。
cp_test_沙箱隔离的测试支付,不产生真实资金。

幂等

在 POST /payments 时带上 Idempotency-Key。Key 按商户隔离,重复提交会返回同一笔支付,避免重复扣款。

HTTP
Idempotency-Key: order-1042
在门户里轮换。 API Key 是服务端凭据,不能暴露在前端、截图或客服消息里。

创建支付

POST /payments

amount 用 JSON 数字提交。merchantOrderId 是你的订单唯一参考号。返回结果里包含托管收银台地址 checkoutUrl。

curl
curl -X POST http://127.0.0.1:8791/api/v1/payments   -H 'X-Api-Key: cp_live_...'   -H 'Idempotency-Key: order-1042'   -H 'Content-Type: application/json'   -d '{"amount":149.00,"currency":"USD","merchantOrderId":"order-1042","customer":{"email":"buyer@example.com"},"callbackUrl":"https://your-store.com/webhook"}'
响应
{
  "ok": true,
  "payment": {
    "id": "ord_...",
    "orderNo": "CP-...",
    "status": "pending",
    "amount": 149.00,
    "currency": "USD",
    "commissionBps": 3000,
    "commissionCents": 4470,
    "merchantNetCents": 10281
  },
  "checkoutUrl": "http://127.0.0.1:8791/pay/ord_...?token=..."
}
字段类型说明
amountnumber收款金额,例如 149.00。
currencystring商户收款目前支持 USD。
merchantOrderIdstring你的唯一订单参考号。
customer.emailstring可选,用于收据。
callbackUrlstring接收 webhook 的服务端地址。

commissionBps=3000 表示 30% 服务费;commissionCents 和 merchantNetCents 是这笔支付的分账金额。

查询支付

GET /payments/:id

用创建接口返回的支付 ID 查询状态。status 会从 pending 变为 paid。

curl
curl http://127.0.0.1:8791/api/v1/payments/ord_...   -H 'X-Api-Key: cp_live_...'
响应
{
  "ok": true,
  "payment": {
    "id": "ord_...",
    "status": "paid"
  }
}

Webhook 验签与事件

CoinPay 用商户 webhook secret 对原始请求体做 HMAC-SHA256 签名。签名放在 X-Coinpay-Signature,事件名放在 X-Coinpay-Event,投递 ID 放在 X-Coinpay-Delivery。一次支付可能收到四个事件,处理逻辑必须按事件分别判断。

Headers
X-Coinpay-Signature: sha256=HMAC_SHA256(secret, raw_body)
X-Coinpay-Event: payment.succeeded
X-Coinpay-Delivery: <orderId>-<attempt>

四个事件

事件共用同一份 payload schema;差异在 event 与 status。不要只看 HTTP 200,也不要只凭浏览器跳转判断支付成功。

← 可横向滚动查看完整事件表 →

事件触发时机payload.status处理
payment.created订单创建成功并返回 checkoutUrl 后;记录待支付订单,不履约。pending记录待支付订单,不履约。
payment.succeeded支付确认到账后;验签后幂等履约。paid验签后幂等履约。
payment.failed建单失败或处理失败,订单不可继续支付;不要履约。failed不履约;新支付另开订单。
payment.underpaid实付低于订单金额 99%;不要按已付订单履约。underpaid不履约;标记少付并处理。

Payload 字段

四个事件都包含以下字段;event 与 status 随事件变化。

字段类型说明
id / orderIdstringCoinPay 订单 ID;同一订单的多个事件使用同一个 ID。
orderNostring面向人的订单号,CP- 开头。
eventstring事件名:payment.created / succeeded / failed / underpaid。
statusstring事件状态:pending / paid / failed / underpaid。
amount / amountCentsnumber订单金额;美元与最小货币单位。
currencystringUSD。
commissionBpsnumber创建时锁定的佣金比例,3000 = 30%。
commissionCentsnumber平台佣金,单位分。
merchantNetCentsnumber商户净额,单位分。

四个事件的 payload 示例

事件体 · payment.created
{
  "id": "ord_...",
  "orderId": "ord_...",
  "orderNo": "CP-...",
  "amount": 149.0,
  "amountCents": 14900,
  "currency": "USD",
  "commissionBps": 3000,
  "commissionCents": 4470,
  "merchantNetCents": 10281,
  "status": "pending",
  "event": "payment.created"
}
事件体 · payment.succeeded
{
  "id": "ord_...",
  "orderId": "ord_...",
  "orderNo": "CP-...",
  "amount": 149.0,
  "amountCents": 14900,
  "currency": "USD",
  "commissionBps": 3000,
  "commissionCents": 4470,
  "merchantNetCents": 10281,
  "status": "paid",
  "event": "payment.succeeded"
}
事件体 · payment.failed
{
  "id": "ord_...",
  "orderId": "ord_...",
  "orderNo": "CP-...",
  "amount": 149.0,
  "amountCents": 14900,
  "currency": "USD",
  "commissionBps": 3000,
  "commissionCents": 4470,
  "merchantNetCents": 10281,
  "status": "failed",
  "event": "payment.failed"
}
事件体 · payment.underpaid
{
  "id": "ord_...",
  "orderId": "ord_...",
  "orderNo": "CP-...",
  "amount": 149.0,
  "amountCents": 14900,
  "currency": "USD",
  "commissionBps": 3000,
  "commissionCents": 4470,
  "merchantNetCents": 10281,
  "status": "underpaid",
  "event": "payment.underpaid"
}

失败与少付处理

情况建议处理
payment.created记录 checkout URL 和订单状态,不履约;客户完成支付前继续保持 pending。
payment.succeeded验签通过后按 id + event 做幂等,再履约/记账;重复投递不能重复发货。
payment.failed订单已失败,不履约;不要让客户继续在原 checkout 重试,必要时创建新支付并保留原订单审计记录。
payment.underpaid到账低于订单金额 99%,不按成功订单履约;在后台标记少付并联系客户补款,或创建新订单,不要把 underpaid 当作 paid。

验签必须使用原始请求体。失败重试 3 次,间隔 150ms、300ms,每次投递都记录在 webhook_deliveries。

Node.js
const crypto = require('crypto');

const expected = 'sha256=' + crypto
  .createHmac('sha256', secret)
  .update(rawBody)
  .digest('hex');

if (expected !== req.headers['x-coinpay-signature']) {
  throw new Error('Invalid CoinPay signature');
}
完整签名代码在商户门户文档。 门户里的 Node.js / PHP 示例包含原始 body 验签、恒定时间比较和幂等处理;本页只保留最小验签示例。 登录商户门户 → 开发文档

错误码

错误响应会带稳定的 code 和可读说明。给客户展示重试或联系支持的入口,不要把原始服务端错误直接抛给客户。

HTTP错误码API 返回
400invalid_amountamount must be positive
400amount_out_of_limitsamount is outside merchant limits
400invalid_methodunsupported payment method
401missing_api_keyX-Api-Key is required
401invalid_api_keyinvalid API key
403merchant_inactivemerchant is paused
403ip_not_allowedrequest IP is not allowlisted
404payment_not_foundpayment not found
429rate_limitedtoo many requests
429daily_limit_reachedmerchant daily limit reached

Sandbox 完整流程

沙箱使用 cp_test_ Key,与生产隔离。测试支付不产生真实资金,但会走和线上相同的签名与重试逻辑。

  1. 申请 / 复制 test key:商户审批后登录商户门户「API 与 Webhook」,复制 Sandbox API Key(cp_test_)和 Webhook 签名密钥(同一 secret 也用于沙箱事件)。入口不明显时,在门户申请沙箱权限,或通过 Telegram 联系客服开通测试凭据。
  2. 配置沙箱 webhook:在门户把接收地址指向你的测试端点(可用 webhook.site 或 ngrok),保存后确认最近投递状态。
  3. 创建测试支付:用 Sandbox Key 调用 POST /api/v1/sandbox/payments,拿到 order id 和 checkoutUrl。
  4. 模拟完成 / 失败:调用 .../complete 模拟成功,或 .../fail 模拟失败;不要用真实卡或真实资金。
  5. 核对事件:创建后应收到 payment.created;完成后应收到 payment.succeeded;失败后应收到 payment.failed。用 Webhook 签名密钥(同一 secret 也用于沙箱事件)验签,并在门户查看投递日志。

沙箱凭据

每个商户会从门户拿到以下测试凭据:

  • Sandbox API Key:认证 cp_test_ 请求。
  • Webhook 签名密钥:验证沙箱事件签名;与正式环境共用同一个 secret,也用于沙箱事件。

第一步:创建测试支付

curl
curl -X POST http://127.0.0.1:8791/api/v1/sandbox/payments \
  -H 'X-Api-Key: cp_test_...' \
  -H 'Content-Type: application/json' \
  -d '{"amount":7.00,"currency":"USD","merchantOrderId":"sandbox-001"}' 

第二步:complete 或 fail

curl
curl -X POST http://127.0.0.1:8791/api/v1/sandbox/payments/ord_.../complete \
  -H 'X-Api-Key: cp_test_...' 
curl
curl -X POST http://127.0.0.1:8791/api/v1/sandbox/payments/ord_.../fail \
  -H 'X-Api-Key: cp_test_...' 
测试通过再切生产。 换回 Live Key、生产 webhook 地址和 secret,再从小额真实支付开始。

本地沙箱地址:http://127.0.0.1:8791/api/v1;生产:https://api.coinpayusd.com/v1。underpaid 不对应沙箱 complete/fail 按钮,需用真实少付场景或受控事件回放验证。

下一步

这周就把收款路径接上。

提交申请,拿到 API 凭据,从小规模 curl 测试走到生产接入。