PaymentOne 开发者接入指引
这份文档面向要把业务系统接入 PaymentOne 的开发者。PaymentOne 负责创建支付意图、展示聚合收银台、路由上游支付通道、同步支付状态,并在确认成功后通知你的业务系统。用户余额、订单状态、会员权益、订阅开通和幂等入账仍由业务系统自己负责。
选择接入方式
| 场景 | 推荐方式 | 接入成本 |
|---|---|---|
| 现有系统已经支持易支付 | 易支付兼容接入 | 把网关改成 https://payment.agentone.work/easypay,使用后台应用里的 PID 和 KEY |
| 新系统或希望拿到结构化状态 | 标准 JSON API 接入 | 服务端调用 POST https://payment.agentone.work/v1/payment-intents,使用 HMAC-SHA256 签名 |
| 想让用户自己选择支付宝、微信、银行卡等方式 | PaymentOne 聚合收银台 | 易支付兼容模式默认进入聚合收银台;标准 API 传 provider=paymentone |
| 想在服务端指定上游通道 | 标准 API 指定 provider/method | 传 provider=stripe/easypay 和 method=card/alipay/wxpay |
接入前先在后台完成配置:
- 在“应用”里创建业务应用,复制易支付
PID、KEY,或标准 API 的应用 ID 和标准接口密钥。 - 在应用里填写允许通知的域名,例如
merchant.example.com。notify_url的 host 必须匹配该配置。 - 在“支付通道”里创建并启用上游通道,例如 EasyPay、Stripe 或其他已接入 provider。
- 用一笔小额订单跑通创建支付、付款、异步通知、订单查询和重复通知幂等处理。
最小接入流程
不管选择易支付兼容模式还是标准 JSON API,业务系统都建议按下面的顺序实现:
- 在业务库里创建本地订单,状态为
pending,记录订单号、金额、币种和用户 ID。 - 服务端向 PaymentOne 创建支付,易支付模式调用
mapi.php或跳转submit.php,标准模式调用POST /v1/payment-intents。 - 前端打开
payurl/checkout_url,或展示qrcode给用户扫码。 - PaymentOne 收到上游支付成功事件后,向订单的
notify_url投递回调。 - 业务系统验签,核对订单号、金额、币种和应用身份。
- 业务系统用本地唯一约束做幂等入账,把订单更新为已支付并发放权益。
- 业务系统返回 HTTP 2xx,建议正文返回
success。 - 如果用户页面先跳回
return_url,只展示处理中页面,再由后端订单状态或查询接口确认结果。
易支付兼容接入
易支付兼容模式适合已有易支付 SDK、插件或配置页的系统。业务侧通常只需要替换网关地址,签名算法和核心字段沿用易支付习惯。
| 项 | 值 |
|---|---|
| 网关根地址 | https://payment.agentone.work/easypay |
| 应用标识 | 后台应用里的易支付 PID |
| 应用密钥 | 后台应用里的易支付 KEY |
| 签名方式 | MD5 |
| 金额单位 | 元,最多两位小数,例如 9.90 |
接口总览
| 接口 | 方法 | URL | 用途 | 成功结果 |
|---|---|---|---|---|
| 跳转收银台 submit.php | GET/POST | https://payment.agentone.work/easypay/submit.php |
浏览器表单或 URL 直接发起支付 | 302 跳转到 PaymentOne 收银台 |
| API 创建支付 mapi.php | POST | https://payment.agentone.work/easypay/mapi.php |
服务端创建订单并拿支付链接/二维码 | JSON,code=1 时读取 payurl、qrcode、img |
| 订单查询 api.php | GET | https://payment.agentone.work/easypay/api.php?act=order |
按业务订单号查询支付状态 | JSON,trade_status 为 WAIT_BUYER_PAY 或 TRADE_SUCCESS |
| 异步通知 notify_url | POST | 你在创建支付时传入的 notify_url |
PaymentOne 确认支付成功后通知业务系统 | 业务系统验签、核对金额、幂等入账后返回 2xx,建议正文为 success |
| 页面返回 return_url | GET | 你在创建支付时传入的 return_url |
用户支付后的浏览器跳转 | 只能展示结果页,不能作为入账依据 |
pid、key 和签名
所有创建支付请求都必须带 pid、sign、sign_type=MD5。签名步骤如下:
- 去掉
sign、sign_type和值为空的字段。 - 按参数名 ASCII 升序排序。
- 拼接为
k=v,字段之间用&连接。 - 在拼接串末尾直接追加应用
KEY。 - 对最终字符串做 MD5,得到小写十六进制摘要。
签名原文不要 URL 编码。额外兼容字段如果参与请求,也必须参与签名。
sign_source = "money=9.90&name=Recharge¬ify_url=https://merchant.example/pay/notify&out_trade_no=order_123&pid=1000&return_url=https://merchant.example/pay/return&type=alipay" + KEY
sign = md5_lower_hex(sign_source)
签名示例代码
PHP 易支付 MD5 示例:
function epay_sign(array $params, string $key): string {
unset($params['sign'], $params['sign_type']);
$filtered = [];
foreach ($params as $name => $value) {
if ($value === '' || $value === null) {
continue;
}
$filtered[$name] = (string) $value;
}
ksort($filtered, SORT_STRING);
$pairs = [];
foreach ($filtered as $name => $value) {
$pairs[] = $name . '=' . $value;
}
return md5(implode('&', $pairs) . $key);
}
Node.js 标准 API HMAC 示例:
import crypto from 'node:crypto';
function paymentoneSignature(secret, timestamp, rawBody) {
return crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
}
const rawBody = JSON.stringify({
app_order_id: 'order_123',
amount: 990,
currency: 'CNY',
subject: 'Recharge',
provider: 'paymentone',
method: 'alipay',
notify_url: 'https://merchant.example/pay/notify',
return_url: 'https://merchant.example/pay/return'
});
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = paymentoneSignature(process.env.PAYMENTONE_SECRET, timestamp, rawBody);
Webhook 验签时使用收到的原始请求体,不要解析后再重新 JSON.stringify 或重新拼表单。
公共创建参数
submit.php 和 mapi.php 使用同一组核心参数。
| 字段 | 必填 | 说明 |
|---|---|---|
pid |
是 | 后台应用里的易支付 PID |
type |
是 | 支付方式,常用 alipay、wxpay;也兼容 qqpay、bank、jdpay |
out_trade_no |
是 | 业务订单号,同一应用内必须唯一;重复请求必须保持金额、标题、通知地址等字段一致 |
name |
是 | 商品名或订单标题,会展示在收银台和后台排查信息里 |
money |
是 | 金额,单位元,必须大于 0,最多两位小数 |
notify_url |
建议必填 | 异步通知地址;不填则不会投递业务回调 |
return_url |
建议必填 | 用户完成支付后的浏览器返回地址;不要在这里做入账 |
sign |
是 | 按上面的 MD5 规则生成 |
sign_type |
是 | 固定传 MD5 |
param |
否 | 兼容字段,PaymentOne 会保存原始请求用于排查;当前回调不会回传,见下方兼容差异 |
cid |
否 | 兼容字段,当前不用于 PaymentOne 路由 |
clientip |
否 | 兼容字段,当前不用于 PaymentOne 路由 |
device |
否 | 兼容字段,当前不用于 PaymentOne 路由 |
跳转收银台 submit.php
适合浏览器端通过表单发起支付。推荐使用 POST,也支持 GET。
<form method="post" action="https://payment.agentone.work/easypay/submit.php">
<input name="pid" value="1000">
<input name="type" value="alipay">
<input name="out_trade_no" value="order_123">
<input name="name" value="Recharge">
<input name="money" value="9.90">
<input name="notify_url" value="https://merchant.example/pay/notify">
<input name="return_url" value="https://merchant.example/pay/return">
<input name="sign" value="<MD5签名>">
<input name="sign_type" value="MD5">
<button type="submit">Pay</button>
</form>
成功时 PaymentOne 返回 302 Found,Location 指向 PaymentOne 聚合收银台。签名错误返回 HTTP 400 和纯文本 fail;创建支付失败通常返回 HTTP 502。
API 创建支付 mapi.php
适合服务端创建订单后,把支付链接或二维码交给前端展示。
curl -X POST "https://payment.agentone.work/easypay/mapi.php" \
-F "pid=1000" \
-F "type=alipay" \
-F "out_trade_no=order_123" \
-F "name=Recharge" \
-F "money=9.90" \
-F "notify_url=https://merchant.example/pay/notify" \
-F "return_url=https://merchant.example/pay/return" \
-F "sign=<MD5签名>" \
-F "sign_type=MD5"
成功响应示例:
{
"code": 1,
"msg": "success",
"trade_no": "paymentone_pi_xxx",
"out_trade_no": "order_123",
"payurl": "https://pay.example.com/checkout/pi_xxx",
"qrcode": "https://pay.example.com/checkout/pi_xxx/qrcode.png",
"img": "https://pay.example.com/checkout/pi_xxx/qrcode.png"
}
字段说明:
| 字段 | 说明 |
|---|---|
code |
1 表示创建成功;-1 表示失败 |
msg |
成功为 success,失败时是原因,例如 sign error |
trade_no |
PaymentOne 或上游生成的支付交易号 |
out_trade_no |
你的业务订单号 |
payurl |
支付跳转地址,前端可直接打开 |
qrcode |
二维码内容或二维码图片地址,取决于上游通道返回 |
img |
与 qrcode 保持一致,兼容旧易支付插件 |
请求成功不等于支付成功。mapi.php 只表示支付单创建成功,是否到账必须以后续 notify_url 通知或 api.php?act=order 查询为准。
订单查询 api.php
当前易支付兼容查询只支持查询单个订单:act=order。
curl "https://payment.agentone.work/easypay/api.php?act=order&pid=1000&out_trade_no=order_123&key=<KEY>"
也可以不用 key,改为对 act、pid、out_trade_no 等查询参数做 MD5 签名并传 sign、sign_type=MD5。
| 字段 | 必填 | 说明 |
|---|---|---|
act |
是 | 固定为 order |
pid |
是 | 后台应用里的易支付 PID |
out_trade_no |
是 | 业务订单号 |
key |
二选一 | 应用 KEY。适合服务端查询,不要放到浏览器或前端代码里 |
sign、sign_type |
二选一 | 使用 MD5 签名查询;适合不想明文传 key 的场景 |
待支付响应示例:
{
"code": 1,
"msg": "success",
"pid": "1000",
"out_trade_no": "order_123",
"trade_no": "paymentone_pi_xxx",
"type": "alipay",
"name": "Recharge",
"money": "9.90",
"status": 0,
"trade_status": "WAIT_BUYER_PAY"
}
已支付时 status=1,trade_status=TRADE_SUCCESS。查询接口会在订单仍是 created 或 pending 时尝试同步一次上游状态;如果上游暂时不可用,仍会返回本地当前状态。
返回码与错误
| 场景 | submit.php | mapi.php / api.php |
|---|---|---|
| 签名错误、PID 不存在 | HTTP 400,正文 fail |
HTTP 200,{"code":-1,"msg":"sign error"} |
| 金额格式错误 | HTTP 502,正文 fail |
HTTP 200,{"code":-1,"msg":"money must have at most two decimal places"} |
| 重复订单字段不一致 | HTTP 502,正文 fail |
HTTP 200,{"code":-1,"msg":"idempotency mismatch: ... "} |
| 查询订单不存在 | 不适用 | HTTP 200,{"code":-1,"msg":"order not found"} |
查询不支持的 act |
不适用 | HTTP 200,{"code":-1,"msg":"unsupported act"} |
回调处理:异步通知 notify_url
PaymentOne 确认支付成功后,会向创建支付时传入的 notify_url 发起 POST 请求。
兼容易支付的通知体为 application/x-www-form-urlencoded:
| 字段 | 说明 |
|---|---|
pid |
应用 PID |
trade_no |
PaymentOne 或上游交易号 |
out_trade_no |
业务订单号 |
type |
创建支付时传入的支付方式 |
name |
商品名或订单标题 |
money |
金额,单位元,保留两位小数 |
trade_status |
支付成功为 TRADE_SUCCESS |
sign |
易支付 MD5 签名 |
sign_type |
MD5 |
通知请求还会带标准 Webhook 头:
| Header | 说明 |
|---|---|
X-App-Id |
PaymentOne 应用 ID |
X-PaymentOne-Timestamp |
Unix 秒级时间戳 |
X-PaymentOne-Signature |
HMAC_SHA256_HEX(StandardSecret, "<timestamp>.<raw_body>") |
兼容易支付系统时,至少校验表单里的 MD5 签名、pid、out_trade_no、money 和 trade_status=TRADE_SUCCESS。如果你是新系统,也建议同时校验标准 Webhook 头。
处理成功后返回 HTTP 2xx,正文建议返回纯文本 success。PaymentOne 当前以 HTTP 2xx 判断投递成功;如果业务返回 4xx、5xx 或网络超时,会进入失败重试和后台投递记录。
回调重试策略
Webhook 投递 worker 每 5 秒扫描一次待投递记录,每次 HTTP 请求超时时间为 10 秒。投递失败后会按 attempts^2 分钟退避重试:第 1 次失败约 1 分钟后重试,第 2 次失败约 4 分钟后重试,第 3 次失败约 9 分钟后重试,以此类推。累计 10 次失败后投递状态会变为 failed。
后台“Deliveries”页面可以查看每次投递的 URL、状态、尝试次数、下一次投递时间和最近错误。业务系统收到重复通知时必须幂等处理;已经入账的订单直接返回 2xx 即可。
页面返回 return_url
return_url 是用户浏览器跳回业务站点的地址。它可能发生在异步通知之前,也可能被用户刷新、复制或伪造,所以只适合展示“支付处理中/请稍候”的页面。
正确做法是:页面进入后用业务订单号查询你自己的后端状态;后端以 notify_url 幂等入账或服务端查询 api.php?act=order 的结果为准。
兼容差异
PaymentOne 的易支付兼容层覆盖支付接入主流程,但不是完整复制所有易支付平台行为。
| 项 | PaymentOne 当前行为 | 接入建议 |
|---|---|---|
param |
创建支付时会保存到原始请求,但业务回调当前不回传 param |
老系统如果依赖 param 入账,请改为通过 out_trade_no 查本地订单上下文 |
cid |
接收并参与签名,但当前不用于通道选择 | 在 PaymentOne 后台用支付通道优先级管理路由 |
clientip、device |
接收并参与签名,但当前不影响收银台展示 | 可继续传,避免旧插件改动 |
mapi.php 响应 |
返回 trade_no、out_trade_no、payurl、qrcode、img |
旧插件如依赖 O_id、payurl2,需要适配字段 |
| 成功判断 | mapi.php 成功只代表支付单创建成功 |
入账只看 notify_url 或服务端查询结果 |
不支持接口
PaymentOne 当前不是完整的易支付商户后台 API,只提供支付接入所需的兼容面。
| 易支付常见接口 | PaymentOne 当前状态 | 建议处理 |
|---|---|---|
act=balance 查询余额 |
不支持,返回 unsupported act |
在 PaymentOne 后台查看通道和订单,不要让业务系统依赖余额查询 |
| 退款 / 提交订单退款 | 不支持易支付兼容退款接口 | 先在上游通道后台处理,或后续接入 PaymentOne 标准退款 API |
| 微信小程序专用跳转 | 不支持 ZPAY 小程序式参数 | 使用 payurl 打开 PaymentOne 收银台,或展示 qrcode |
| 批量订单查询、结算、商户管理类接口 | 不支持 | 通过后台页面和支付意图列表排查 |
标准 JSON API 接入
标准 API 适合新系统。它使用 JSON 请求、最小货币单位和 HMAC-SHA256 签名,字段更稳定,也更适合服务端集成。
接口总览
| 接口 | 方法 | URL | 说明 |
|---|---|---|---|
| 获取可用支付方式 | GET | https://payment.agentone.work/v1/payment-methods |
返回当前启用且用户可见的 provider |
| 创建支付意图 | POST | https://payment.agentone.work/v1/payment-intents |
创建订单并返回 checkout_url、qrcode |
| 查询支付意图 | GET | https://payment.agentone.work/v1/payment-intents/{payment_intent_id} |
查询并尝试同步当前支付状态 |
| 主动同步状态 | POST | https://payment.agentone.work/v1/payment-intents/{payment_intent_id}/sync |
强制向上游查询一次状态 |
provider/method 可用值
provider 决定 PaymentOne 使用哪个上游通道,method 表示支付方式。实际是否可用还取决于后台是否启用了对应支付通道。
| provider | method | 说明 |
|---|---|---|
paymentone |
alipay、wxpay、card 或留空 |
进入 PaymentOne 聚合收银台,让用户选择已启用方式 |
easypay |
alipay、wxpay、qqpay、bank、jdpay |
直接创建 EasyPay 上游支付;上游账号可能只支持其中一部分 |
stripe |
card 或 stripe |
创建 Stripe Checkout 支付 |
creem |
creem |
仅在对应 provider 已配置时可用 |
waffo |
waffo |
仅在对应 provider 已配置时可用 |
waffo_pancake |
waffo_pancake、pancake |
仅在对应 provider 已配置时可用 |
如果 provider 留空,PaymentOne 会根据 method 推断 provider:card 走 Stripe,alipay / wxpay 等走 EasyPay,未知值默认按 EasyPay 处理。新系统建议显式传 provider。
请求认证
除 GET /v1/payment-methods 外,标准 API 都需要以下请求头:
| Header | 说明 |
|---|---|
X-App-Id |
后台应用 ID |
X-PaymentOne-Timestamp |
Unix 秒级时间戳,默认只接受 5 分钟窗口内的请求 |
X-PaymentOne-Signature |
HMAC_SHA256_HEX(secret, "<timestamp>.<raw_body>") |
raw_body 必须是实际发送给 PaymentOne 的原始请求体字节。GET 或空 body 的 POST 也要签名,签名原文是 <timestamp>.。
raw_body = exact JSON bytes sent to PaymentOne
timestamp = unix_seconds()
signature = hmac_sha256_hex(StandardSecret, timestamp + "." + raw_body)
创建支付意图
curl -X POST "https://payment.agentone.work/v1/payment-intents" \
-H "Content-Type: application/json" \
-H "X-App-Id: demo" \
-H "X-PaymentOne-Timestamp: <timestamp>" \
-H "X-PaymentOne-Signature: <signature>" \
--data '{
"app_order_id": "order_123",
"amount": 990,
"currency": "CNY",
"subject": "Recharge",
"provider": "paymentone",
"method": "alipay",
"return_url": "https://merchant.example/pay/return",
"notify_url": "https://merchant.example/pay/notify",
"metadata": {
"user_id": "user_123"
}
}'
请求字段:
| 字段 | 必填 | 说明 |
|---|---|---|
app_order_id |
是 | 业务订单号,同一应用内唯一;重复请求必须保持关键字段一致 |
amount |
是 | 最小货币单位,CNY 990 表示 9.90 元 |
currency |
否 | 默认 CNY;Stripe 卡支付通常传 Stripe 支持的币种 |
subject |
是 | 订单标题 |
provider |
否 | paymentone 进入聚合收银台;也可指定 stripe、easypay 等 |
method |
否 | 支付方式,例如 card、alipay、wxpay |
notify_url |
建议必填 | 支付结果异步通知地址;host 必须在应用允许通知域名内 |
return_url |
建议必填 | 用户完成支付后的浏览器返回地址 |
metadata |
否 | 字符串键值对象,用于排查或业务关联 |
响应字段:
| 字段 | 说明 |
|---|---|
payment_intent_id |
PaymentOne 支付意图 ID |
app_order_id |
业务订单号 |
status |
created、pending、paid、failed、expired、cancelled |
provider |
实际路由到的 provider |
provider_trade_no |
上游交易号或 PaymentOne 收银台交易号 |
amount、currency |
金额和币种 |
subject |
订单标题 |
checkout_url |
支付跳转地址,前端优先使用这个字段 |
qrcode |
上游返回的二维码内容或图片地址 |
expires_at |
过期时间,可能为空 |
paid_at |
支付完成时间,未支付时为空 |
创建成功响应:
{
"payment_intent_id": "pi_123",
"app_order_id": "order_123",
"status": "pending",
"provider": "paymentone",
"provider_trade_no": "paymentone_pi_123",
"amount": 990,
"currency": "CNY",
"subject": "Recharge",
"checkout_url": "https://pay.example.com/checkout/pi_123",
"qrcode": "https://pay.example.com/checkout/pi_123/qrcode.png",
"expires_at": null,
"paid_at": null
}
查询待支付响应:
{
"payment_intent_id": "pi_123",
"app_order_id": "order_123",
"status": "pending",
"provider": "easypay",
"provider_trade_no": "epay_20260703123456",
"amount": 990,
"currency": "CNY",
"subject": "Recharge",
"checkout_url": "https://pay.example.com/pay/epay_20260703123456",
"qrcode": "https://pay.example.com/qrcode/epay_20260703123456.png",
"expires_at": "2026-07-03T08:30:00Z",
"paid_at": null
}
查询已支付响应:
{
"payment_intent_id": "pi_123",
"app_order_id": "order_123",
"status": "paid",
"provider": "easypay",
"provider_trade_no": "epay_20260703123456",
"amount": 990,
"currency": "CNY",
"subject": "Recharge",
"checkout_url": "https://pay.example.com/pay/epay_20260703123456",
"qrcode": "https://pay.example.com/qrcode/epay_20260703123456.png",
"expires_at": "2026-07-03T08:30:00Z",
"paid_at": "2026-07-03T08:10:12Z"
}
查询和同步
查询支付意图:
curl -X GET "https://payment.agentone.work/v1/payment-intents/{payment_intent_id}" \
-H "X-App-Id: demo" \
-H "X-PaymentOne-Timestamp: <timestamp>" \
-H "X-PaymentOne-Signature: <signature-for-empty-body>"
主动同步:
curl -X POST "https://payment.agentone.work/v1/payment-intents/{payment_intent_id}/sync" \
-H "X-App-Id: demo" \
-H "X-PaymentOne-Timestamp: <timestamp>" \
-H "X-PaymentOne-Signature: <signature-for-empty-body>"
GET 查询会在必要时尝试同步;POST /sync 会强制请求上游状态。两者响应结构都和创建支付意图一致。业务系统可以把查询作为补偿手段,但最终入账仍建议以 Webhook 幂等处理为主。
标准 Webhook
标准 API 创建的订单支付成功后,PaymentOne 会向 notify_url 发送 JSON:
{
"event_id": "evt_123",
"type": "payment.succeeded",
"payment_intent_id": "pi_123",
"app_id": "demo",
"app_order_id": "order_123",
"amount": 990,
"currency": "CNY",
"provider": "stripe",
"provider_trade_no": "cs_test_123",
"paid_at": "2026-07-02T10:20:30Z"
}
通知头:
| Header | 说明 |
|---|---|
Content-Type |
application/json |
X-App-Id |
PaymentOne 应用 ID |
X-PaymentOne-Timestamp |
Unix 秒级时间戳 |
X-PaymentOne-Signature |
HMAC_SHA256_HEX(StandardSecret, "<timestamp>.<raw_body>") |
验签通过后,再核对 app_id、app_order_id、amount、currency 和本地订单是否一致。处理成功返回任意 HTTP 2xx。
业务侧幂等要求
不要只看“通知到了”就入账。每次通知至少检查:
- 签名合法,时间戳在可接受窗口内。
app_id或pid属于当前业务系统。app_order_id或out_trade_no能匹配本地订单。- 金额和币种与本地订单一致。
- 订单尚未入账;如果已经入账,直接返回成功。
推荐以业务订单号或 PaymentOne payment_intent_id 建唯一入账约束。重复通知只能更新日志,不能重复增加余额或发放权益。
测试与联调
本地联调可以使用 mock provider,避免真实扣款:
PAYMENTONE_MOCK_PROVIDERS=true \
PAYMENTONE_WEBHOOK_ALLOW_PRIVATE_HOSTS=true \
PAYMENTONE_WEBHOOK_ALLOWED_HOSTS=localhost,127.0.0.1 \
PAYMENTONE_PUBLIC_BASE_URL=http://localhost:8080 \
go run ./cmd/paymentone
PAYMENTONE_MOCK_PROVIDERS=true 会为未配置的 provider 注册 mock provider。创建支付后打开 checkout_url,mock 收银台会显示“标记为已支付”按钮,点击后 PaymentOne 会把订单标记为 paid 并向 notify_url 投递回调。
本地回调到 localhost、127.0.0.1 或内网地址时,需要 PAYMENTONE_WEBHOOK_ALLOW_PRIVATE_HOSTS=true。生产环境不要打开这个开关,生产环境应使用 HTTPS 的公网回调地址,并在应用里配置允许通知域名。
建议至少验证这些场景:
- 签名正确时能创建支付,签名错误时会失败。
notify_url收到成功通知后能验签、核对金额并幂等入账。- 重复投递同一订单不会重复发放权益。
return_url先到达时页面只展示处理中,不直接入账。- 使用
api.php?act=order或标准查询接口能补偿确认订单状态。
上线检查清单
- 已在后台应用里配置允许通知域名,生产环境不要放开任意 host。
- 已启用真实支付通道,并在上游后台配置 PaymentOne 的 provider webhook 地址。
- 业务系统已实现回调验签、金额核对、订单幂等和失败日志。
- 标准 API 请求全部在服务端签名,前端只拿
checkout_url或qrcode。 - 不要在前端暴露 KEY 或标准接口密钥,不要把密钥写入公开日志。
- 已验证
submit.php、mapi.php、api.php?act=order、notify_url、return_url的完整流程。 - 已用重复通知、重复创建订单、金额不一致、签名错误等场景做过测试。
排错顺序
- 创建支付失败:先看
pid/X-App-Id、签名、金额格式、应用密钥和 provider 是否启用。 - 支付页无法打开:检查
payurl/checkout_url是否为空,以及聚合收银台是否有可用支付方式。 - 订单一直 pending:用后台支付意图详情和
api.php?act=order/ 标准查询接口确认当前状态。 - 回调未到账:查看 Webhook 投递列表,确认
notify_urlhost 是否允许、下游是否返回 2xx、是否超时。 - 业务未入账:先查业务侧幂等表,再查 PaymentOne 支付状态、通知签名和投递记录。