JeffreySu / WeiXinMPSDK

微信全平台 .NET SDK, Senparc.Weixin for C#,支持 .NET Framework 及 .NET Core、.NET 8.0。已支持微信公众号、小程序、小游戏、微信支付、企业微信/企业号、开放平台、JSSDK、微信周边等全平台。 WeChat SDK for C#.
https://weixin.senparc.com
Apache License 2.0
8.43k stars 4.35k forks source link

调用统一下单接口包含非英文字符时报错 `签名错误` #572

Closed snowfoxzx closed 7 years ago

snowfoxzx commented 7 years ago
问题描述

调用统一下单接口时,如果参数中含有 非英文字符 (如body="微信支付"),微信会返回 签名错误。 查看 Developer-FullDotNet 分支下 Senparc.Weixin.MP.TenPayLibV3.RequestHandler.GetCharset() 方法,估计此方法应该是返回空字符串或 null,导致调用 Senparc.Weixin.Helpers.EncryptHelper.GetMD5() 方法时使用了默认的 GB2312 编码,而微信官方明确要求字符编码使用 UTF-8 不知道 GetCharset() 方法中的逻辑是有什么其它考虑吗?

临时解决方法,下单时不要使用非英文字符。。。

发现问题的模块
模块对应的.net版本
开发环境
缓存环境
JeffreySu commented 7 years ago

这个类是腾讯提供的,我们进行了一些修改,但是这个地方一直没有动,现在已经将Core版本的强制改为UTF8:

        public virtual string CreateMd5Sign(string key, string value)
        {
            StringBuilder sb = new StringBuilder();

            ArrayList akeys = new ArrayList(Parameters.Keys);
            akeys.Sort();

            foreach (string k in akeys)
            {
                string v = (string)Parameters[k];
                if (null != v && "".CompareTo(v) != 0
                    && "sign".CompareTo(k) != 0
                    //&& "sign_type".CompareTo(k) != 0
                    && "key".CompareTo(k) != 0)
                {
                    sb.Append(k + "=" + v + "&");
                }
            }

            sb.Append(key + "=" + value);
            //string sign = MD5UtilHelper.GetMD5(sb.ToString(), GetCharset()).ToUpper();
            string sign = MD5UtilHelper.GetMD5(sb.ToString(), "UTF-8").ToUpper();

            return sign;
        }

您看一下这个是不是您要的结果。

或者能不能给一组您那边的数据,方便我们做一个单元测试?

snowfoxzx commented 7 years ago

@JeffreySu 这样处理就 OK 了,不过我觉得问题的根源应该是在 GetCharset() 方法中,不知道腾讯这样处理是出于什么考虑 另附一个单元测试,还有,NETCore 下使用 GB2312GBK 等编码时需要引用 System.Text.Encoding.CodePages 并注册一下,注释里有写

[TestClass]
public class SenparcTest
{
    [TestInitialize]
    public void Setup()
    {
        // .NET Core 下使用 GBK, GB2312 等编码时, 
        // 必须 NuGet 添加 System.Text.Encoding.CodePages
        // 并执行以下语句注册, 否则运行时会报错 `No data is available for encoding xxxx`
        // 或 `'xxxx' is not a supported encoding name. `
        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
    }

    [TestMethod]
    public void TestUnifiedorderSign()
    {
        var appId = "wx0000000000000000";
        var mchId = "1234567890";
        var body = "微信支付"; // 如果此处改为英文则不会出现 `签名错误`
        var billNo = "1234567890_1495442415";
        var price = 8866;
        var billIp = "127.0.0.1";
        var notifyUrl = "http://abc.123.org/wechat";
        var payType = TenPayV3Type.JSAPI;
        var openId = "oEhblwEDhUTz1Sj5qnPo6p_PbUrc";
        var mchKey = "e64fc5e5139d393311b645dc1fceed0c";
        var nonceStr = "2271A353370C61225EB2588D3F1DEB14";
        var timeStart = new DateTime(2017, 5, 23, 11, 12, 0);

        var expectedSign = "A94C30EFE9893844B2EC6D875E2FE413";

        var reqData = new TenPayV3UnifiedorderRequestData(appId, mchId, body, billNo, price,
            billIp, notifyUrl, payType, openId, mchKey, nonceStr, null, timeStart);

        Assert.AreEqual(expectedSign, reqData.Sign, false, "签名错误");
    }
}
JeffreySu commented 7 years ago

非常感谢!

腾讯的代码估计是哪里抄的吧,比较通用,他获取的是当前通讯请求过程中的字符编码,我们也疏忽没注意这个问题。。