Skip to content

看购支付接入文档 ​

简介 ​

本文档介绍如何在看购 App 内的 H5 页面中唤起微信支付,适用于在看购平台上架 H5APP 商城的开发者。

看购采用微信支付服务商模式:看购是服务商,你是看购名下的子商户。你的业务服务器通过看购支付(KGPay)提供的微信兼容 API 下单,协议与微信支付 APIv3 服务商接口一致,可以直接使用微信支付官方 SDK。

更详细的看购支付(KGPay)帮助文档,请登录看购支付平台 pay.citv.cc 查看。

读完本文,你应该能让自己的 H5 商城在看购 App 里完成一笔真实付款。


一、业务流程 ​

① 你的业务服务器   持有 KGPay 发放的微信兼容 API 证书
   │  POST {网关}/v3/pay/partner/transactions/app   预下单
   │  网关应答里直接带着签好的 APP 调起参数(wxApp)
   ▼
② 你的 H5 商城(运行在看购 App 的 WebView 中)
   │  kg.chooseWXPay(wxApp)
   ▼
③ 看购 App  ──▶  微信(直接唤起,不经过看购服务端)

④ 支付结果:KGPay 网关 ──支付通知──▶ 你的业务服务器(notify_url)
            你的业务服务器 ──查单──▶ KGPay 网关(兜底)

看购服务端不参与这条链路:不转发、不签名、不记流水、不参与定价。看购 App 只负责拿着你服务器签好的参数唤起微信。款项进入你的子商户号。

也正因为看购服务端不经手订单,支付结果只能由你自己的服务器确认——H5 里的 success 回调不带任何订单状态。

三条红线 ​

上线前请确认你没有踩到这三条中的任何一条:

  1. 商户私钥、APIv3 密钥绝不能出现在 H5 页面里。 前端能读到,就等于任何人都能用你的子商户号任意金额下单、伪造支付通知。预下单和签名必须在你的服务器上完成。
  2. 金额由服务器决定,H5 只传订单号。 不要让 H5 把金额传给服务器再去下单,否则改一下请求就能 1 分钱买走商品。
  3. 不要拿 success 回调当作支付成功。 它只代表微信回了"支付完成",用户可能付完没回到 App、订单也可能已关闭。发货一律以你服务器收到的支付通知或查单结果为准。

二、接入前准备 ​

步骤在哪里做拿到什么
1. 开通子商户看购支付平台 / 联系看购运营子商户号 subMchid,款项进入这个商户号
2. 领取参数看购支付平台一份 meta.txt + 商户私钥 apiclient_key.pem + 微信支付公钥 pub_key.pem
3. 部署你自己的业务服务器以上参数写进服务端配置(环境变量或配置中心,不要提交进代码库)

自建 H5 商城不需要在看购开发者中心登记支付渠道——看购服务端不参与你的支付。

meta.txt 参数说明 ​

参数示例用途
gatewayUrlhttps://pay.example.com网关地址,替代微信的 https://api.mch.weixin.qq.com
compatMchid5957139992兼容商户号。请求签名的 mchid,下单时的 sp_mchid。不是APP 调起参数的 partnerId
merchantSerialNo6A92D712…商户 API 证书序列号,请求签名用
pubKeyIdPUB_KEY_ID_…微信支付公钥 ID,验证网关应答和支付通知的签名
apiV3Key32 位字符串APIv3 密钥,解密支付通知
subMchid1750389389你的子商户号,下单时的 sub_mchid
spAppidwxa381da5bf6852984看购 App 的微信 AppID,下单时的 sp_appid,APP 调起参数的 appId
subAppid空不用填

sp_appid 只能是看购 App 的 AppID

你的 H5 运行在看购 App 里,微信只认看购 App 这一个 AppID。sp_appid 填了别的值,预下单可能成功,但 App 会直接报错、唤不起微信。


三、第一步:业务服务器预下单 ​

3.1 请求参数 ​

