DEVELOPER GUIDE / V1

Pay 商户接入指南

三个公网接口,完成下单、支付确认与退款。

OpenAPI 3.1

接入准备

在商户管理后台创建商户,取得内部商户 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
商户密钥只保存在业务服务端,不能放进前端代码或浏览器 URL。后台刷新密钥后,旧密钥立即失效,请更新服务端配置。商户使用哪个支付宝账户,由 pay 后台配置决定。

如需回跳业务页面,请先配置允许的 HTTPS origin,例如 https://pvcamera.appsvc.net;origin 不含路径。然后下单时传入完整回跳地址。

完整支付流程

业务端下单 → pay 收银页 → 支付宝付款 → pay 确认结果 → 可选业务回跳
  1. 业务服务端生成自己的业务订单号,确定金额与商品,调用发起支付接口。
  2. 保存返回的 payment_id 与业务订单的关联,将用户浏览器跳转至 checkout_url。
  3. pay 收银页跳转支付宝。付款后,支付宝回到 pay 结果页,支付通知也由 pay 处理。
  4. pay 确认支付成功后,若创建时传了 return_url,则展示成功页,3 秒后回跳业务页面,也可点击“立即返回”;否则展示“支付成功,您可以关闭此页面”。未确认付款时每 3 秒自动重试,最多 5 次;仍未确认时提示用户稍后手动刷新。
  5. 业务服务端再次查询 pay,核对订单及金额,确认 PAID 后幂等发货或激活。
当前没有 pay → 商户的支付通知接口。浏览器可能被用户关闭,因此回跳不能作为唯一触发器;商户应在订单页或后台任务中定期查询待支付订单,以补偿未回跳的付款。

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:结果尚未确认,退款额度仍被预留。稍后使用相同请求号、金额和原因重试。
不要在超时或 PENDING 后换退款号重试,否则可能重复退款。同号参数不一致返回 409。当前没有单独的退款查询或关闭支付单接口。

错误与重试

错误响应使用真实 HTTP 状态码,响应体格式:

{"error":"身份认证失败"}
HTTP 状态 含义 / 处理
400 参数无效或当前订单不可操作,修正参数后请求。
401 密钥无效、已刷新或商户禁用,检查服务端凭证。
404 订单不存在或不属于当前商户。
409 幂等参数冲突或退款额度不足,核对原请求。
429 请求频繁,退避后重试,避免密集轮询。
500 / 502 / 503 服务或上游暂不可用、账户未启用;稍后重试或联系管理员。结果不确定时保留原订单号 / 退款请求号。

对接完整字段、类型和响应结构请查看 OpenAPI 3.1 文档。所有响应示例中的订单号与交易号均为示意值。