接入文档 V0.1.0

收款

中台持有商户密钥,站点只下单与收货。两种接法,推荐第一种。

方式 A:托管收银台(不占你的支付授权目录名额)

# 1) 站点下单
POST https://wx.onzx.cn/api/v1/pay/order
{ "out_trade_no": "VIP-20261015-0001",
  "amount_total": 50000,
  "description": "心理咨询 · 初诊 60 分钟",
  "scene": "cashier",
  "return_url": "https://vip.onzx.cn/me",
  "attach": { "appointment_id": 3391 },
  "channel": "yzy" }        # 可选,留空用默认通道

# 2) 返回
{ "ok": true, "data": {
    "cashier_url": "https://wx.onzx.cn/cashier/1f9c…",
    "order": { "out_trade_no": "…", "status": "pending", "amount_total": 50000 },
    "expire_at": 1760000900 } }

# 3) 站点把用户跳到 cashier_url。微信内会静默授权拿 openid 并调起支付;
#    PC 上自动显示微信扫码页。付完自动回 return_url。
商户平台只需要一条支付授权目录:https://wx.onzx.cn/cashier/。 微信对"顶级域名目录"只校验 host、不校验后缀,所以这一条覆盖中台所有支付页面, 5 条名额用掉 1 条,以后再接多少个站点都不用回商户平台改配置。

方式 B:站点自己调起(需要额外名额)

{ …, "scene": "jsapi", "openid": "oXXXX…" }
→ data.jsapi_params = { appId, timeStamp, nonceStr, package, signType:"RSA", paySign }

站点页面里执行:

WeixinJSBridge.invoke('getBrandWCPayRequest', jsapi_params, function (res) {
  if (res.err_msg === 'get_brand_wcpay_request:ok') { /* 只是体验,最终以服务端通知为准 */ }
});

这时"发起支付的页面 host"必须是支付授权目录之一,请在商户平台为该站点另加一条(形如 https://vip.onzx.cn/,必须以 / 结尾)。已有 5 条上限,最多再接 4 个站点。

场景与前置条件

scene用在哪前置
cashier默认,中台托管入口域名可 https 访问;openid 由收银台静默授权取得
jsapi站点页面内直接调起该 AppID 已关联商户号;页面 host 已配支付授权目录;openid 属于该 AppID
native外部浏览器/PC 出二维码商户号已开通 Native 支付
h5微信外手机浏览器需在产品中心单独申请 H5 支付,并传真实终端 IP
miniprogram小程序内小程序 AppID 已关联商户号;用小程序的 openid

回调:站点必须做的三件事

POST 你的 webhook_url(中台用「回调密钥」签名,算法同 /docs/sign)
{ "event": "pay.success", "site_key": "vip", "sent_at": 1760000950, "callback_id": 88,
  "data": { "out_trade_no": "VIP-…", "transaction_id": "4200…", "status": "paid",
            "amount_total": 50000, "amount_paid": 50000, "openid": "oX…",
            "attach": { "appointment_id": 3391 }, "paid_at": 1760000940 } }
  1. 验签:用回调密钥(不是接入密钥)。
  2. 幂等:按 event + out_trade_no 去重。微信会重复通知,中台也会补发。
  3. 快速返回 2xx:先落库再处理业务。返回 3xx/4xx/超时都算失败,会按 15s/60s/5m/30m/2h 重试,超过次数进死信。

没配 webhook 也能用:站点可以主动轮询 POST /api/v1/pay/query { "out_trade_no": "…", "refresh": "1" }, 其中 refresh=1 会让中台向微信查一次单并以微信结果为准修正状态。

其余接口

接口入参说明
/pay/queryout_trade_no[, refresh]查状态;refresh=1 触发向微信查单
/pay/closeout_trade_no关单。仅 pending 可关;已支付会报 ORDERPAID 并自动转查单入账
/pay/refundout_trade_no, out_refund_no, amount_refund[, reason]退款走原路,结果同样通过回调通知

状态机(中台不允许非法跳转)

pending → paid | closed | failed
paid    → refunding | refunded | partial
partial → refunded
refunding → refunded | partial
closed / refunded → 终局
实付金额 != 应付金额时中台不会入账,只写一条 amount_mismatch 事件等人工核对。 这类情况几乎都来自"订单号复用"或"改价重试",所以同单号改金额会被直接拒绝(order_conflict)。

合规红线

本页内容随代码一起版本化(V0.1.0)。发现文档与实际行为不一致,请当缺陷报告。