Peptide-Pay API
用于刷卡和加密货币收款的 REST API,结算到你掌控的 USDC 钱包。一个 POST 创建结账。一个 webhook 通知你已付款。30 分钟内上线。
开始使用
Peptide-Pay 支持四种接入模式。按你的约束选一种;底层 API 完全一样。
拖拽即用按钮,无需注册。把你的 USDC 钱包地址放在请求体里。后端零状态;公开,在 DevTools 可见。
服务端到服务端。用 Bearer sk_live_ 把钱包藏起来。从后台配置品牌、webhook、批量出金。
现成插件。上传 ZIP,粘贴 API key + webhook secret,订单在付款时自动完成。支持 HPOS,WC 7.0+。
Custom App + Manual Payment Method。在现有店铺上约 30 分钟安装。我们通过 Shopify Admin API 将订单标记为已支付。
快速开始(5 分钟)
三步:创建会话、重定向客户、处理 webhook。下面的示例是生产级的 Node.js 结账路由。
// Create a checkout session and redirect your customer.
// Authorization resolves the merchant wallet server-side — no wallet in the body.
const res = await fetch('https://peptide-pay.com/api/v1/checkout/init', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.PEPTIDEPAY_API_KEY}`, // sk_live_…
},
body: JSON.stringify({
amount_cents: 5000, // €50.00 — integer, in cents
currency: 'EUR', // EUR, USD, GBP, CAD, AUD, CHF
email: 'buyer@example.com', // optional, shown in checkout
success_url: 'https://mystore.com/success',
cancel_url: 'https://mystore.com/cart',
webhook_url: 'https://mystore.com/api/peptidepay-webhook',
metadata: { order_id: '1234' },
}),
});
const { id, url, tracking_number } = await res.json();
// => { id: 'cs_abc…', url: 'https://peptide-pay.com/session/cs_abc…',
// tracking_number: '0x…', provider: 'gateway', status: 'pending', … }
// Redirect your customer to the hosted checkout.
return Response.redirect(url, 303);这就是整条主干流程。客户落在 的托管结账页,选银行卡或加密货币,你的 webhook 在付款 30 秒内触发。
认证
两种方案,取决于接入模式:
| 方案 | 怎么用 | 何时用 |
|---|---|---|
| Bearer token | Authorization: Bearer sk_live_… | 服务端。钱包保持私密。 |
| 钱包放请求体 | { "wallet": "0x…", … } | 无后端的静态站点 / widget。 |
sk_live_… and an sk_test_… key. Use sk_live_ as your canonical key — that is what every example here uses. The sk_test_ key is provided for the webhook-receiver simulator at /api/v1/test/fire-webhook. Peptide-Pay settles real on-chain USDC — there is no test network. To dry-run an integration, run a $1 real payment and refund yourself.API 参考
Base URL:。所有接口使用 JSON,成功返回单个对象,4xx/5xx 返回 对象。
#创建结账会话
生成托管结账 URL。客户打开,用银行卡或加密货币付款,Peptide-Pay 以 USDC 结算到你的钱包,webhook 触发。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| amount_cents | integer | 必填 | 以最小货币单位表示的金额(分)。范围 100 – 10 000 000。 |
| currency | string | 必填 | ISO 4217 代码。支持:EUR、USD、GBP、CAD、AUD、CHF。 |
| wallet | string | 二选一 | Polygon 上的 USDC 钱包(0x + 40 位 hex)。通过 Bearer 密钥认证时非必填。 |
| customer_email | string | 选填 | 显示在结账页 UI 上,并转发给法币通道用于 KYC 复用。 |
| success_url | url | 选填 | 付款成功后重定向。仅 http/https。 |
| cancel_url | url | 选填 | 客户放弃结账时重定向。 |
| webhook_url | url | 选填 | order.paid 事件的 POST 目标。覆盖后台默认值。 |
| provider | string | 选填 | 默认 'gateway'(智能选路 —— 推荐)。或从 GET /providers 指定一个具体的法币通道 id(例如 moonpay、revolut、banxa、transak)。 |
| product_name | string | 选填 | 结账页显示的标签(最多 80 字符)。 |
| metadata | object | 选填 | 最多 10 组 string key/value,会在 webhook 中原样回传。保留 key:order_id。 |
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 选填 | 会话 id,以 cs_ 开头。 |
| url | string | 选填 | 重定向客户用的托管结账 URL。 |
| status | string | 选填 | 创建时永远是 "pending"。 |
| amount | integer | 选填 | amount_cents 的回显。 |
| currency | string | 选填 | currency 的回显。 |
| provider | string | 选填 | provider 的回显(默认 'gateway')。 |
| expires_at | string | 选填 | ISO 8601 过期时间(创建后 24 小时)。 |
| tracking_number | string | 选填 | Polygon 结算地址 —— 匹配 webhook 负载中的 address_in,可用 /track 实时监控。 |
示例
// Node.js 18+
const res = await fetch('https://peptide-pay.com/api/v1/checkout/init', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.PEPTIDEPAY_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(), // safe double-submit
},
body: JSON.stringify({
amount_cents: 5000,
currency: 'EUR',
email: 'buyer@example.com',
success_url: 'https://mystore.com/success',
cancel_url: 'https://mystore.com/cart',
metadata: { order_id: '1234' },
}),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { id, url } = await res.json();
return Response.redirect(url, 303);#查询会话(轮询)
作为 webhook 降级方案,或重定向后用来渲染成功页。服务端每次调用都会回源结算层 —— 便宜(<200ms),每 3-5 秒轮询没问题。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 选填 | 会话 id。 |
| status | string | 选填 | pending | paid | expired | failed。 |
| amount | integer | 选填 | 原始金额(分)。 |
| currency | string | 选填 | 原始币种。 |
| paid_at | string|null | 选填 | ISO 8601,链上结算完成时间。 |
| paid_provider | string|null | 选填 | 实际处理付款的通道(可能和请求的不一样)。 |
| txid | string|null | 选填 | Polygon 结算 txid。链接到 polygonscan.com/tx/{txid}。 |
| expires_at | string | 选填 | ISO 8601 过期时间。 |
// Poll every 3-5 seconds until terminal state. Use webhooks for push-
// delivery in production; polling is the fallback when webhooks are down.
async function waitForPayment(sessionId, { timeoutMs = 15 * 60 * 1000 } = {}) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await fetch(`https://peptide-pay.com/api/v1/sessions/${sessionId}`);
const s = await res.json();
if (s.status === 'paid') return s; // terminal: success
if (s.status === 'expired') throw new Error('Session expired');
if (s.status === 'failed') throw new Error('Payment failed');
await new Promise(r => setTimeout(r, 4000));
}
throw new Error('Polling timeout');
}#实时通道矩阵
列出当前接受流量的法币通道,以及每个通道的最低金额。边缘缓存 5 分钟 —— 应用启动时拉一次,不要每次请求都拉。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| providers[].id | string | 选填 | 通道 key(可以作为 `provider` 传给 /checkout/init)。 |
| providers[].provider_name | string | 选填 | 下拉菜单里显示给人看的名称。 |
| providers[].status | string | 选填 | 'active'(此接口始终只返回 active)。 |
| providers[].minimum_currency | string | 选填 | 最低金额的 ISO 代码。 |
| providers[].minimum_amount | number | 选填 | 通道接受的最低金额(以 minimum_currency 单位)。 |
curl -sS 'https://peptide-pay.com/api/v1/providers' | jq '.providers[] | {id, provider_name, minimum_currency, minimum_amount}'
# [
# { "id": "gateway", "provider_name": "Smart (recommended)", "minimum_currency": "USD", "minimum_amount": 1 },
# { "id": "moonpay", "provider_name": "Moonpay", "minimum_currency": "EUR", "minimum_amount": 20 },
# { "id": "revolut", "provider_name": "Revolut Ramp", "minimum_currency": "EUR", "minimum_amount": 10 },
# { "id": "binance", "provider_name": "Binance Pay", "minimum_currency": "EUR", "minimum_amount": 15 },
# …
# ]
# Cache: 5 minutes at the edge. Call once per deploy, not per request.Webhooks
当会话进入终态,我们会向你配置的 (按会话或在后台)POST 一个签名 JSON 事件。做签名验证时永远读取 请求体 —— 重新序列化 JSON 会打乱 key 顺序,HMAC 就失败了。
POST /your-endpoint HTTP/1.1
Host: mystore.com
Content-Type: application/json
x-peptidepay-signature: t=1745300551,v1=3f9b5c1e8a7d… ← HMAC-SHA256, hex
{
"event": "order.paid",
"session_id": "cs_abc123",
"order_id": "1234",
"address_in": "0xAb12…",
"status": "paid",
"amount": 5000,
"currency": "EUR",
"txid": "0xfa89b2…",
"paid_at": "2026-04-23T10:02:31.000Z",
"attempt": 1
}事件类型
| 事件 | 何时 |
|---|---|
| order.paid | 链上结算已确认。 + + 一定存在。标记订单已付。 |
当前只推送 —— expired 和 failed 的会话可通过 观察(24 小时 TTL 后状态变为 ;终态失败显示 )。后续版本可能为这些加推送事件。
签名验证
已注册的商家会收到一个 secret,每次投递都带 头,格式为 。计算 ,用恒定时间比较 。拒绝超过 5 分钟的签名。
// Node.js — Express/Next.js route handler
import crypto from 'node:crypto';
const SECRET = process.env.PEPTIDEPAY_WEBHOOK_SECRET; // dashboard → Webhooks
export async function POST(req) {
const rawBody = await req.text(); // MUST be the raw bytes
const header = req.headers.get('x-peptidepay-signature') ?? '';
const [ tPart, v1Part ] = header.split(',');
const t = tPart?.split('=')[1];
const v1 = v1Part?.split('=')[1];
if (!t || !v1) return new Response('bad sig', { status: 400 });
// Reject replays older than 5 minutes.
if (Math.abs(Date.now() / 1000 - Number(t)) > 300)
return new Response('stale', { status: 400 });
const expected = crypto
.createHmac('sha256', SECRET)
.update(`${t}.${rawBody}`)
.digest('hex');
const ok =
v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(v1, 'hex'), Buffer.from(expected, 'hex'));
if (!ok) return new Response('invalid sig', { status: 401 });
const event = JSON.parse(rawBody);
// Idempotency: dedupe by event.session_id in your DB — retries re-fire
// the same event (with an incrementing "attempt" field) until you 2xx.
if (event.event === 'order.paid') {
await markOrderPaid(event.order_id, event.txid);
}
return new Response('ok');
}重试策略
非 2xx 响应(以及 > 5 秒超时)我们会按指数退避重试。约 42 小时内总共 6 次:
- 第 1 次 —— 确认后立即。
- 第 2 次 —— +5 分钟。
- 第 3 次 —— +15 分钟。
- 第 4 次 —— +1 小时。
- 第 5 次 —— +4 小时。
- 第 6 次 —— +12 小时,然后 +24 小时(最后一次)。
6 次都失败后进入死信队列。你随时可以通过 重新请求当前状态。
常见问题
- 每次投递都提示 '签名无效'
- 你的框架在你做 hash 前就把 body 解析成 JSON 了。读原始字节(Express:express.raw({type:'*/*'});Next.js:req.text();Laravel:request()->getContent();Rails:request.raw_post)。hash 前永不要重新序列化。
- IP 白名单 —— 你们从哪些 IP 发?
- 目前投递来自 Vercel Edge(动态 IP)。我们不发布静态网段。如果你必须白名单,用签名头作为认证闸门,接受任意源 IP —— HMAC 才是真正的身份校验。
- 必须 HTTPS 吗?
- 是。我们拒绝 POST 到 http:// 接口(confusable deputy / 明文重放风险)。ngrok 免费的 https URL 本地测试足够。
- 我的接口很慢 —— 能延长 5 秒超时吗?
- 不能。立即 2xx 响应,然后异步处理(任务队列、setImmediate、goroutine)。长阻塞处理函数总会超时。
SDK
API 很小, 就完全够用 —— 但 Node SDK 给你类型、自动重试和一个帮你做签名验证的 辅助函数。
// npm install github:kinerette/peptide-pay-sdk
import { PeptidePay } from 'peptide-pay';
const pp = new PeptidePay(process.env.PEPTIDEPAY_API_KEY);
// Create a session
const session = await pp.checkout.create({
amount_cents: 5000,
currency: 'EUR',
customer_email: 'buyer@example.com',
success_url: 'https://mystore.com/success',
cancel_url: 'https://mystore.com/cart',
metadata: { order_id: '1234' },
});
// Retrieve a session
const latest = await pp.sessions.retrieve(session.id);
// Verify + parse a webhook (throws on invalid signature)
app.post('/webhooks/peptidepay', express.raw({ type: '*/*' }), (req, res) => {
const event = pp.webhooks.constructEvent(
req.body,
req.headers['x-peptidepay-signature'],
process.env.PEPTIDEPAY_WEBHOOK_SECRET,
);
// event.event === 'order.paid' (currently the only event delivered)
res.sendStatus(200);
});peptide-pay完整类型、webhook 辅助、自动重试。
任意语言一个 POST,一个 GET。不需要库。
费率
统一 —— Peptide-Pay 的全部佣金。没有订阅、没有月费、没有拒付费。卡法币通道费(上游卡处理商收约 4.5%)是透传 —— 客户付,从不影响你的到账。
| 付款方式 | 你付 | 客户付 |
|---|---|---|
| 银行卡 / Apple Pay / Google Pay | 3% | 约 4.5%(法币通道,透传) |
| 加密货币直接(USDC → USDC) | 3% | 仅 gas(Polygon 约 $0.01) |
含示例的完整拆解见 /fees。
测试
每个新商家账户免费获得 —— 完整的 3% 手续费 24 小时内退到你钱包。用它端到端演练完整流程(真实卡、真实 USDC、真实 webhook),上线前先跑一遍。
- 沙箱模式 自动启用:每个商家的前 3 笔已付会话会标记 并自动退款。
- MoonPay 开发测试卡:,过期日期随便填,CVV 随便填,ZIP 10001。
- 本地 webhook 测试:用 ngrok 暴露 localhost,把 URL 粘到会话的 字段。
完整本地回路(ngrok)
# 1. Expose your local webhook endpoint
ngrok http 3000
# 2. Copy the https://xxxx.ngrok-free.app URL and paste it into
# Dashboard → Webhooks → Endpoint URL, OR send it inline:
curl -X POST 'https://peptide-pay.com/api/v1/checkout/init' \
-H "Authorization: Bearer $PEPTIDEPAY_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"amount_cents": 100,
"currency": "EUR",
"customer_email": "test+sandbox@yours.com",
"success_url": "https://yours.com/success",
"cancel_url": "https://yours.com/cart",
"webhook_url": "https://xxxx.ngrok-free.app/webhooks/peptidepay"
}'
# 3. Open the returned `url`, hit MoonPay's dev test card
# 4242 4242 4242 4242 (any future exp, any CVV).
# 4. Your local endpoint receives the signed POST within ~30s of payment.错误与速率限制
所有错误统一用 。状态码是标准 REST。
400401403404429502故障排查
- 后台显示会话 'paid' 但我的 webhook 从没触发
- 检查 webhook_url 能从公网 HTTPS 访问(在 LAN 外用 curl 测试)。如果没问题,用 GET /sessions/{id} 轮询确认状态 —— 后台 /app 会显示 webhook 投递统计(成功率、次数)。42 小时内 6 次尝试后死信;你随时能通过轮询重新同步。
- HMAC 不匹配 —— 签名总是无效
- 99% 的情况:你 hash 的是已重新序列化的 body,而不是原始字节。框架在你处理函数之前自动解析 JSON;你需要原始 buffer。Next.js:任何 .json() 之前先 req.text()。Express:app.use('/webhooks', express.raw({ type: '*/*' }), …)。Rails:request.raw_post。还要检查你计算的是 `HMAC(whsec_secret, t + '.' + rawBody)` —— 而不是 `HMAC(whsec_secret, rawBody)`。时间戳前缀是必须的。
- MoonPay 提示 '你所在国家不可用'
- MoonPay 限制约 20 个国家(伊朗、朝鲜、古巴,完整列表在他们官网)。默认 provider 是 'gateway' —— 智能选路器会自动降级到 Revolut、Transak 或 Banxa,覆盖不同地区。如果你用 provider: 'moonpay' 指定了具体通道,去掉它让路由自己选。
- 'paid' 事件后我的钱包没收到 USDC
- 到 polygonscan.com/address/<your-wallet> 看 USDC(Polygon POS)转账。结算是 97% 到你,3% 到 Peptide-Pay —— 如果你没看到 97% 入账,可能是 init 调用里粘错了钱包。用 GET /sessions/{id} 再确认一下 —— txid 字段指向真实的链上转账。
- 客户被扣了两次
- 不应该发生。每个会话只有一个结算 addressIn;打到同一地址的第二笔付款在我们侧会变成独立会话,只有第一笔会计入你的订单。如果发生了,截图两笔 polygonscan txid + 会话 id 邮件给 hi@peptide-pay.com —— 我们从资金库退重复那笔。
- 出现 502 '支付基础设施暂时不可用'
- 我们的结算上游降级(< 0.5% 的请求)。用相同 Idempotency-Key 30 秒后重试 —— 钱包一旦生成成功,我们的缓存就返回原响应。关注 /status 查看实时事件。
准备好接入?
大多数商家从零到第一笔已付交易不到 30 分钟。