POST {gatewayUrl}/v3/pay/partner/transactions/app
字段类型必填说明
sp_appidstring是meta.txt 的 spAppid
sp_mchidstring是meta.txt 的 compatMchid
sub_mchidstring是meta.txt 的 subMchid
out_trade_nostring是你自己的订单号,见 3.2
descriptionstring是商品描述,会显示在微信支付界面和账单里
notify_urlstring是你自己业务服务器的支付通知地址,公网可达,不能带 query 参数
amount.totalint是金额,单位分,必须大于 0
amount.currencystring否固定 CNY

其余可选字段以 KGPay 文档为准。

3.2 关于 out_trade_no ​

  • 由你自己生成,6–32 位,只能是数字、大小写字母和 _-|*
  • 在你的子商户号下必须唯一
  • 同一张业务单允许多次发起支付(用户取消后重试)时,建议每次生成新的 out_trade_no,而不是直接复用业务订单号,并在自己的库里记录两者的对应关系

3.3 请求签名 ​

签名规则与微信支付 APIv3 完全一致(WECHATPAY2-SHA256-RSA2048),推荐直接用微信支付官方 SDK,只需把请求地址换成 gatewayUrl,并用「微信支付公钥」模式初始化:

go
// 官方 SDK:github.com/wechatpay-apiv3/wechatpay-go
priv, _ := utils.LoadPrivateKeyWithPath("apiclient_key.pem")
pub, _ := utils.LoadPublicKeyWithPath("pub_key.pem")

client, err := core.NewClient(ctx,
	option.WithWechatPayPublicKeyAuthCipher(compatMchid, merchantSerialNo, priv, pubKeyId, pub))

result, err := client.Post(ctx, gatewayUrl+"/v3/pay/partner/transactions/app", map[string]any{
	"sp_appid":     spAppid,
	"sp_mchid":     compatMchid,
	"sub_mchid":    subMchid,
	"out_trade_no": outTradeNo,
	"description":  "商品A",
	"notify_url":   "https://your-server.com/pay/notify",
	"amount":       map[string]any{"total": 1, "currency": "CNY"},
})
var rs struct {
	PrepayId  string `json:"prepay_id"`
	AppId     string `json:"appid"`
	PartnerId string `json:"partnerid"`
	Package   string `json:"package"`
	NonceStr  string `json:"noncestr"`
	Timestamp string `json:"timestamp"`
	Sign      string `json:"sign"`
}
err = core.UnMarshalResponse(result.Response, &rs)

不要用写死微信域名的 SDK

部分第三方 SDK 把 api.mch.weixin.qq.com 写死在代码里,无法指向网关。其他语言请使用对应的微信支付官方 SDK(Java / PHP / Node.js 等),同样选择「微信支付公钥」模式。

3.4 取 APP 调起参数 ​

网关的预下单应答除 prepay_id 外,直接带着一套签好的 APP 调起参数:

json
{
  "prepay_id": "wx80216678…",
  "appid": "wxa381da5bf6852984",
  "partnerid": "1117385739",
  "prepayid": "wx80216678…",
  "package": "Sign=WXPay",
  "noncestr": "e0b05134…",
  "timestamp": "1789465671",
  "sign": "flxa2tfR…"
}

partnerid 是看购在微信侧的真实服务商号,sign 由网关用真实服务商证书签出,微信只认这一套。原样转成下面的结构返回,不要改任何字段,也不要自己重签:

go
wxApp := map[string]string{
	"appId":     rs.AppId,
	"partnerId": rs.PartnerId,
	"prepayId":  rs.PrepayId,
	"package":   rs.Package,
	"nonceStr":  rs.NonceStr,
	"timeStamp": rs.Timestamp,
	"sign":      rs.Sign,
}

不要用 compatMchid 和商户私钥自己签调起参数

compatMchid 不是微信商户号。自己拼 appid\ntimestamp\nnoncestr\nprepayid\n 用商户私钥签出来的参数,预下单看着成功,App 里却唤不起微信,而且没有明确报错。

把 { outTradeNo, wxApp } 返回给你的 H5 页面。


