ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

微信公众号网页 JSAPI 支付接入实战:基于 Senparc.Weixin SDK 微信支付 V3 全流程解析

微信公众号网页 JSAPI 支付接入实战:基于 Senparc.Weixin SDK 微信支付 V3 全流程解析 后端即时通讯金融科技【免费下载链接】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仓库中微信支付 V3 的官方指南 docs/en/guide/tenpayv3/jssdk.md及其中文对照版 docs/zh/guide/tenpayv3/jssdk.md为骨架围绕“在微信公众号网页中使用 JSAPI 完成微信支付”这一核心场景展开。文章将带你走完从商品列表、商品详情到预支付下单、前端唤起支付、后端支付结果通知的完整链路并结合仓库内TenPayApiV3Controller控制器、JsApi.cshtml视图与TenPaySignHelper签名工具的真实源码讲透prepay_id、JsApiUiPackage信息包与 RSA/国密签名等底层原理。读完你可以直接照着示例项目在自己的公众号网页中落地 JSAPI 支付。在微信公众号网页中使用微信支付必须借助 JSAPI 完成支付流程。官方示例在 Samples/TenPayV3/Senparc.Weixin.Sample.TenPayV3 中提供了 3 个关键页面页面作用ProductList商品列表展示商品提供下单入口如“一键购买”ProductItem商品详情展示商品详情根据运行环境分流到 JSAPI 支付或扫码支付JsApiJSAPI 订单支付生成预支付订单并唤起微信支付三个页面对应的后端逻辑全部集中在 Controllers/TenPayApiV3Controller.cs注意仓库文件名是TenPayApiV3Controller.cs与文档中略写的TenPayApiV3Controller.cs对应前端视图位于 Views/TenPayApiV3 目录。一、商品列表页ProductList提供下单入口JSAPI 支付的第一步是让用户找到一个“下订单”的入口。示例项目用内存模拟了一份商品数据见 Models/ProductModel.cs 中的GetFakeProductList()后端仅需一行代码即可渲染商品列表public IActionResult ProductList() { var products ProductModel.GetFakeProductList(); return View(products); }对应源码位置TenPayApiV3Controller.cs#L113-L117。ProductList()从内存获取商品集合交给ProductList.cshtml视图渲染每个商品上提供进入详情的链接同时把productId与hc商品信息的 HashCode作为参数传递用于后续校验。视图商品列表的前端展示请参考Views/TenPayApiV3下的对应视图文件。文档对应小节见 docs/en/guide/tenpayv3/jssdk.md#productlist-productlist。在实际项目中商品列表通常来自数据库GetFakeProductList()仅作为演示。二、商品详情页ProductItem按运行环境分流商品详情页的职责不止是展示商品还要判断“当前页面是在微信内打开还是在 PC 浏览器打开”从而决定走哪条支付路径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); } }对应源码位置TenPayApiV3Controller.cs#L124-L144。关键点 1productId与hc双参数校验productId是商品编号示例中从内存列表查询真实项目中通常以数据库为准。hc是商品信息对应的GetHashCode()值用来防止非法或过期参数直接进入下单流程如价格被篡改。文档明确说明hc仅用于保证示例内存数据的有效性实际开发项目中可以忽略。关键点 2SideInWeixinBrowser()环境判断方法Senparc.Weixin.BrowserUtility.BrowserUtility.SideInWeixinBrowser(HttpContext)用于判断当前请求是否来自微信内置浏览器在微信内打开直接RedirectToAction(JsApi, ...)进入 JSAPI 订单支付页此时拿不到手机扫码环节直接唤起支付。在 PC 端打开渲染商品详情页并提供多种支付方式选择其中“扫一扫”支付会在页面底部根据所选商品自动生成二维码用户用手机微信扫码后即可进入对应的 JsApi 订单页面对应控制器中的NativePayCode与ProductPayCode方法见 TenPayApiV3Controller.cs#L152-L206二者分别生成 Native 支付码与跳转手机详情页的二维码。前端视图参考 Views/TenPayApiV3/ProductItem.cshtml。三、JSAPI 订单支付页JsApi预支付下单与唤起支付这是整个流程的核心页面。用户点击下单按钮后后端需要生成预支付订单拿到prepay_id前端再凭借它唤起微信支付。文档对应的代码参考TenPayApiV3Controller.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)); } else { sp_billno Request.Query[order_no]; } //调用下单接口下单 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; } }对应源码位置TenPayApiV3Controller.cs#L254-L315。文档中的代码片段保留了更早期的写法如var result TenPayOldV3.Unifiedorder(xmlDataInfo)或直接以TenPayV3Info.AppId调用GetJsApiUiPackage仓库当前版本已演进为TenPayHttpClient.BasePayApis2.JsApiAsync()与带TenPayV3Info的新重载但核心语义完全一致result.prepay_id即“预支付 ID”前端必须凭借它才能在手机端唤起微信支付此时当前订单编号已在微信支付后台注册。3.1[CustomOAuth]特性自动完成用户身份识别JsApi()方法上标注了[CustomOAuth(null, /TenpayApiV3/OAuthCallback)]该特性实现见 Filters/CustomOAuthAttribute.cs用于自动使用微信公众号 OAuth 能力识别用户身份回调地址指向OAuthCallbackTenPayApiV3Controller.cs#L213-L247拿到openid后存入 Session。JSAPI 支付要求payer.openid与当前公众号用户一致因此该步骤不能省略。文档特别说明OAuth 属于公众号功能范畴本文不展开。3.2 下单请求参数TransactionsRequestDataTransactionsRequestData是微信支付 V3 下单接口的请求数据实体构造参数顺序对应如下语义示例中按位置传参位置参数含义示例值/说明appid公众号 AppIdTenPayV3Info.AppIdmchid商户号TenPayV3Info.MchIddescription商品描述name - 微信支付 V3out_trade_no商户订单号必唯一sp_billno商户号时间6 位随机数time_expire订单失效时间new TenpayDateTime(DateTime.Now.AddHours(1), false)1 小时后过期attach附加数据nullnotify_url支付结果回调地址TenPayV3Info.TenPayV3Notifygoods_tag商品标记nullamount金额对象new() { currency CNY, total price }单位分payer支付者信息new(openId)detail/scene_info等扩展信息null金额单位是分示例中(int)(product.Price * 100)正是把元换算成分。订单号生成逻辑在 TenPayApiV3Controller.cs#L269-L281代码注释提醒高访问量场景下应增加订单流水号去重检查。3.3 发起下单请求与验签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 结果校验出错); }BasePayApis2是 SDK 基于TenPayHttpClient实现的支付基础能力封装JsApiAsync()定义于 TenPayHttpClient/TenPayHttpClient.cspublic async TaskJsApiReturnJson JsApiAsync(TransactionsRequestData data, int timeOut Config.TIME_OUT)。返回结果JsApiReturnJson中的prepay_id是唤起支付的凭证SDK 还内置了VerifySignSuccess响应验签这是安全链路的重要一环——验签失败直接抛WeixinException避免把伪造的prepay_id交给前端。3.4 生成前端 UI 信息包GetJsApiUiPackage拿到prepay_id后还需要为前端组装调起支付所需的全部签名信息var jsApiUiPackage TenPaySignHelper.GetJsApiUiPackage(TenPayV3Info.AppId, result.prepay_id, Senparc.Weixin.Config.SenparcWeixinSetting); ViewData[jsApiUiPackage] jsApiUiPackage;JsApiUiPackage实体定义在 Entities/JsApiUiPackage.cs包含 5 个字段属性含义AppId公众号 AppIdTimestamp时间戳NonceStr随机串PrepayIdPackage打包后的预支付标识形如prepay_idxxxSignature微信支付签名SignType签名类型固定返回RSA见 JsApiUiPackage.cs#L65GetJsApiUiPackage()的底层实现Helpers/TenPaySignHelper.cs#L311-L325依次完成生成时间戳TenPayV3Util.GetTimestamp()与随机串TenPayV3Util.GetNoncestr()将prepay_id打包为prepay_id{0}格式GetPrepayIdPackage若传入时已带前缀会自动归一化调用CreatePaySign(timeStamp, nonceStr, prepayIdPackage, tenPayV3Info)计算签名组装并返回JsApiUiPackage。其中CreatePaySign的签名串格式为见 TenPaySignHelper.cs#L158-L162{appId}\n{timeStamp}\n{nonceStr}\n{package}\n签名算法由商户证书类型CertType决定RSA 模式下使用 SHA256withRSAPKCS#1 填充国密SM模式下使用 SM3withSM2密钥从TenPayV3_PrivateKey配置读取TenPaySignHelper.cs#L73-L112。SDK 同时支持“微信平台证书”与“微信支付公钥”两种验签方式VerifyTenpaySign见 TenPaySignHelper.cs#L176-L266。3.5 前端WeixinJSBridge唤起支付文档指出前端的关键操作是“当用户点击支付按钮后执行 JS 代码”。仓库中的视图 Views/TenPayApiV3/JsApi.cshtml 给出了完整实现// 当微信内置浏览器完成内部初始化后会触发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)); } }); }); WeixinJSBridge.log(WeixinJSBridge ready); }, false);调起支付参数说明WeixinJSBridge.invoke(getBrandWCPayRequest, ...)参数来源说明appIdjsApiUiPackage.AppId公众号名称AppId由商户传入timeStampjsApiUiPackage.Timestamp时间戳nonceStrjsApiUiPackage.NonceStr随机串packagejsApiUiPackage.PrepayIdPackage扩展包即prepay_idxxx使用Html.Raw输出避免转义signType配置项EncryptionType微信 V3 签名类型RSA文档示例中直接写死为RSA仓库视图则从配置读取可兼容国密 SM 模式paySignjsApiUiPackage.Signature微信支付签名同样用Html.Raw输出回调res.err_msg处理当res.err_msg get_brand_wcpay_request:ok时代表用户已调起支付。文档引用微信官方团队的郑重提示res.err_msg在用户支付成功后返回ok但并不保证它绝对可靠。建议当收到ok返回时向商户后台询问是否收到“交易成功”的通知若已收到通知前端展示交易成功界面若未收到商户后台应主动调用“查询订单”接口核对订单状态再反馈给前端展示对应界面。示例页面在支付成功后还会弹窗引导进入退款流程测试Refund方法见 TenPayApiV3Controller.cs#L497-L542并演示了通过TradeNumberToTransactionId对照表记录transaction_id供退款使用。四、支付结果通知PayNotifyUrl真正的成功依据JSAPI 支付的“成功”必须以微信服务器异步回调为准而不是前端res.err_msg。示例中的回调地址由下单时的notifyUrlTenPayV3Info.TenPayV3Notify指定处理逻辑在 TenPayApiV3Controller.cs#L322-L387//获取微信服务器异步发送的支付通知信息 var resHandler new TenPayNotifyHandler(HttpContext); var orderReturnJson await resHandler.DecryptGetObjectAsyncOrderReturnJson(_isPublicKey); //演示记录 transaction_id实际开发中需要记录到数据库以便退款和后续跟踪 TradeNumberToTransactionId[orderReturnJson.out_trade_no] orderReturnJson.transaction_id; string trade_state orderReturnJson.trade_state; //验证可靠的支付状态 if (orderReturnJson.VerifySignSuccess true trade_state SUCCESS) { returnData.code SUCCESS;//正确的订单处理 /* 提示 * 1、直到这里才能认为交易真正成功了可以进行数据库操作但是别忘了返回规定格式的消息 * 2、上述判断已经具有比较高的安全性以外还可以对访问 IP 进行判断进一步加强安全性。 */ } else { returnData.code FAILD;//错误的订单处理 returnData.message 验证失败; }要点总结TenPayNotifyHandler负责解密微信回调报文V3 回调为 AES-256-GCM 加密DecryptGetObjectAsyncOrderReturnJson()直接得到结构化结果判断逻辑是VerifySignSuccess true且trade_state SUCCESS两者同时满足才认定为支付成功否则返回FAILD处理完业务后必须返回固定格式的 JSON 应答{code:SUCCESS,message:成功}等微信侧才会停止重推回调中还演示了将通知原文落盘到App_Data/TenPayNotify/目录进行审计生产环境建议入库。五、升级注意ApiV2 与 ApiV3 的差异文档末尾特别强调了一条升级提醒值得在接入时反复确认微信支付 ApiV2 和 ApiV3 在订单接口有完全不同的区别如果升级请留意具体差异体现在多个层面签名机制V2 使用 MD5/HMAC-SHA256 商户 API KeyV3 使用商户私钥 RSASHA256withRSA或国密 SM2 签名并引入平台证书/微信支付公钥验签请求数据实体V2 的TenPayV3RefundRequestData、xmlDataInfo等 XML 风格实体在 V3 中已被TransactionsRequestData、RefundRequestData、QueryRequestData、CloseRequestData等强类型 JSON 实体取代Apis/BasePay 目录调用方式旧式静态入口如文档示例中的TenPayOldV3.Unifiedorder已演进为BasePayApis/BasePayApis2的实例方法JsApiAsync、NativeAsync、H5Async等。因此从 V2 升级到 V3 时不能简单替换请求地址而应整体迁移到新的数据实体与调用链。六、JSAPI 支付接入清单与最佳实践把上文内容收敛为一份可直接照做的接入清单环境识别在商品详情页用BrowserUtility.SideInWeixinBrowser(HttpContext)分流——微信内直接进JsApiPC 端展示详情并提供二维码扫码入口OAuth 取 openidJsApi方法标注[CustomOAuth]通过公众号 OAuth 获取用户openid回调方法参考OAuthCallback并妥善保存Session/缓存均可分布式环境注意共享存储下单组装TransactionsRequestData注意金额单位是分、订单号保证唯一、time_expire建议设置失效时间调用BasePayApis2.JsApiAsync()获取prepay_id验签检查result.VerifySignSuccess未通过必须中断流程组装 UI 信息包调用TenPaySignHelper.GetJsApiUiPackage(prepayId, tenPayV3Info)得到JsApiUiPackage传入视图前端唤起监听WeixinJSBridgeReady用WeixinJSBridge.invoke(getBrandWCPayRequest, ...)调起支付注意package与paySign需Html.Raw输出可靠判定前端res.err_msg get_brand_wcpay_request:ok仅作参考最终以PayNotifyUrl回调中VerifySignSuccess trade_state SUCCESS为准必要时主动调用订单查询接口兜底OrderQuery见 TenPayApiV3Controller.cs#L602-L629。安全性提示均来自文档与源码注释订单号需去重hc仅限演示回调应对返回 IP 做校验签名验签不可跳过退款、账单等接口的完整示例可继续翻阅 TenPayApiV3Controller.cs 的其余区域以及仓库中 docs/zh/guide/tenpayv3 与 docs/en/guide/tenpayv3 目录下的配套指南。赞分享后端即时通讯金融科技【免费下载链接】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.Weixin 微信支付 V3 JSAPI 公众号网页支付实战从商品页到 prepay_id 签名唤起Senparc.Weixin 微信支付 V3 JSAPI 公众号网页支付实战从商品页到 prepay_id 签名唤起 本文基于当前仓库的 TenPay V3后端即时通讯金融科技Senparc.WeixinWeiXinMPSDK微信支付 V2 JSAPI 支付实战商品页、预支付订单与 WeixinJSBridge 唤起全流程Senparc.WeixinWeiXinMPSDK微信支付 V2 JSAPI 支付实战商品页、预支付订单与 WeixinJSBridge 唤起全流程 本文后端即时通讯金融科技Senparc.Weixin 微信支付V2接入实战全局注册、公众号与支付模块注册及 appsettings.json 配置全解Senparc.Weixin 微信支付V2接入实战全局注册、公众号与支付模块注册及 appsettings.json 配置全解 本文基于 Senparc.后端即时通讯金融科技上一篇telebot核心功能揭秘自动回复、图片生成与Webhook设置实战下一篇toto文章管理技巧Markdown与YAML元数据完美结合创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表