回调通知实战:PayNotifyUrl 安全校验与订单处理指南)
后端即时通讯金融科技【免费下载链接】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 文档体系即仓库中TenPayV2示例所对应的微信支付 V2/旧 V3 文档中的**支付回调Payment Callback**环节以 JSAPI 支付为例讲解用户付款成功后微信服务器如何主动通知应用服务器、如何通过ResponseHandler完成签名校验、如何构造规定的 XML 应答格式以及原生支付Native模式下回调地址的不同设置方式。读完本文你将掌握在 Samples/TenPayV2/Senparc.Weixin.Sample.TenPayV2/Controllers/TenPayV3Controller.cs 中PayNotifyUrl()的完整实现原理并能在自己的 ASP.NET Core / .NET Framework 项目中安全落地支付回调。术语说明仓库中TenPayV2示例的控制器名为TenPayV3Controller它对应的是微信支付官方文档中的 V2旧版接口统一下单、扫码支付、刷卡支付等文件头注释已明确说明并非微信之后出来的 API V3真正 API V3 的示例见TenPayApiV3Controller。本文全文围绕这套旧文档体系的回调展开。支付回调的触发机制与信任模型当用户在微信侧完成支付后微信服务器会主动向应用服务器推送一条支付结果通知。这条消息只发生在微信官方服务器与应用服务器之间不经过用户端用户的手机客户端不会也不应该作为支付结果的传递通道附带签名校验微信会对通知内容按约定规则签名应用侧必须校验通过后才能采信回调地址由发起支付时指定或由商户后台配置。以 JSAPI 支付为例统一下单时通过TenPayV3UnifiedorderRequestData的notifyUrl参数指定回调地址详见 JSAPI 支付文档而Native扫码支付的回调地址则是在微信支付商户后台配置商户后台 产品中心 开发配置 Native 支付回调链接不同支付方式的回调地址设置位置可能不同。安全红线绝不能信任客户端状态原文档反复强调一条核心安全原则Never trust the status of the mobile client completing the payment and using this to notify the app server that the user has completed the payment!即永远不要用移动端 JS 回调显示支付成功来作为服务端确认交易成功的依据。WeixinJSBridge.invoke(getBrandWCPayRequest, ...)返回的res.err_msg get_brand_wcpay_request:ok只能用于前端交互提示真正的入账判断必须以服务端收到并经签名校验的微信支付通知为准。定义回调入口PayNotifyUrl 完整实现原文档给出了 JS-SDK 支付回调入口的完整代码注释已按仓库实际实现整理修正对应 TenPayV3Controller.cs 中的PayNotifyUrl()方法/// summary /// JS-SDK支付回调地址在统一下单接口中设置notify_url /// /summary /// returns/returns public ActionResult PayNotifyUrl() { try { ResponseHandler resHandler new ResponseHandler(HttpContext); string return_code resHandler.GetParameter(return_code); string return_msg resHandler.GetParameter(return_msg); string res null; resHandler.SetKey(TenPayV3Info.Key); //验证请求是否从微信发过来安全 if (resHandler.IsTenpaySign() return_code.ToUpper() SUCCESS) { res success;//正确的订单处理 //直到这里才能认为交易真正成功了可以进行数据库操作但是别忘了返回规定格式的消息 } else { res wrong;//错误的订单处理 } /* 这里可以进行订单处理的逻辑 */ #region 记录日志 var logDir ServerUtility.ContentRootMapPath( string.Format(~/App_Data/TenPayNotify/{0}, SystemTime.Now.ToString(yyyyMMdd))); if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); } var logPath Path.Combine(logDir, string.Format({0}-{1}-{2}.txt, SystemTime.Now.ToString(yyyyMMdd), SystemTime.Now.ToString(HHmmss), Guid.NewGuid().ToString(n).Substring(0, 8))); using (var fileStream System.IO.File.OpenWrite(logPath)) { var notifyXml resHandler.ParseXML(); fileStream.Write(Encoding.Default.GetBytes(notifyXml), 0, Encoding.Default.GetByteCount(notifyXml)); fileStream.Close(); } #endregion string xml string.Format(xml return_code![CDATA[{0}]]/return_code return_msg![CDATA[{1}]]/return_msg /xml, return_code, return_msg); return Content(xml, text/xml); } catch (Exception ex) { WeixinTrace.WeixinExceptionLog(new WeixinException(ex.Message, ex)); throw; } }代码关键点拆解new ResponseHandler(HttpContext)接收并解析微信推送的 POST 请求。SDK 内部会把 Form、QueryString 以及请求体中的 XMLxml根节点下的所有子节点统一加载为参数表Hashtable之后即可通过GetParameter(xxx)读取任意字段。注意在 .NET Core 环境下必须传入HttpContext实例构造注释明确警告传 Null 会调试困难见 ResponseHandler.cs。return_code与return_msg通知中最基本的两个字段return_code为SUCCESS表示微信侧受理成功注意它不等于业务成功仍需配合result_code与签名判断。resHandler.SetKey(TenPayV3Info.Key)IsTenpaySign()设置商户支付密钥后做签名校验。IsTenpaySign()的实现规则见 ResponseHandler.cs是对除sign、key及空值外的全部参数按 ASCII 字典序排序拼接为kvkv...key商户密钥的字符串取 MD5 并小写化后与通知中的sign字段比对。校验通过才说明消息确实来自微信服务器。双重条件才可入账IsTenpaySign() return_code.ToUpper() SUCCESS同时满足后注释明确提示直到这里才能认为交易真正成功了可以进行数据库操作但是别忘了返回规定格式的消息——即入账逻辑如修改订单状态、发送模板消息等必须放在该校验通过的分支内。应答格式无论成功与否都必须以微信规定的 XML 格式返回return_code/return_msg包裹在 CDATA 中Content(xml, text/xml)。若不按格式应答微信会视为通知失败并多次重试推送。异常处理捕获异常后通过WeixinTrace.WeixinExceptionLog(new WeixinException(...))记录并throw让框架层面兜底——回调处理失败宁可抛异常也不能静默吞掉。示例中的订单处理与日志落盘在真实项目里这里可以进行订单处理的逻辑就是你的幂等入账代码。示例在其下方演示了一个通用的做法将微信推送的原始 XMLresHandler.ParseXML()输出按日期写入~/App_Data/TenPayNotify/yyyyMMdd/目录下的 txt 文件方便事后排查对账。被注释掉的模板消息代码则展示了在回调里发送支付成功模板消息给用户TemplateApi.SendTemplateMessage实际项目可参照 TemplateMessageService.cs 中的封装实现。回调地址的两种配置来源原文档特别提醒回调地址的设置方式随支付方式不同而不同务必区分支付方式回调地址设置位置说明JSAPI公众号内支付统一下单请求的notifyUrl参数见 JSAPI 支付文档示例代码在TenPayV3Controller.JsApi()中以TenPayV3Info.TenPayV3Notify传入Native扫码支付微信支付商户后台配置商户后台 产品中心 开发配置 Native 支付回调链接见 Native 支付文档在配置文件中TenPayV3_TenpayNotify即TenPayV3Info.TenPayV3Notify就是支付完成后的回调处理页面地址默认形如http://YourDomainName/TenpayV3/PayNotifyUrl小程序支付另有TenPayV3_WxOpenTenpayNotify未显式设置时会默认在TenPayV3_TenpayNotify值末尾追加WxOpen相关字段定义见 TenPayV3Info.cs。从配置到代码的完整链路TenPayV3Info.Key、TenPayV3Info.TenPayV3Notify等值来源于appsettings.json中SenparcWeixinSetting节点如TenPayV3_Key、TenPayV3_TenpayNotify并在应用启动注册时被装入TenPayV3InfoCollection。示例中通过TenPayHelper.GetRegisterKey(Config.SenparcWeixinSetting)取得注册键后从TenPayV3InfoCollection.Data[key]读取见 TenPayV3Controller.cs。完整的注册与配置流程见 注册文档 与示例 appsettings.json。签名校验的底层原理ResponseHandler 源码剖析ResponseHandler是 SDK 封装的应答解析器位于 src/Senparc.Weixin.TenPay/Senparc.Weixin.TenPay/V3/ResponseHandler.cs其核心成员与方法如下成员/方法作用SetKey(key)/GetKey()设置/获取商户支付密钥微信商户后台 账户设置 API 安全 API 密钥GetParameter(name)获取已解析的任一通知参数缺失时返回空串SetParameter(name, value)设置参数覆盖式供内部解析与回显使用IsTenpaySign()按财付通签名规则校验签名参数按 ASCII 排序、空值不参与、拼接key后 MD5 小写比对ParseXML()将当前参数表序列化为xml包裹的 XML数字不加 CDATA其余加 CDATAGetDebugInfo()获取最近一次签名拼接串调试签名不通过时非常有用从源码可观察到几个实现细节构造函数自动解析无论是 .NET Framework读取HttpContext.Request.Form、QueryString、InputStream还是 ASP.NET Core读取Request.Form、Request.Query、Request.Body都会自动把微信推送的 XML 根节点xml下所有子节点写入参数表所以回调代码里无需手动解析 XML。XML 安全处理XML 解析使用XmlDocument_XxeFixedXmlResolver null从实现层面规避了 XXEXML 外部实体注入攻击这也呼应了回调入口必须暴露在公网、更需注重安全的现实。签名拼接顺序IsTenpaySign()使用ASCIISort字典排序与统一下单请求端RequestHandler.CreateMd5Sign(key, key)的签名规则保持一致形成请求签名、回调验签的闭环。与回调配套的周边能力退款回调 RefundNotifyUrl支付回调的镜像是退款回调。示例RefundNotifyUrl()见 TenPayV3Controller.cs演示了另一类通知的处理要点微信推送的req_info字段是用商户密钥做 AES 加密的密文需调用TenPayV3Util.DecodeRefundReqInfo(req_info, TenPayV3Info.Key)解密后再解析transaction_id、out_trade_no、refund_status等字段。完整流程见 退款文档。Native 支付回调 NativeNotifyUrlNative 支付模式一中用户扫码后微信会先回调商户地址换取prepay_id详见 Native 支付文档此时使用RequestHandler与ResponseHandler相对负责构造带签名请求组织应答 XML 返回给微信再走后续统一下单流程——这与支付完成后的PayNotifyUrl属于两种不同性质的回调需要注意区分。实战落地清单在接入 WeiXinMPSDK 微信支付回调时建议按以下清单逐项核对回调入口独立 Action如PayNotifyUrl()务必能从公网访问且路径与notifyUrl/ 商户后台配置一致。签名校验先行resHandler.SetKey(Key)后再IsTenpaySign()与return_code SUCCESS双重判定全部通过才执行入账。入账逻辑幂等微信通知会重试订单状态更新必须防重可用订单号唯一约束或状态机避免重复入账。按规定格式应答以xmlreturn_code![CDATA[SUCCESS]]/return_code...的格式返回失败时返回FAIL并附return_msg说明原因让微信重试或人工介入。敏感数据落盘谨慎回调日志可参考示例写入App_Data等受保护目录该目录默认不允许 Web 访问切勿直接写入可被公网下载的路径。区分回调类型JSAPI 的notifyUrl在统一下单时传参、Native 的回调在商户后台配置、退款回调走RefundNotifyUrl且req_info需 AES 解密不要混用。总结微信支付回调是服务器对服务器的可靠通知通道Senparc.WeixinWeiXinMPSDK通过ResponseHandler一行解析、一行验签将繁琐的 XML 解析与签名算法封装为可直接调用的 API。实践中的成败关键在于回调入口必须做签名校验 状态双重判定、入账必须幂等、应答必须符合微信规定的 XML 格式并牢记永远不要信任客户端上报的支付结果。在仓库中你可以直接对照 TenPayV3Controller.cs 的PayNotifyUrl()/RefundNotifyUrl()/NativeNotifyUrl()三个方法快速搭建起完整的回调处理体系。赞分享后端即时通讯金融科技【免费下载链接】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 唤起全流程 本文后端即时通讯金融科技WeiXinMPSDK 微信支付V2支付回调实战从 notify_url 到 ResponseHandler 签名验证的完整实现WeiXinMPSDK 微信支付V2支付回调实战从 notify_url 到 ResponseHandler 签名验证的完整实现 本文围绕 WeiXinM后端即时通讯金融科技微信支付退款状态实时同步基于WeiXinMPSDK的完整通知处理指南微信支付退款状态实时同步基于WeiXinMPSDK的完整通知处理指南 微信支付退款状态同步是电商系统中至关重要的环节直接关系到用户体验和资金安全。通过Wei后端即时通讯金融科技上一篇BBUncrustifyPlugin-Xcode使用指南3步实现Xcode代码自动格式化下一篇如何用AI自动化视频创作ShortGPT完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考