微信支付[v3]

原文:微信支付[v3]

V2升级V3 顺便记录一下 ,文档: http://pay.weixin.qq.com/wiki/doc/api/index.html

!!!

  • 支付授权目录与测试人的微信帐号白名单(出现access_denied或access_not_allow错误,请检查是否设置正确)
  • 微信签名(用于jssdk调用支付,查询订单,异步参数签名等)
  • 数字证书,用于微信退款

参数签名

对所有传入参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL 键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1,注意:值为空的参数不参与签名 在string1 最后拼接上key=Key( 商户支付密钥) 得到stringSignTemp 字符串, 并对stringSignTemp 进行md5 运算,再将得到的字符串所有字符转换为大写,得到sign 值signValue

        /// <summary>
        /// 创建微信Sign
        /// </summary>
        /// <param name="key">微信商户支付密钥</param>
        /// <param name="dict">参数字典</param>
        /// <returns></returns>
        public string GenerateWxSign(string key, SortedDictionary<string, string> dict)
        {
            try
            {
                string str = string.Empty;
                foreach (var item in dict)
                {
                    if (item.Key != "sign" && item.Value.ToString() != "")
                    {
                        str += item.Key + "=" + item.Value + "&";
                    }
                }
                str = str.Trim('&');
                //在string后加入API KEY
                str += "&key=" + key;
                //MD5加密
                var md5 = System.Security.Cryptography.MD5.Create();
                var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
                var sb = new StringBuilder();
                foreach (byte b in bs)
                {
                    sb.Append(b.ToString("x2"));
                }
                //所有字符转为大写
                return sb.ToString().ToUpper();
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

异步通知

基本按照文档来,返回的参数要使用流的方式来读取XML,另外验证参数的合法性可以验证签名或调用一次微信订单查询接口

    /// <summary>
    /// 微信支付异步回调控制器【V3版本】
    /// </summary>
    public class WeixinController : Controller
    {
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 订单服务
        /// </summary>
        private readonly IOrderService _orderService;

        /// <summary>
        /// 微信服务
        /// </summary>
        private readonly IWeixinService _weixinService;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="orderService">订单服务</param>
        /// <param name="weixinService">微信服务</param>
        public WeixinController(IOrderService orderService, IWeixinService weixinService)
        {
            _orderService = orderService;
            _weixinService = weixinService;
        }

        /// <summary>
        /// 微信Wap支付异步通知
        /// </summary>
        /// <returns></returns>
        public async Task<ActionResult> Notify()
        {
            int intLen = Convert.ToInt32(Request.InputStream.Length);
            if (intLen <= 0)
            {
                return Content(AjaxResult.Error("WeixinController NO Wap Notify PARAMS!").ToStringSafe());
            }
            try
            {
                var dict = new SortedDictionary<string, string>();
                //dict = @"appid=wxqe353dd942e01&attach=F&bank_type=CFT&cash_fee=1&fee_type=CNY&is_subscribe=Y&mch_id=10036766&nonce_str=e9605038cccbffe5ed2b2eb095345d18&openid=oIO3gjsiGqv7eeuAKhbB_V83V528&out_trade_no=68047857&result_code=SUCCESS&return_code=SUCCESS&sign=85A469F874619EAB924FA5B7EB779444&time_end=20150526183632&total_fee=1&trade_type=JSAPI&transaction_id=1001640542201505260168707842".ConvertStringToSortedDictionary();
                var xmlDoc = new XmlDocument();
                xmlDoc.Load(Request.InputStream);
                var root = xmlDoc.SelectSingleNode("xml");
                XmlNodeList xnl = root.ChildNodes;
                foreach (XmlNode xnf in xnl)
                {
                    dict.Add(xnf.Name, xnf.InnerText);
                }
                if (dict.Count <= 0)
                {
                    return Content(AjaxResult.Error("NO Notify PARAMS!").ToStringSafe());
                }
                logger.Info("【 WeixinController Wap Notify SDKUtil.ConvertDictionaryToString : 请求报文=[" + dict.ConvertDictionaryToString() + "]\n");
                //验证参数签名
                string signK = _weixinService.GenerateWxSign(WxPayConfig.KEY, dict);
                if (signK != dict["sign"])
                {
                    logger.Info("WeixinController Wap Notify Verify WxSign Error : 请求报文=[signK " + signK + " : dict['sign'] " + dict["sign"] + "]\n");
                    return Content(AjaxResult.Error("WeixinController Wap Notify Verify WxSign Error " + "]\n").ToStringSafe());
                }
                //验证通信标识
                string return_code = dict["return_code"];
                if (!return_code.Equals("SUCCESS", StringComparison.OrdinalIgnoreCase))
                {
                    string return_msg = dict["return_msg"];
                    logger.Info("WeixinController Wap Notify return_code Error : 请求报文=[" + return_code + " : " + return_msg + "]\n");
                    return Content(AjaxResult.Error("WeixinController Wap Notify return_code Error " + return_code + "]\n").ToStringSafe());
                }
                //验证交易标识
                string result_code = dict["result_code"];
                if (!result_code.Equals("SUCCESS", StringComparison.OrdinalIgnoreCase))
                {
                    string err_code = dict["err_code"];
                    string err_code_des = dict["err_code_des"];
                    logger.Info("WeixinController Wap Notify return_code Error : 请求报文=[" + err_code + " : " + err_code_des + "]\n");
                    return Content(AjaxResult.Error("WeixinController Wap Notify return_code Error " + result_code + "]\n").ToStringSafe());
                }
                //公众账号ID
                string appid = dict["appid"];
                //商户号
                string mch_id = dict["mch_id"];
                //随机字符串
                string nonce_str = dict["nonce_str"];
                //签名
                string sign = dict["sign"];
                //用户在商户appid 下的唯一标识
                string openid = dict["openid"];
                //用户是否关注公众账
                string is_subscribe = dict["is_subscribe"];
                //交易类型
                string trade_type = dict["trade_type"];
                //付款银行
                string bank_type = dict["bank_type"];
                //订单总金额,单位为分
                string total_fee = dict["total_fee"];
                //金额分转元
                string total_fee_rmb = (float.Parse(total_fee) / 100).ToString();
                //商户订单号
                string out_trade_no = dict["out_trade_no"];
                //微信支付订单号
                string transaction_id = dict["transaction_id"];
                //商家数据包
                string attach = dict["attach"];
                //支付完成时间
                string time_end = dict["time_end"];
                //支付方式
                string paymethod = attach;
               //微信订单查询【使用签名验证废弃】
                //var wxorder = await _weixinService.Query(WxPayConfig.KEY, WxPayConfig.APPID, WxPayConfig.MCHID, transaction_id, out_trade_no);
               //检查订单是否已经支付
                 var order = await _orderService.Query(out_trade_no);
               if (order.IsNull() || order.Total_fee.IsNotNullOrEmpty()){
                    return Content(AjaxResult.Error("ORDERNO STATUS ERROR !!!").ToStringSafe());
               }
                //更新支付金额与订单状态
                 var orderFlag = await _orderService.Update(crsResv);
                if (orderFlag.IsError)
                {
                    logger.Fatal("WeixinController Wap Notify  Update Order Error  crsResv : " + crsResv.SerializeJson(System.Text.Encoding.UTF8));
                    return Json(new { IsError = true, Msg = "更新订单数据错误", Data = string.Empty }, JsonRequestBehavior.AllowGet);
                }
                //微信支付发货通知
                 var wxNotify = await _weixinService.WeixinNotify(openid, transaction_id, out_trade_no);
                //记录DB
                var save = await _weixinService.Save(wxAsync);
                //构造返回成功XML
                var wxdict = new SortedDictionary<string, string>();
                wxdict.Add("return_code", "SUCCESS");
                wxdict.Add("return_msg", "PAY_SUCCESS");
                string wxRXml = wxdict.ConvertWxDictToString();
                logger.Info("WeixinController Wap Notify  Success wxRXml : " + wxRXml + " 】");
                return Content(wxRXml);
            }
            catch (Exception ex)
            {
                logger.Fatal("WeixinController Wap Notify Exception : " + ex.Message + " 】", ex);
                return Content(AjaxResult.Error("WeixinController Wap Notify Exception").ToStringSafe());
            }
        }

        /// <summary>
        /// 字典转微信返回XML
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        /// <param name="wxdict"></param>
        /// <returns></returns>
        public static string ConvertWxDictToString<TKey, TValue>(this IDictionary<TKey, TValue> wxdict)
        {
            try
            {
                string xml = "<xml>";
                wxdict.ForEach(wd =>
                {
                    if (wd.Value.GetType() == typeof(int))
                    {
                        xml += "<" + wd.Key + ">" + wd.Value + "</" + wd.Key + ">";
                    }
                    else if (wd.Value.GetType() == typeof(string))
                    {
                        xml += "<" + wd.Key + ">" + "<![CDATA[" + wd.Value + "]]></" + wd.Key + ">";
                    }
                });
                xml += "</xml>";
                return xml;
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

订单查询

        /// <summary>
        /// 微信订单查询
         /// </summary>
        /// <param name="appid">公众账号ID</param>
        /// <param name="mch_id">商户号</param>
        /// <param name="transaction_id">微信的订单号</param>
        /// <param name="out_trade_no">商户订单号</param>
        /// <param name="nonce_str">随机字符串</param>
        /// <param name="sign">签名</param>
        /// <returns></returns>
        public async Task<WebAPIResponse> Query(string appid, string mch_id, string transaction_id, string out_trade_no, string nonce_str, string sign)
        {
            if (appid.IsEmpty() || mch_id.IsEmpty() || transaction_id.IsEmpty())
            {
                return new WebAPIResponse { IsError = true, Msg = "参数有误!!!" };
            }
            try
            {
                var dict = new Dictionary<string, string>();
                dict.Add("appid", appid);
                dict.Add("mch_id", mch_id);
                dict.Add("transaction_id", transaction_id);
                dict.Add("out_trade_no", out_trade_no);
                dict.Add("nonce_str", nonce_str);
                dict.Add("sign", sign);
                string xml = "<xml>";
                foreach (var pair in dict)
                {
                    if (pair.Value.GetType() == typeof(int))
                    {
                        xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">";
                    }
                    else if (pair.Value.GetType() == typeof(string))
                    {
                        xml += "<" + pair.Key + ">" + "<![CDATA[" + pair.Value + "]]></" + pair.Key + ">";
                    }
                }
                xml += "</xml>";
                logger.Info("WeixinRepository WeixinNotify Req xml: " + xml);
                var data = await @"https://api.mch.weixin.qq.com/pay/orderquery".PostStringAsync(xml).ReceiveString();
                logger.Info("WeixinRepository WeixinNotify Resp xml: " + data);
                if (data.IsNotNullOrEmpty() && data.Contains("SUCCESS", StringComparison.OrdinalIgnoreCase))
                {
                    return new WebAPIResponse { IsError = false, Data = "查询订单成功!!!" };
                }
                return new WebAPIResponse { IsError = true, Msg = "微信订单查询失败!!!" };
            }
            catch (Exception ex)
            {
                logger.Error("WeixinRepository WeixinNotify ConvertDictionaryToJson Exception :" + ex.Message, ex);
                return new WebAPIResponse { IsError = true, Msg = ex.Message };
            }
        }

微信退款

!!!需注意签名与证书

        /// <summary>
        /// 微信申请退款V3
        /// </summary>
        /// <param name="orderNo">订单号</param>
        /// <returns></returns>
        public bool RefundWxV3(string orderNo)
        {
            try
            {
                //查询支付信息
                  var orderAsync =await _orderService.Query(orderNo);
                var dict = new SortedDictionary<string, string>();
                dict.Add("appid", WxPayConfig.APPID);
                dict.Add("mch_id", WxPayConfig.MCHID);
                dict.Add("nonce_str", ConstantKey.NonceStr);
                dict.Add("transaction_id", orderAsync.transaction_id);
                dict.Add("out_trade_no", orderAsync.out_trade_no);
                dict.Add("out_refund_no", WxPayConfig.GenerateOutTradeNo);
                dict.Add("total_fee", orderAsync.total_fee);
                dict.Add("refund_fee", orderAsync.total_fee);
                dict.Add("op_user_id", WxPayConfig.MCHID);
                string sign = GenerateWxSign(WxPayConfig.KEY, dict);
                dict.Add("sign", sign);
                string xml = "<xml>";
                foreach (var pair in dict)
                {
                    if (pair.Value.GetType() == typeof(int))
                    {
                        xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">";
                    }
                    else if (pair.Value.GetType() == typeof(string))
                    {
                        xml += "<" + pair.Key + ">" + "<![CDATA[" + pair.Value + "]]></" + pair.Key + ">";
                    }
                }
                xml += "</xml>";
                logger.Info("RefundWxV3 Req xml: " + xml);
                var data = HttpWxCert(@"https://api.mch.weixin.qq.com/secapi/pay/refund", "POST", xml, "text/xml", isUseCert: true);
                logger.Info("RefundWxV3 Resp xml: " + data);
                if (data.IsNotEmpty() && data.Contains("SUCCESS", StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
                return false;
            }
            catch (Exception ex)
            {
                logger.Error("CancelResvForWeb RefundWxV3 Exception :" + ex.Message, ex);
                return false;
            }
        }
 /// <summary>
        /// 创建HTTP请求
        /// </summary>
        /// <param name="url">请求URL</param>
        /// <param name="method">请求方式</param>
        /// <param name="data">请求参数</param>
        /// <param name="contentType">设置HTTP contentType类型</param>
        /// <param name="isUseCert">是否使用证书</param>
        /// <param name="timeout">请求超时时间</param>
        /// <returns></returns>
        public static string HttpWxCert(string url, string method = "GET", string data = null, string contentType = "application/x-www-form-urlencoded", bool isUseCert = false, int timeout = 15000)
        {
            if (url.IsEmpty())
                throw new ArgumentNullException("url");
            HttpWebRequest req = null;
            HttpWebResponse resp = null;
            StreamReader sr = null;
            Stream stream = null;
            try
            {
                if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
                {
                    System.Net.ServicePointManager.ServerCertificateValidationCallback += (se, cert, chain, sslError) =>
                    {
                        return true;
                    };
                }
                //设置最大连接数
                System.Net.ServicePointManager.DefaultConnectionLimit = 200;
                req = (HttpWebRequest)WebRequest.Create(url);
                req.Method = method.ToUpper();
                req.Timeout = timeout;
                req.ContentType = contentType;
                //是否使用证书
                if (isUseCert)
                {
                    string pfxPath = WxPayConfig.SSLCERT_PATH;
                    logger.Info("wxV3.signCert.path pfxPath : " + pfxPath + " File Exists: " + File.Exists(pfxPath));
                    //string path = HttpContext.Current.Request.PhysicalApplicationPath;
                    //调用证书
                    var cert = new X509Certificate2(pfxPath, WxPayConfig.SSLCERT_PASSWORD, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
                    req.ClientCertificates.Add(cert);
                }

                if (method.ToUpper() == "POST" && !data.IsEmpty())
                {
                    byte[] postBytes = UTF8Encoding.UTF8.GetBytes(data);
                    req.ContentLength = postBytes.Length;
                    using (stream = req.GetRequestStream())
                    {
                        stream.Write(postBytes, 0, postBytes.Length);
                    }
                }
                resp = (HttpWebResponse)req.GetResponse();
                using (sr = new StreamReader(resp.GetResponseStream(), Encoding.UTF8))
                {
                    return sr.ReadToEnd().Trim();
                }
            }
            catch (Exception ex)
            {
                logger.Error(string.Format("HttpWxCert Exception url: {0} \r\n data: {1}", url, data), ex);
                return string.Empty;
            }
            finally
            {
                if (sr != null)
                {
                    sr.Close();
                    sr = null;
                }
                if (stream != null)
                {
                    stream.Close();
                    stream = null;
                }
                if (resp != null)
                {
                    resp.Close();
                    resp = null;
                }
                if (req != null)
                {
                    req.Abort();
                    req = null;
                }
            }
        }
时间: 2024-11-05 21:57:35

微信支付[v3]的相关文章

到处都是坑的微信支付V3

业务需要一个在微信上能付款的功能,于是乎想到了最普遍的支付宝,坑爹的是T与A是水火不容啊,默默的还是接微信支付吧,没想到从此掉进了连环坑-- 网上写微信支付接口的还是很多,PHP官方有(鄙视源码作者,连参数对应都错了,能跑通才怪),NET版 微信支付--入门篇 这篇算是比较全的,java版. PHP的只能参考,终究还是先按NET版的还创建项目,不过文章里面也没具体写到如何前后台交互(偶用的MVC),对于初次接触的人来说只能问题来一个解决一个. 至于怎么从0开发就不用详细说明了,参考这位仁兄的 微

到处都是坑的微信支付V3之 微信支付回调页面

据上次 到处都是坑的微信支付V3 后很多园友在被虐了千百遍后终于跳转到了亲切的微信支付界面,但输入密码支付后却不知道怎么处理了,接下来补上支付后的处理流程. 1. html中根据前台支付后反馈信息成功与否跳转到相关页面 if (res.err_msg == "get_brand_wcpay_request:ok") { // message: "微信支付成功!", window.location.replace("@Url.Content("~/

android端微信支付V3版本地签名统一下单详解_Android

满满的都是坑,因为服务器偷懒让客服端写统一下单,服务器只给了通知的url.微信的支付demo并没有统一下单的代码. 读此文前先阅读: https://pay.weixin.qq.com/wiki/doc/api/app/app.PHP?chapter=9_1 一步步的来  先根据统一下单的参数介绍工具: 1. 获取到当前的ip: <span style="font-size:14px;">public String getLocalIpAddress() { try { f

android端微信支付V3版本地签名统一下单详解

满满的都是坑,因为服务器偷懒让客服端写统一下单,服务器只给了通知的url.微信的支付demo并没有统一下单的代码. 读此文前先阅读: https://pay.weixin.qq.com/wiki/doc/api/app/app.PHP?chapter=9_1 一步步的来  先根据统一下单的参数介绍工具: 1. 获取到当前的ip: <span style="font-size:14px;">public String getLocalIpAddress() { try { f

微信支付java版V3验证数据合法性(Deom)_java

1.1 解析微信回调数据 InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } o

android微信支付问题总结

首先我们先看一个android微信支付时遇到的一个错误提示: onPayFinish, errCode = -1  当你参数签名都没有问题的时候,出现这个提示,请按照如下操作: 在你的项目测试android微信的组件(微信分享.微信支付等)的时候,一定要用你自己的keystore签名出来测试,如果用debug.keystore肯定是不成功的! 本文部分内容转自:http://blog.csdn.net/baidu_17508977/article/details/44517283 1,在微信开放

微信支付开发教程

  本文介绍微信支付v2和v3版的微信支付开发教程,来源于微信公众平台官方网站. 原文:http://www.cnblogs.com/txw1958/p/wxpay-development-document.html   前言 微信支付现在分为v2版和v3版,2014年9月10号之前申请的为v2版,之后申请的为v3版.V3版的微信支付没有paySignKey参数.   一.微信支付v2  1. 使用网页授权接口 使用网页授权接口获取用户的基本信息. [微信公众号]OAuth2.0授权.pdf点击

微信支付开发(8) 刷卡支付

关键字:微信支付 微信支付v3 刷卡支付 统一支付 prepay_id 作者:方倍工作室原文: http://www.cnblogs.com/txw1958/p/wxpayv3-micropay.html    本文介绍微信支付下的刷卡支付的开发过程.刷卡支付是指用户打开微信钱包的刷卡的界面,商户扫码后提交完成支付的支付过程.     一.刷卡支付API 接口地址 https://api.mch.weixin.qq.com/pay/micropay 是否需要证书 不需要. 输入参数 名称 变量名

关于微信支付的支付授权目录是如何定义的

问题描述 最近在做微信支付v3版的开发,在测试时报access_control:not_allow.查了资料发现应该是支付授权目录不对的原因.这里请教下:我的页面是在WEB-INF目录下的,那么我的支付授权目录是否应该包括WEB-INF这一级?因为WEB-INF下的页面客户端是无法直接访问的,需要由后台跳解析转到.那么这里再衍生出一个问题,由于微信支付本身是JS方式的Ajax请求,因此它判断来路页面就是支付页面URL,而这个URL就应该是微信浏览器的URL,也就是说支付授权目录是根据微信浏览器的