接入准备
在商户管理后台创建商户,取得内部商户 ID 与独立密钥。商户 ID 与支付宝开放平台 AppID 是两种不同标识;接入方不需要配置支付宝 AppID 或持有支付宝密钥。
所有接口均由业务服务端通过 HTTPS 调用,地址为 https://pay.appsvc.net。每次请求携带以下请求头;GET 不需要请求体。
X-Merchant-Id: YOUR_MERCHANT_ID
Authorization: Bearer YOUR_MERCHANT_KEY
Content-Type: application/json
如需回跳业务页面,请先配置允许的 HTTPS origin,例如 https://pvcamera.appsvc.net;origin 不含路径。然后下单时传入完整回跳地址。
完整支付流程
- 业务服务端生成自己的业务订单号,确定金额与商品,调用发起支付接口。
- 保存返回的
payment_id与业务订单的关联,将用户浏览器跳转至checkout_url。 - pay 收银页跳转支付宝。付款后,支付宝回到 pay 结果页,支付通知也由 pay 处理。
- pay 确认支付成功后,若创建时传了
return_url,则展示成功页,3 秒后回跳业务页面,也可点击“立即返回”;否则展示“支付成功,您可以关闭此页面”。未确认付款时每 3 秒自动重试,最多 5 次;仍未确认时提示用户稍后手动刷新。 - 业务服务端再次查询 pay,核对订单及金额,确认
PAID后幂等发货或激活。
1. 发起支付
POST https://pay.appsvc.net/v1/payments
{
"business_order_no": "order-20261010-001",
"amount": "18.00",
"subject": "随动S1激活服务",
"return_url": "https://pvcamera.appsvc.net/payment/return"
}
| 参数 | 必填 | 说明 |
|---|---|---|
business_order_no
|
是 | 商户自己的业务订单号,1–80 位字母、数字、下划线或连字符;商户内唯一。 |
amount
|
是 | 人民币元字符串,如 "18.00";0.01–100000000.00,最多两位小数。价格应由服务端确定。 |
subject
|
是 | 非空商品标题,最长 128 字符。 |
return_url
|
否 | 完整 HTTPS 回跳 URL,最长 2048 字符,origin 必须在商户允许列表。省略或空字符串表示停留 pay 成功页。 |
成功响应 · HTTP 200
{
"payment_id": "20261010160910_pvcamera_0123456789abcdef0123456789abcdef",
"business_order_no": "order-20261010-001",
"merchant_id": "pvcamera",
"amount": "18.00",
"status": "WAIT_BUYER_PAY",
"trade_no": "",
"checkout_url": "https://pay.appsvc.net/checkout/随机收银凭证"
}
payment_id 由 pay 生成,新格式含北京时间、商户可读前缀与随机后缀。实际商户归属以 merchant_id 为准,不要依靠解析支付单号做业务判断。
同一商户、同一业务订单号为幂等请求。金额、标题、回跳 URL 必须完全一致;一致时返回原支付单,不一致返回 409。下单请求超时后,使用相同业务订单号和原参数重试。
checkout_url 含独立随机凭证,用于浏览器收银及 pay 结果页,不是查询 API。不要分享或写入公开日志。付款有效期从 pay 创建时起固定为 30 分钟,响应中的 expires_at 为 UTC 截止时间,重复打开或幂等重试不延期。过期后需新的业务订单号重新下单;若已付款,仍可查询和确认结果。新订单冻结支付宝账户,后续调整商户账户配置不影响历史订单。
2. 支付回跳参数
pay 确认付款后展示成功页,3 秒后跳转创建时保存的 return_url,附加以下两个查询参数:
| 参数 | 含义 |
|---|---|
out_trade_no
|
商户创建时传入的 business_order_no,不是 pay 支付单号。 |
payment_id
|
pay 生成的支付单号,用于服务端查询。 |
https://pvcamera.appsvc.net/payment/return
?out_trade_no=order-20261010-001
&payment_id=20261010160910_pvcamera_0123456789abcdef0123456789abcdef
以上分行为阅读展示,实际回跳 URL 为一行。原 URL 的其他查询参数保留;同名 out_trade_no 和 payment_id 会由 pay 覆盖。
trade_no(支付宝交易号),也不附加可信的付款证明或签名。商户应使用本地保存的订单关联,校验回跳的订单标识,再从服务端查询接口取得 trade_no 和付款状态。不要信任浏览器传来的状态、金额或交易号。3. 查询支付结果与履约
GET https://pay.appsvc.net/v1/payments/{payment_id}
使用创建订单时保存的 payment_id。只能查询当前认证商户自己的订单。
成功响应 · HTTP 200
{
"payment_id": "20261010160910_pvcamera_0123456789abcdef0123456789abcdef",
"business_order_no": "order-20261010-001",
"merchant_id": "pvcamera",
"amount": "18.00",
"status": "PAID",
"trade_no": "2026101022000000000000000000"
}
交易号为示例。trade_no 是支付宝生成的交易号,经可信结果确认后由 pay 保存;待支付时通常为空。
| 状态 | 商户处理 |
|---|---|
WAIT_BUYER_PAY
|
尚未确认付款,不履约,可稍后再查询。 |
PAID
|
付款已确认,核对商户、业务订单号、支付单号和金额后幂等履约。 |
TRADE_CLOSED
|
交易已关闭,不按成功订单履约。 |
REFUNDED
|
退款状态枚举;具体退款结果以退款接口的 SUCCEEDED 为准,不要假设退款一定改变支付单状态。 |
服务端处理示意
const payment = await queryPayment(localOrder.payment_id)
if (payment.merchant_id !== configuredMerchantId ||
payment.business_order_no !== localOrder.business_order_no ||
payment.payment_id !== localOrder.payment_id ||
payment.amount !== localOrder.amount) {
throw new Error("支付结果与业务订单不一致")
}
if (payment.status === "PAID" && payment.trade_no) {
await fulfillOnce(localOrder) // 用业务订单号去重,保证并发与重复回跳不会重复履约
}
查询超时或返回错误表示暂时无法确认结果,不等于未付款。保持业务订单待确认,稍后重试;不要仅凭回跳就标记成功。
4. 退款
POST https://pay.appsvc.net/v1/payments/{payment_id}/refunds
{
"request_no": "refund-001",
"amount": "1.00",
"reason": "客户申请退款"
}
仅已确认付款的订单可退款。先查询确认付款状态;退款金额为人民币元字符串,不得超过剩余可退金额。request_no 在订单内唯一,1–80 位字母、数字、下划线或连字符;reason 可选,最长 256 字符。
成功响应 · HTTP 200
{
"payment_id": "20261010160910_pvcamera_0123456789abcdef0123456789abcdef",
"request_no": "refund-001",
"amount": "1.00",
"status": "SUCCEEDED"
}
-
SUCCEEDED:本次退款已确认;同一请求再次提交会返回原结果。 -
PENDING:结果尚未确认,退款额度仍被预留。稍后使用相同请求号、金额和原因重试。
错误与重试
错误响应使用真实 HTTP 状态码,响应体格式:
{"error":"身份认证失败"}
| HTTP 状态 | 含义 / 处理 |
|---|---|
| 400 | 参数无效或当前订单不可操作,修正参数后请求。 |
| 401 | 密钥无效、已刷新或商户禁用,检查服务端凭证。 |
| 404 | 订单不存在或不属于当前商户。 |
| 409 | 幂等参数冲突或退款额度不足,核对原请求。 |
| 429 | 请求频繁,退避后重试,避免密集轮询。 |
| 500 / 502 / 503 | 服务或上游暂不可用、账户未启用;稍后重试或联系管理员。结果不确定时保留原订单号 / 退款请求号。 |
对接完整字段、类型和响应结构请查看 OpenAPI 3.1 文档。所有响应示例中的订单号与交易号均为示意值。