导读:本期聚焦于木下创作的《微信公众号支付退款结果通知验签失败怎么办?可能是签名类型用错了》,敬请观看详情。微信支付退款结果通知接口回调时总是提示签名验证失败,排查了密钥和参数都确认无误,问题很可能出在签名类型上。与普通的支付结果通知不同,退款结果通知使用的是商户密钥的MD5签名方式,而很多开发者直接套用了普通通知的验签逻辑,导致始终验签不通过。本文详细分析退款通知的特殊性,包括req_info字段的AES-256-ECB解密流程、MD5签名与HMAC-SHA256签名的区别,并给出完整的验签与解密代码示例,帮助你快速定位并解决这个常见的回调验签问题。

微信支付的回调通知体系里,普通支付结果通知和退款结果通知走的是两套完全不同的安全机制。不少开发者已经顺利跑通了支付通知的验签,到了退款通知这里却反复踩坑:明明商户证书、API密钥都没问题,验签就是失败。其中一个高频原因是签名类型用错了,退款通知的req_info字段使用的是MD5签名密钥派生的解密密钥,而不是和普通通知一样直接用APIv2密钥做HMAC-SHA256或MD5验签。下面我们把整个机制拆开讲清楚。

微信公众号支付退款结果通知验签失败怎么办?可能是签名类型用错了

一、退款通知和支付通知的安全机制差异

先看普通支付结果通知的验签方式。微信会在返回的XML里附带sign字段,验签时需要把所有非sign字段按key的ASCII码排序,拼接成key1=value1&key2=value2的字符串,再在末尾拼上&key=APIv2密钥,根据sign_type选择MD5或HMAC-SHA256计算摘要,与sign字段比对。这是开发者最熟悉的一套流程。

退款结果通知的结构却不一样。它的XML里只有四个字段:return_codeappidmch_id,以及最关键的req_info。注意这里没有sign字段,也就是说退款通知本身不参与普通的排序验签流程。真正需要验证的是req_info这个加密字段,它的加密流程为:先对商户密钥做MD5得到32位小写字符串,再将该字符串转换为大写,以此作为AES-256-ECB的密钥,对退款结果报文做PKCS7填充后加密,最后Base64编码。

所以验签失败的第一个典型误区就是:拿到退款通知后,仍然按支付通知的老办法对所有字段排序拼接后做MD5比对。由于通知里压根没有sign字段,自然无论如何都验不过。正确的理解是,解密成功本身就等价于验证了数据完整性——因为解密密钥只有持有商户密钥的双方才知道。

二、正确的解密与验证流程详解

解密的完整步骤分为四步。第一步,对商户平台的APIv2密钥执行MD5,得到32位小写摘要;第二步,把这个摘要全部转为大写;第三步,用这32个大写字符作为密钥,对Base64解码后的req_info数据做AES-256-ECB解密;第四步,去掉PKCS7填充,得到UTF-8编码的XML明文,里面包含out_refund_no、refund_status、refund_fee等退款明细字段。

很多语言默认的AES库不会自动去除PKCS7填充,解密出来会在明文末尾出现一串填充字节,导致XML解析失败。这也是被误判为验签失败的另一个常见来源。下面以Java为例给出完整实现:

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Base64;

public class RefundNotifyDecoder {

    /**
     * 解密退款通知中的 req_info 字段
     * @param reqInfo  微信通知里的加密字符串
     * @param apiV2Key 商户平台设置的APIv2密钥
     */
    public static String decrypt(String reqInfo, String apiV2Key) throws Exception {
        // 第一步:对商户密钥做MD5,得到32位小写字符串
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        byte[] digest = md5.digest(apiV2Key.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            String hex = Integer.toHexString(b & 0xFF);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        // 第二步:转为大写,作为AES-256-ECB的密钥
        String aesKey = sb.toString().toUpperCase();

        // 第三步:Base64解码后用AES/ECB/PKCS5Padding解密
        // PKCS5Padding在实现上兼容PKCS7,JDK会自动去除填充
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(aesKey.getBytes("UTF-8"), "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(reqInfo));

        // 第四步:得到UTF-8编码的退款明文XML
        return new String(decrypted, "UTF-8");
    }
}

PHP开发者如果使用openssl系列函数,需要注意openssl_decrypt要显式传入OPENSSL_RAW_DATA标志,否则函数会按Base64处理输入,而我们已经手动解码过了,结果就会出错。密钥同样是通过strtoupper(md5($apiV2Key))生成,这一点三种语言下完全一致。

三、APIv3退款通知为什么又不一样了

如果你的商户号已经切到APIv3,退款通知的验签规则又变了:微信会使用平台证书对通知报文做数字签名,签名算法是RSA-SHA256,签名串的构造是按固定顺序拼接时间戳、随机串和报文体,放在HTTP头Wechatpay-Signature中。验签时要用微信支付平台证书的公钥(不是商户私钥,也不是APIv3密钥),而报文体本身是用APIv3密钥做AES-256-GCM解密的,其中对称解密的key直接就是APIv3密钥,nonce在报文JSON里,additional_data为空字符串。

这三种机制混合使用是最容易出错的场景。排查时可以按下面的清单逐项确认:

  • 确认商户号当前用的是APIv2还是APIv3,两套通知的报文格式完全不同,v2是XML,v3是JSON;
  • v2退款通知不要做排序验签,直接走MD5派生密钥加AES-256-ECB解密流程;
  • 密钥的大写转换不能遗漏,小写MD5直接当AES密钥会抛出InvalidKeyException;
  • 解密后若无法解析XML,检查是否漏掉了PKCS7去填充;
  • v3退款通知验签失败时,优先检查是否误用了商户私钥而不是平台证书公钥。

还有一个容易忽视的点:商户平台修改APIv2密钥后,已发出的退款通知重试仍然用旧密钥加密,如果回调处理程序正好在改密窗口期收到通知,解密会失败。这种情况下应返回失败让微信择机重推,而不是直接丢弃。处理好这些细节,退款通知的验签问题基本都能彻底解决。

微信支付退款通知验签签名验证修改时间:2026-09-01 08:44:50

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。