尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Senparc.Weixin 微信支付 V3 JSAPI 公众号网页支付实战:从商品页到 prepay_id 签名唤起

发布时间:2026/9/25 5:08:01

资讯中心
01
ARTICLE

Senparc.Weixin 微信支付 V3 JSAPI 公众号网页支付实战:从商品页到 prepay_id 签名唤起

Senparc.Weixin 微信支付 V3 JSAPI 公众号网页支付实战:从商品页到 prepay_id 签名唤起
后端即时通讯金融科技【免费下载链接】WeiXinMPSDK微信全平台 .NET SDK Senparc.Weixin for C#支持 .NET Framework 及 .NET Core、.NET 10.0。已支持微信公众号、小程序、小游戏、微信支付、企业微信/企业号、开放平台、JSSDK、微信周边等全平台。 WeChat SDK for C#.项目地址https://gitcode.com/gh_mirrors/we/WeiXinMPSDK点击查看免费下载本文基于当前仓库的 TenPay V3 官方示例工程完整讲解 JSAPI公众号网页支付的三段式实现商品列表 → 商品详情环境识别→ JsApi 支付页下单、prepay_id、前端签名唤起并深入 SDK 源码说明BasePayApis.JsApiAsync、TenPaySignHelper.GetJsApiUiPackage等关键调用的真实实现。读完本文你可以照仓库示例复刻一套可运行的公众号网页支付流程并理解每个字段的来源与安全边界。JSAPI 是微信内置浏览器H5 网页中唯一能唤起微信支付收银台的 JS 接口。仓库示例提供了 3 个关键页面ProductList商品列表、ProductItem商品详情和 JsApiJSAPI 订单支付分别位于 TenPayApiV3Controller.cs 与 Views/TenPayApiV3/ 目录下。ProductList商品列表页后端参考 TenPayApiV3Controller.cs 中的ProductList()方法public IActionResult ProductList() { var products ProductModel.GetFakeProductList(); return View(products); }这里并没有真实数据库商品数据由 ProductModel.cs 中的GetFakeProductList()在内存中模拟返回一个静态ListProductModelId、Name、Price如“产品1 ¥0.01”“捐赠4 ¥500.00”。价格单位为“元”decimal到下单接口时会转换为“分”。实际项目中此处应替换为从数据库按业务 ID 查询。前端Views/TenPayApiV3/ProductList.cshtml 负责渲染商品列表并为每个商品链接携带productId与hc两个参数跳转到商品详情页。ProductItem商品详情页与环境分流后端参考 TenPayApiV3Controller.cs 中的ProductItem()方法public ActionResult ProductItem(int productId, int hc) { var products ProductModel.GetFakeProductList(); var product products.FirstOrDefault(z z.Id productId); if (product null || product.GetHashCode() ! hc) { return Content(商品信息不存在或非法进入2003); } //判断是否正在微信端 if (Senparc.Weixin.BrowserUtility.BrowserUtility.SideInWeixinBrowser(HttpContext)) { //正在微信端直接跳转到微信支付页面 return RedirectToAction(JsApi, new { productId productId, hc hc }); } else { //在PC端打开提供二维码扫描进行支付 return View(product); } }这里的两个设计点值得注意hc参数的作用hc是商品对象的HashCode用于校验当前内存商品信息的有效性防止伪造链接直接进入支付页。这是 Sample 演示手段实际开发项目中无需使用可忽略。SideInWeixinBrowser()环境分流SDK 的Senparc.Weixin.BrowserUtility.BrowserUtility通过 UA 判断当前请求是否来自微信内置浏览器。在微信内打开则 302 跳转到JsApi支付页在 PC 浏览器打开则渲染详情页并在“支付方式二扫一扫支付”处展示由 ProductPayCode() 动态生成的二维码——该二维码编码的是 JsApi 支付页 URL手机扫码后即可在微信内完成下单支付形成“PC 下单、手机支付”的闭环。前端Views/TenPayApiV3/ProductItem.cshtml。JsApi订单支付页JSAPI 支付核心这是整个 JSAPI 支付中最关键的页面分为后端下单与前端唤起两步。后端OAuth 登录 统一下单 生成 UI 签名包用户点击下单按钮后需要在后台生成一个预支付订单并在页面上登记。参考 TenPayApiV3Controller.cs 中的JsApi()方法仓库当前代码//需要OAuth登录 [CustomOAuth(null, /TenpayApiV3/OAuthCallback)] public async TaskIActionResult JsApi(int productId, int hc) { try { //获取产品信息 var products ProductModel.GetFakeProductList(); var product products.FirstOrDefault(z z.Id productId); if (product null || product.GetHashCode() ! hc) { return Content(商品信息不存在或非法进入1002); } ViewData[product] product; var openId HttpContext.Session.GetString(OpenId); string sp_billno Request.Query[order_no];//out_trade_no if (string.IsNullOrEmpty(sp_billno)) { //生成订单10位序列号此处用时间和随机数生成商户根据自己调整保证唯一 sp_billno string.Format({0}{1}{2}, TenPayV3Info.MchId/*10位*/, SystemTime.Now.ToString(yyyyMMddHHmmss), TenPayV3Util.BuildRandomStr(6)); //注意以上订单号仅作为演示使用如果访问量比较大建议增加订单流水号的去重检查。 } //调用下单接口下单 var name product null ? test : product.Name; var price product null ? 100 : (int)(product.Price * 100);//单位分 var notifyUrl TenPayV3Info.TenPayV3Notify; //请求信息 TransactionsRequestData jsApiRequestData new(TenPayV3Info.AppId, TenPayV3Info.MchId, name - 微信支付 V3, sp_billno, new TenpayDateTime(DateTime.Now.AddHours(1), false), null, notifyUrl, null, new() { currency CNY, total price }, new(openId), null, null, null); //请求接口 var basePayApis2 new Senparc.Weixin.TenPayV3.TenPayHttpClient.BasePayApis2(_httpClient, _tenpayV3Setting); var result await basePayApis2.JsApiAsync(jsApiRequestData); if (result.VerifySignSuccess ! true) { throw new WeixinException(获取 prepay_id 结果校验出错); } //获取 UI 信息包 var jsApiUiPackage TenPaySignHelper.GetJsApiUiPackage(TenPayV3Info.AppId, result.prepay_id, Senparc.Weixin.Config.SenparcWeixinSetting); ViewData[jsApiUiPackage] jsApiUiPackage; //临时记录订单信息留给退款申请接口测试使用分布式情况下请注意数据同步 HttpContext.Session.SetString(BillNo, sp_billno); HttpContext.Session.SetString(BillFee, price.ToString()); return View(); } catch (Exception ex) { Senparc.Weixin.WeixinTrace.BaseExceptionLog(ex); throw; } }逐段拆解这段代码的三个要点1.TransactionsRequestData下单参数。SDK 将微信 V3 统一下单接口的请求体封装为该结构示例中按位置传参含义依次为参数示例取值说明appid/mchidTenPayV3Info.AppId/MchId公众号 AppId 与商户号来自TenPayV3Infodescriptionproduct.Name - 微信支付 V3商品描述V3 中替代 V2 的 bodyout_trade_nosp_billno商户订单号示例中由“商户号(10位) 时间戳(yyyyMMddHHmmss) 6位随机串”生成time_expireTenpayDateTime(当前1小时, false)订单失效时间示例为 1 小时后notify_urlTenPayV3Info.TenPayV3Notify支付结果异步回调地址对应PayNotifyUrlActionamountnew() { currency CNY, total price }金额货币固定 CNYtotal单位为分product.Price * 100payernew(openId)付款用户 OpenIdJSAPI 支付必填2. 统一下单与 prepay_id。BasePayApis2.JsApiAsync()内部请求微信接口POST /v3/pay/transactions/jsapi可参见 SDK 源码 BasePayApis.cs返回体中的result.prepay_id即“预支付交易会话标识”——当前订单编号已在此刻于微信支付后台完成注册前端页面必须凭借 prepay_id 才能让手机端唤起微信支付。示例在调用后首先检查result.VerifySignSuccess只有响应签名验证通过才继续这是 V3 接口防篡改的基本安全要求。注意微信支付 API V2 与 API V3 在订单接口上有完全不同的区别请求结构、签名方式、返回格式均不同如果从 V2 升级请留意。3.[CustomOAuth]特性与 OpenId 的获取。JSAPI 支付必须传入付款用户 OpenId而 OpenId 需要通过微信公众号 OAuth 授权获得。[CustomOAuth(null, /TenpayApiV3/OAuthCallback)]特性实现见 CustomOAuthAttribute.cs会在请求未登录Session 中无OpenId时自动 302 到微信授权页授权完成后微信回调OAuthCallback由OAuthApi.GetAccessToken()用 code 换取 OpenId 并写入 Session再跳回 JsApi 页面见 TenPayApiV3Controller.cs。这个 OAuth 功能属于公众号范畴此处只说明它在支付链路中的位置。此外示例用HttpContext.Session.SetString(BillNo, sp_billno)与SetString(BillFee, ...)临时记录订单号与金额供后续的退款申请接口测试使用分布式部署下应改用缓存或数据库并注意数据同步。前端用 WeixinJSBridge 唤起收银台前端的关键操作是用户点击“立即支付”按钮后执行 JS 代码调用微信内置浏览器的WeixinJSBridge.invoke(getBrandWCPayRequest, ...)。仓库实际页面 Views/TenPayApiV3/JsApi.cshtml 中后端生成的JsApiUiPackage通过 Razor 表达式注入到参数对象中// 当微信内置浏览器完成内部初始化后会触发WeixinJSBridgeReady事件。 document.addEventListener(WeixinJSBridgeReady, function onBridgeReady() { //公众号支付 jQuery(#getBrandWCPayRequest).click(function (e) { WeixinJSBridge.invoke(getBrandWCPayRequest, { appId: jsApiUiPackage.AppId, //公众号名称由商户传入 timeStamp: jsApiUiPackage.Timestamp, //时间戳 nonceStr: jsApiUiPackage.NonceStr, //随机串 package: Html.Raw(jsApiUiPackage.PrepayIdPackage), //扩展包 signType: Senparc.Weixin.Config.SenparcWeixinSetting.TenpayV3Setting.EncryptionType, //签名方式 paySign: Html.Raw(jsApiUiPackage.Signature) //微信签名 }, function (res) { if (res.err_msg get_brand_wcpay_request:ok) { setTimeout(function () { if (confirm(支付成功点击确定进入退款流程测试。)) { location.href Url.Action(Refund, TenPayApiV3); } }, 300); } else { alert(JSON.stringify(res)); } }); }); }, false);getBrandWCPayRequest的 6 个参数与 SDK 实体 JsApiUiPackage.cs 一一对应参数来源JsApiUiPackage 属性说明appIdAppId公众号 AppIdtimeStampTimestamp时间戳字符串nonceStrNonceStr随机串packagePrepayIdPackage格式固定为prepay_idxxx的扩展包signType配置项EncryptionTypeV3 签名方式示例配置为RSAJsApiUiPackage.SignType属性固定返回RSApaySignSignature前端调起支付的签名源码纵深GetJsApiUiPackage 如何生成签名包JsApiUiPackage并非手工拼装而是由 TenPaySignHelper.cs 中的GetJsApiUiPackage()一次性生成public static JsApiUiPackage GetJsApiUiPackage(string prepayId, TenPayV3Info tenPayV3Info, JsApiAppType appType JsApiAppType.WxOpen) { if (tenPayV3Info null) { throw new TenpayApiRequestException(tenPayV3Info 参数不能为空); } var timeStamp TenPayV3Util.GetTimestamp(); var nonceStr TenPayV3Util.GetNoncestr(); var prepayIdPackage GetPrepayIdPackage(prepayId, appType); var sign TenPaySignHelper.CreatePaySign(timeStamp, nonceStr, prepayIdPackage, tenPayV3Info); JsApiUiPackage jsApiUiPackage new(tenPayV3Info.AppId, timeStamp, nonceStr, prepayIdPackage, sign); return jsApiUiPackage; }从源码结构看签名内容的构成是appId \n timeStamp \n nonceStr \n package \n四行拼接后使用商户 API 私钥TenPayV3Info中配置的证书/私钥做 RSA 签名——这正是paySign的由来。v2.4.0 起该方法新增JsApiAppType参数区分应用场景JsApiAppType.WxOpen默认公众号/小程序PrepayIdPackage为prepay_idxxx带前缀形式JsApiAppType.NativeApp原生 AppPrepayIdPackage为不带前缀的原始 prepay_id。仓库测试用例 TenPaySignHelperTests.cs 明确验证了这两种格式的差异。同时注意TenPaySignHelper中还提供了多个带[Obsolete]标记的旧签名如按appId prepayId传参的版本示例工程当前使用的三参数重载已标记过时新代码建议迁移到GetJsApiUiPackage(prepayId, tenPayV3Info, appType)形式避免因验签 appId 传参无效导致调起支付验签失败这是 v2.3.3 修复过的实际缺陷。注意微信团队郑重提示res.err_msg会在用户支付成功后返回ok但并不保证它绝对可靠。微信团队建议当收到ok返回时向商户后台确认是否已收到交易成功的异步通知若未收到商户后台应主动调用查询订单接口示例中的OrderQuery查询订单当前状态再反馈给前端展示相应界面。支付后的闭环异步通知、退款与订单查询JSAPI 支付页不是链路的终点示例在同一个 Controller 中还实现了完整的后续流程异步回调PayNotifyUrl()TenPayApiV3Controller.cs这是下单时notify_url指向的地址。收到微信回调后使用TenPayNotifyHandler的DecryptGetObjectAsyncOrderReturnJson()完成 AEAD 解密与验签仅当VerifySignSuccess true trade_state SUCCESS时才返回{code:SUCCESS}同时示例把transaction_id记入内存对照表TradeNumberToTransactionId并将通知 JSON 落盘到App_Data/TenPayNotify/目录作为审计日志。退款申请Refund()TenPayApiV3Controller.csJsApi 支付成功页的“确定”按钮会跳转到这里。它从 Session 取出订单号从TradeNumberToTransactionId中换取transaction_id若微信回调尚未到达会提示稍后再试构造RefundRequestData后调用_basePayApis.RefundAsync(dataInfo)发起全额退款。订单查询与关单OrderQuery()/CloseOrder()TenPayApiV3Controller.cs分别支持按out_trade_no或transaction_id查询订单以及按out_trade_no关闭订单对应微信 V3 的/v3/pay/transactions/out-trade-no/{out_trade_no}等接口。这条“下单 → 前端唤起 → 异步通知验签 → 退款/查询”的完整闭环正是仓库 TenPay V3 示例对 JSAPI 支付场景的完整演示。落地时的注意事项prepay_id 与订单号一一对应JsApi()注释明确提示“不能对同一个订单号重复支付”示例中的订单号仅由时间随机数生成高并发场景必须增加流水号去重检查。金额单位TransactionsRequestData.amount.total以“分”为单位示例中product.Price * 100的换算不可省略货币固定CNY。OpenId 的时效性OpenId 通过[CustomOAuth]写入 Session 后在整个下单过程中复用若用户长时间未操作或 Session 过期重新进入页面会自动重新走 OAuth 流程。签名方式V3 下单响应校验VerifySignSuccess与前端signType都依赖商户证书/私钥配置TenPayV3Info中的 AppId、MchId、SerialNo、PrivateKey 及回调证书配置缺失时签名环节会直接失败这是排查支付问题时最先应检查的配置项。结果确认以异步通知为准前端get_brand_wcpay_request:ok只作为展示参考真正的业务状态变更发货、记账必须发生在PayNotifyUrl验签通过之后。参考文件汇总TenPayApiV3Controller.cs、JsApi.cshtml、ProductItem.cshtml、CustomOAuthAttribute.cs、BasePayApis.cs、TenPaySignHelper.cs、JsApiUiPackage.cs。赞分享后端即时通讯金融科技【免费下载链接】WeiXinMPSDK微信全平台 .NET SDK Senparc.Weixin for C#支持 .NET Framework 及 .NET Core、.NET 10.0。已支持微信公众号、小程序、小游戏、微信支付、企业微信/企业号、开放平台、JSSDK、微信周边等全平台。 WeChat SDK for C#.项目地址https://gitcode.com/gh_mirrors/we/WeiXinMPSDK点击查看免费下载相关推荐Senparc.WeixinWeiXinMPSDK微信支付 V2 JSAPI 支付实战商品页、预支付订单与 WeixinJSBridge 唤起全流程Senparc.WeixinWeiXinMPSDK微信支付 V2 JSAPI 支付实战商品页、预支付订单与 WeixinJSBridge 唤起全流程 本文后端即时通讯金融科技Senparc.Weixin 微信支付V2接入实战全局注册、公众号与支付模块注册及 appsettings.json 配置全解Senparc.Weixin 微信支付V2接入实战全局注册、公众号与支付模块注册及 appsettings.json 配置全解 本文基于 Senparc.后端即时通讯金融科技上一篇Kubernetes上的Kafka部署指南下一篇边缘AI部署革命昇腾Atlas 300I Duo与PaddleX的高性能OCR解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。