四、第二步:H5 页面唤起支付 ​

js
// 页面加载时初始化一次(必须先于支付调用)
kg.config({ appId: "YOUR_APP_KEY" });

kg.ready(async () => {
  if (kg.getInitError()) return;

  // 1. 向你自己的服务器下单:只传订单号,金额由服务器决定
  const data = await fetch("/api/my-shop/prepay", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ orderId: "xxx" }),
  }).then((r) => r.json());

  // 2. 把 wxApp 原样交给看购 App
  kg.chooseWXPay({
    ...data.wxApp,
    success() {
      // 只代表微信回了支付完成,去自己的服务器确认
      pollMyOrder(data.outTradeNo);
    },
    cancel() {
      /* 用户取消 */
    },
    fail(res) {
      showToast(res.errMsg);
    },
  });
});

App 内唤起支付

参数说明
appId看购 App 的微信 AppID,须与预下单的 sp_appid 一致
partnerId网关应答的 partnerid(真实微信服务商号)
prepayId预下单返回的 prepay_id
package固定 Sign=WXPay,可不传
nonceStr随机串
timeStamp秒级时间戳
sign网关应答的 sign

字段名也接受微信 APP 支付文档里的全小写写法(appid / partnerid / prepayid / noncestr / timestamp),服务端 SDK 的返回值可以直接透传。

回调含义:

回调含义
success微信回了支付完成。不带订单状态,必须到你自己的服务器确认
cancel用户在微信里取消了支付
fail参数缺失、appId 不对、App 版本过旧等,res.errMsg 里有原因,见第七节

要的是 APP 支付参数,不是公众号 JSAPI 参数

公众号 JSAPI 的 { timestamp, nonceStr, package: 'prepay_id=…', signType, paySign } 签名串与 APP 支付不同,在看购 App 里唤不起微信,SDK 会直接走 fail。请用 /v3/pay/partner/transactions/app 下单。

建议 success 后每 2 秒向自己的服务器查一次订单状态,持续约 1 分钟;服务器那边订单仍是待支付时,顺手调一次第六节的查单接口。


五、第三步:接收支付通知 ​

这是你判断订单是否付款成功的依据。 前端回调只用于改善体验,不能拿来发货。

支付成功后,网关向预下单时填写的 notify_url 发送 POST 请求,格式与微信支付 APIv3 支付通知一致。官方 SDK 提供了现成的通知处理器,以下是它替你做的事情。

5.1 验签 ​

用微信支付公钥验证请求头里的签名,Wechatpay-Serial 应等于 pubKeyId:

验签串 = {Wechatpay-Timestamp}\n{Wechatpay-Nonce}\n{请求体原文}\n

DANGER

验签不通过一律丢弃。 否则任何人构造一个请求就能让你白白发货。验签要用原始请求体,不要先反序列化再序列化。

5.2 解密 ​

请求体的 resource 是密文,算法 AEAD_AES_256_GCM,密钥是 apiV3Key,nonce 与 associated_data 在 resource 里。

解密后的主要字段:

字段说明
sp_mchid兼容商户号
sub_mchid子商户号,应等于你的 subMchid
out_trade_no你的订单号,用它定位自己的订单
transaction_id微信支付交易号
trade_state交易状态,见下表
success_time支付完成时间
amount.total订单金额(分),应等于你下单时的金额
trade_state含义建议处理
SUCCESS支付成功发货
NOTPAY未支付保持待支付
USERPAYING用户支付中保持待支付
CLOSED已关闭置关闭
REVOKED已撤销置失败
PAYERROR支付失败置失败
REFUND转入退款走退款流程

5.3 校验 ​

发货前再核对两项,任何一项不符都不要发货,并记录日志:

  • sub_mchid 等于你自己的子商户号
  • amount.total 等于你库里这笔订单的金额

5.4 应答 ​

处理成功返回 HTTP 200(或 204)。处理失败返回非 2xx 状态码,并带上 {"code":"FAIL","message":"原因"},网关会稍后重新推送。

5.5 幂等 ​

