C#微信支付(六)—— 通知回调

这是重头戏了,修改订单状态一般就是两个地方,一个是前台轮训后发现微信订单完成后修改状态,另一个就是通知回调那了。

通知回调类,修改了下微信Demo代码,兼容了支付和退款两种情况,觉得放一起不好的,想分成两个接口就拆开就好了

    /// <summary>
    /// 支付结果通知回调处理类
    /// 负责接收微信支付后台发送的支付结果并对订单有效性进行验证,将验证结果反馈给微信支付后台
    /// </summary>
    public class ResultNotify : Notify
    {
        public override WxPayData ProcessNotify(HttpContext context)
        {
            WxPayData notifyData = GetNotifyData(context);

            //支付回调
            if (notifyData.IsSet("result_code"))
            {
                #region 验证签名

                try
                {
                    notifyData.CheckSign();//验证签名,不通过会抛异常
                }
                catch (Exception ex)
                {
                    //若签名错误,则立即返回结果给微信支付后台
                    WxPayData res = new WxPayData();
                    res.SetValue("return_code", "FAIL");
                    res.SetValue("return_msg", ex.Message);
                    //Log.Error(this.GetType().ToString(), "Sign check error : " + res.ToXml());
                    context.Response.Write(res.ToXml());
                    return null;
                }

                #endregion

                //检查支付结果中transaction_id是否存在
                if (!notifyData.IsSet("transaction_id"))
                {
                    //若transaction_id不存在,则立即返回结果给微信支付后台
                    WxPayData res = new WxPayData();
                    res.SetValue("return_code", "FAIL");
                    res.SetValue("return_msg", "支付结果中微信订单号不存在");
                    //Log.Error(this.GetType().ToString(), "The Pay result is error : " + res.ToXml());
                    context.Response.Write(res.ToXml());
                }
                else
                {
                    string transaction_id = notifyData.GetValue("transaction_id").ToString();

                    //查询订单,判断订单真实性
                    if (!QueryPayOrder(transaction_id))
                    {
                        //若订单查询失败,则立即返回结果给微信支付后台
                        WxPayData res = new WxPayData();
                        res.SetValue("return_code", "FAIL");
                        res.SetValue("return_msg", "订单查询失败");
                        //Log.Error(this.GetType().ToString(), "Order query failure : " + res.ToXml());
                        context.Response.Write(res.ToXml());
                    }
                    //查询订单成功
                    else
                    {
                        WxPayData res = new WxPayData();
                        res.SetValue("return_code", "SUCCESS");
                        res.SetValue("return_msg", "OK");
                        //Log.Info(this.GetType().ToString(), "order query success : " + res.ToXml());
                        context.Response.Write(res.ToXml());

                        return notifyData;
                    }
                }
            }
            //退款回调
            else if (notifyData.IsSet("req_info"))
            {
                if (!notifyData.IsSet("return_code") || notifyData.GetValue("return_code").ToString() != "SUCCESS" || !notifyData.IsSet("req_info"))
                {
                    WxPayData res = new WxPayData();
                    res.SetValue("return_code", "FAIL");
                    res.SetValue("return_msg", "未返回正确微信支付退款解密信息");
                    context.Response.Write(res.ToXml());
                }

                //信息解密
                string req_info = DecodeReqInfo(notifyData.GetValue("req_info").ToString());
                if (req_info == null)
                {
                    WxPayData res = new WxPayData();
                    res.SetValue("return_code", "FAIL");
                    res.SetValue("return_msg", "解密信息出错");
                    context.Response.Write(res.ToXml());
                }
                else
                {
                    WxPayData res = new WxPayData();
                    res.SetValue("return_code", "SUCCESS");
                    res.SetValue("return_msg", "OK");
                    //Log.Info(this.GetType().ToString(), "order query success : " + res.ToXml());
                    context.Response.Write(res.ToXml());

                    //FormXML会校验签名
                    WxPayData riData = new WxPayData();
                    riData.FromXmlNoCheck(req_info);
                    notifyData.SetValue("req_info", riData);

                    return notifyData;
                }
            }
            else
            {
                WxPayData res = new WxPayData();
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", "未知的通知回调");
                context.Response.Write(res.ToXml());
            }

            return null;
        }

        //查询订单
        private bool QueryPayOrder(string transaction_id)
        {
            WxPayData req = new WxPayData();
            req.SetValue("transaction_id", transaction_id);
            WxPayData res = WxPayApi.OrderQuery(req);
            if (res.GetValue("return_code").ToString() == "SUCCESS" &&
                res.GetValue("result_code").ToString() == "SUCCESS")
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        #region 退款解密

        /// <summary>
        /// AES-256-ECB字符解密
        /// </summary>
        /// <param name="s">要解密字符串</param>
        /// <param name="key">密钥</param>
        /// <returns></returns>
        public static string DecodeAES256ECB(string s, string key)
        {
            string r = null;
            try
            {
                byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
                byte[] toEncryptArray = Convert.FromBase64String(s);
                RijndaelManaged rDel = new RijndaelManaged();
                rDel.Key = keyArray;
                rDel.Mode = CipherMode.ECB;
                rDel.Padding = PaddingMode.PKCS7;
                ICryptoTransform cTransform = rDel.CreateDecryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
                r = UTF8Encoding.UTF8.GetString(resultArray);
            }
            catch { }
            return r;
        }

        /// <summary>
        /// 解密微信支付退款结果通知
        /// </summary>
        /// <param name="s">要解密字符串</param>
        /// <returns></returns>
        public static string DecodeReqInfo(string s)
        {
            string r = null;
            string key = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(Config.Key, "md5").ToLower();
            r = DecodeAES256ECB(s, key);
            return r;
        }
        #endregion
    }

回调基类,基础帮助类 里面也有写到

    /// <summary>
    /// 回调处理基类
    /// 主要负责接收微信支付后台发送过来的数据,对数据进行签名验证
    /// 子类在此类基础上进行派生并重写自己的回调处理过程
    /// </summary>
    public class Notify
    {
        /// <summary>
        /// 接收从微信支付后台发送过来的数据并验证签名
        /// </summary>
        /// <returns>微信支付后台返回的数据</returns>
        public WxPayData GetNotifyData(HttpContext context)
        {
            //接收从微信后台POST过来的数据
            System.IO.Stream s = context.Request.InputStream;
            int count = 0;
            byte[] buffer = new byte[1024];
            StringBuilder builder = new StringBuilder();
            while ((count = s.Read(buffer, 0, 1024)) > 0)
            {
                builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
            }
            s.Flush();
            s.Close();
            s.Dispose();

            //Log.Info(this.GetType().ToString(), "Receive data from WeChat : " + builder.ToString());

            //转换数据格式, 取消签名校验,签名调用方自己调用,兼容支付和退款
            WxPayData data = new WxPayData();
            try
            {
                //FormXML会校验签名
                data.FromXmlNoCheck(builder.ToString());
            }
            catch (Exception ex)
            {
                //若签名错误,则立即返回结果给微信支付后台
                WxPayData res = new WxPayData();
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", ex.Message);
                //Log.Error(this.GetType().ToString(), "Sign check error : " + res.ToXml());
                context.Response.Write(res.ToXml());
                context.Response.End();
            }

            //Log.Info(this.GetType().ToString(), "Check sign success");
            return data;
        }

        //派生类需要重写这个方法,进行不同的回调处理
        public virtual WxPayData ProcessNotify(HttpContext context)
        {
            return null;
        }
    }

具体接口调用

        public void ProcessRequest(HttpContext context)
        {
            ResultNotify resultNotify = new ResultNotify();
            WxPayData notifyData = resultNotify.ProcessNotify(context);

            if (notifyData != null)
            {
                //支付回调
                if (notifyData.IsSet("result_code"))
                {
                    //TODO:核对订单金额与微信订单是否一致

                    //TODO:核对订单金额与系统订单是否一致

                    //TODO:核对订单号,系统订单号,商户订单号三者是否一致

                    //TODO:修改订单状态

                    //TODO:记录日志
                }
                //退款回调
                else if (notifyData.IsSet("req_info"))
                {
                    //TODO:核对退款金额与系统退款是否一致

                    //TODO:核对订单号,系统订单号,商户订单号三者是否一致

                    //TODO:修改订单状态

                    //TODO:记录日志
                }
            }
        }

一定要核对下订单金额和订单号,并且返回结果给微信,不然前者会有假冒数据,后者会让微信几分钟内一直发请求给你。。。。。

解密那有点坑,解密的流程有点繁琐,遇到好几次错误才通过了。其他的没啥想到的,感觉出错都是自己代码问题,微信那好像没什么坑,另外这个回调并不是即时发送的,有延迟的,微信那边人多的时候,有一次30多秒才收到回调。。。所以实际开发的时候,最好轮训和回调都要有。

猜你喜欢

转载自blog.csdn.net/qwt7896541230/article/details/82697449