通知会重复投递,这是正常现象而不是故障。 你的处理必须幂等:

  • 先查自己的订单状态,已经是"已支付"就直接返回成功,不要重复发货、不要覆盖支付时间
  • 更新语句建议带状态守卫,例如 UPDATE ... WHERE out_trade_no = ? AND pay_status <> 1

六、第四步:主动查单兜底 ​

通知可能因网络问题丢失。以下两种情况请主动查单:H5 收到 success 后来问你订单状态、而你还没收到通知时;订单超过 5 分钟仍是待支付时。

GET {gatewayUrl}/v3/pay/transactions/out-trade-no/{out_trade_no}?mchid={compatMchid}

返回的 trade_state / transaction_id / amount.total 含义同 5.2,处理规则也一样:SUCCESS 且金额一致才置为已支付。

用户放弃支付的订单可以关单,关单后同一个 out_trade_no 不能再支付:

POST {gatewayUrl}/v3/pay/transactions/out-trade-no/{out_trade_no}/close
{"mchid": "{compatMchid}"}

七、常见问题 ​

现象原因处理
chooseWXPay:fail 未知 action用户的看购 App 版本过旧,不支持服务商模式提示用户升级看购 App
chooseWXPay:fail appId 与看购 App 不一致…sp_appid / appId 不是看购 App 的 AppID改用 meta.txt 的 spAppid 重新下单
chooseWXPay:fail 缺少参数 …wxApp 字段不全检查服务器返回的结构,见 3.4
chooseWXPay:fail 提示公众号 JSAPI 参数用了 JSAPI 下单接口或 JSAPI 签名方式改用 /v3/pay/partner/transactions/app,见 3.4
chooseWXPay:fail 当前 App 未集成微信支付在非正式构建的看购 App(如 Expo Go)中打开在正式看购 App 内测试。普通浏览器里 SDK 会模拟成功,不能用来验证支付
网关返回签名错误 / 401商户号、证书序列号、私钥三者不匹配;或自己拼签名串拼错了核对 meta.txt;改用官方 SDK
SDK 报应答验签失败用了「平台证书」模式,或 pubKeyId / 公钥文件不对改用「微信支付公钥」模式初始化,见 3.3
唤起微信后立刻失败,无提示没用网关返回的调起参数,自己用 compatMchid 重签了;或改动了其中字段按 3.4 原样透传网关应答
用户付了钱但订单还是待支付通知丢失,或验签失败被丢弃检查通知日志;补上第六节的主动查单
收到 success 后查单仍是 NOTPAY支付结果同步有延迟间隔几秒再查,不要立即判定失败

八、上线自检清单 ​

上线前逐条确认:

  • [ ] 商户私钥、apiV3Key 只存在于服务器,H5 打包产物里搜不到
  • [ ] H5 只把订单号传给服务器,金额由服务器决定
  • [ ] sp_appid 是 meta.txt 的 spAppid(看购 App 的 AppID)
  • [ ] APP 调起参数原样取自网关应答,partnerId 不是 compatMchid
  • [ ] amount.total 单位是分
  • [ ] notify_url 是自己的地址,公网可达,不带 query 参数
  • [ ] 支付通知做了验签,验签失败直接丢弃
  • [ ] 通知处理核对了 sub_mchid 和金额
  • [ ] 通知处理是幂等的,重复投递不会重复发货
  • [ ] 有主动查单兜底,不把 success 回调当作支付成功
  • [ ] 在正式看购 App 里用 0.01 元真实付过一笔

九、支付的其他问题 ​

退款、账单下载等能力请查看 KGPay 支付平台(pay.citv.cc)文档,你的业务服务器用同一套证书调用。


附:KGPay 网关表单模式 ​

早期接入的 kg.chooseWXPay({ form }) 方式(业务服务器用 appSecret 对 KGPay 下单表单做 MD5 签名,经看购服务端转发)仍然可以使用,新接入请使用本文的服务商模式。两种方式的调用区别见 JSSDK 文档 · 支付。