那些年,微信公众号支付爬过的坑

微信公众号内支付文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6


下面来说说开发过程中需要注意的地方:

1.微信支付接口老版和旧版不兼容,一旦在微信公众号平台微信支付升级了支付接口,那么用老版支付接口开发的程序都会不好用。。所以升级一定要谨慎。。

(我们公司的公众号h5微信支付突然有一天不好用了,于是开始疯狂的找bug,结果找了半天发现是不知道谁把支付接口给升级了,,没办法只能用新接口重新开发,真的是坑的一逼再见


2.开发工程中一定要仔细阅读文档的各个字眼,, 一点错误可能就导致无法支付。。


下面举几个栗子:

参数名字一定要和文档中的完全一致

----------------------------------------------------------------------------------------------- 分割线



----------------------------------------------------------------------------------------------- 分割线

这里一定要格外注意,一个步骤出错都会导致报错签名错误。


----------------------------------------------------------------------------------------------- 分割线



注意是10位的。。位数错误也会导致支付报错。



注意新版的只支持MD5加密 ,老版的好像还可以用sha1加密方式,新版的不行了




这个在统一下单接口中,作用是微信支付成功后会带上一些参数回调该地址,来通知支付结果等信息,回调地址一定要填写正确。


----------------------------------------------------------------------------------------------- 分割线


下面附上部分代码:

/// <summary>
    /// 签名工具类
    /// </summary>
    public class RequestHandler
    {

        public RequestHandler(HttpContextBase httpContext)
        {
            parameters = new Hashtable();

            this.httpContext = httpContext;

        }

        /**  密钥 */
        private string key;

        protected HttpContextBase httpContext;

        /** 请求的参数 */
        protected Hashtable parameters;

        /** debug信息 */
        private string debugInfo;

        /** 初始化函数 */
        public virtual void init()
        {
            setKey("商户平台设置的密钥key");
        }
        /** 获取debug信息 */
        public String getDebugInfo()
        {
            return debugInfo;
        }
        /** 获取密钥 */
        public String getKey()
        {
            return key;
        }

        /** 设置密钥 */
        public void setKey(string key)
        {
            this.key = key;
        }

        /** 设置参数值 */
        public void setParameter(string parameter, string parameterValue)
        {
            if (!string.IsNullOrEmpty(parameter))
            {
                if (parameters.Contains(parameter))
                {
                    parameters.Remove(parameter);
                }

                parameters.Add(parameter, parameterValue);
            }
        }



        //创建md5加密Sign,规则是:ASCII码从小到大排序(字典序),遇到空值的参数不参加签名。

        public virtual string CreateMd5Sign()
        {
            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 && "key".CompareTo(k) != 0)
                {
                    sb.Append(k + "=" + v + "&");
                }
            }
            //sign=除sign参数外的所有不为空值的发送参数+key ,在MD5加密(key为商户平台设置的密钥)
            sb.Append("key=" + this.getKey());
            string sign = MD5Util.GetMD5(sb.ToString(), getCharset()).ToUpper();

            //debug信息 可记录到日志方便查看
            this.setDebugInfo(sb.ToString() + " => sign:" + sign);

            return sign;

        }    

        //输出XML
        public string parseXML()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<xml>");
            foreach (string k in parameters.Keys)
            {
                string v = (string)parameters[k];
                if (Regex.IsMatch(v, @"^[0-9.]$"))
                {

                    sb.Append("<" + k + ">" + v + "</" + k + ">");
                }
                else
                {
                    sb.Append("<" + k + "><![CDATA[" + v + "]]></" + k + ">");
                }

            }
            sb.Append("</xml>");
            return sb.ToString();
        }

        /// <summary>
        /// 把XML数据转换为SortedDictionary<string, string>集合
        /// </summary>
        /// <param name="strxml"></param>
        /// <returns></returns>
        public SortedDictionary<string, string> GetInfoFromXml(string xmlstring)
        {
            SortedDictionary<string, string> sParams = new SortedDictionary<string, string>();
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xmlstring);
                XmlElement root = doc.DocumentElement;
                int len = root.ChildNodes.Count;
                for (int i = 0; i < len; i++)
                {
                    string name = root.ChildNodes[i].Name;
                    if (!sParams.ContainsKey(name))
                    {
                        sParams.Add(name.Trim(), root.ChildNodes[i].InnerText.Trim());
                    }
                }
            }
            catch { }
            return sParams;
        }



        /** 设置debug信息 */
        public void setDebugInfo(String debugInfo)
        {
            this.debugInfo = debugInfo;
        }

        public Hashtable getAllParameters()
        {
            return this.parameters;
        }

        protected virtual string getCharset()
        {
            return this.httpContext.Request.ContentEncoding.BodyName;
        }
    }


        private String packageValue = string.Empty; 

        /// <summary>
        /// 获取支付接口需要的Package
        /// </summary>
        /// <returns></returns>
        private string GetPackageByRequest(string orderId, float totalPrice, string remarks)
        {
            string open_id = ""; //统一下单接口需要的openid,获取方式可看文档

            //创建支付应答对象

            RequestHandler packageReqHandler = new RequestHandler(HttpContext);
            //初始化

            packageReqHandler.init();

            //设置统一下单参数
            packageReqHandler.setParameter("appid", appid);
            packageReqHandler.setParameter("trade_type", "JSAPI");
            packageReqHandler.setParameter("body", remarks); //商品描述
            packageReqHandler.setParameter("mch_id","123456");		  //商户号
            packageReqHandler.setParameter("nonce_str", TenpayUtil.Noncestr);


            packageReqHandler.setParameter("out_trade_no", orderId);		//商家订单号



            packageReqHandler.setParameter("total_fee", (1*100).ToString());	//商品金额,以分为单位(money * 100).ToString()
            packageReqHandler.setParameter("fee_type", "CNY");                    //币种,CNY人民币



            packageReqHandler.setParameter("notify_url", "http://wwww.xxx.com/xxxx");      //接收微信支付结果通知的URL
            packageReqHandler.setParameter("spbill_create_ip", Request.UserHostAddress);   //用户的公网ip,不是商户服务器IP
            
            packageReqHandler.setParameter("openid", open_id);

            packageReqHandler.setParameter("sign_type", "MD5");
            packageReqHandler.setParameter("sign", packageReqHandler.CreateMd5Sign()); //签名

            packageValue = getPrepayId(packageReqHandler);
            if (!string.IsNullOrEmpty(packageValue))
            {
                packageValue = "prepay_id=" + packageValue;
            }

            return packageValue;
        }

 /// <summary>
        /// 获取prepay_id
        /// </summary>
        public string getPrepayId(RequestHandler packageReqHandler)
        {
            string UnifiedPayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //统一下单接口链接
            string prepay_id = "";
            string post_data = packageReqHandler.parseXML(); //把发送参数转换为xml格式
            string request_data = CommHelper.PostUrl(UnifiedPayUrl, post_data); //发送post请求
            SortedDictionary<string, string> requestXML = packageReqHandler.GetInfoFromXml(request_data);
            foreach (KeyValuePair<string, string> k in requestXML)
            {
                if (k.Key == "prepay_id")
                {
                    prepay_id = k.Value;
                    break;
                }
            }
            return prepay_id;
        }

 /// <summary>
        /// 设置支付接口需要的参数发送到前台使用(我这里是前台ajax请求获取)
        /// </summary>
        public JsonResult WeixinPay()
        {
            dynamic model = new ExpandoObject();

            string sign = Request["sign"]; //加密验证,保证程序安全性
            if (!sign.Equals("自己设置的加密字符串"))
            {
                model.Error = "验证失败";
                return Json(model, JsonRequestBehavior.AllowGet);
            }
            model.appId = "appid";
            model.timeStamp = GetTimestamp();
            model.nonceStr = GetNoncestr();
            model.packageValue = GetPackageByRequest("商户订单号", "商品金额", "商品描述");
            model.signType = "MD5";
            model.paySign = GetSign(model.nonceStr, model.timeStamp);

            return Json(model, JsonRequestBehavior.AllowGet);
        }
        
        /// <summary>
        /// 支付Sign
        /// </summary>
        /// <returns></returns>
        private string GetSign(string nonceStr, string timeStamp)
        {
            //设置支付接口参数
            RequestHandler paySignReqHandler = new RequestHandler(HttpContext);
            paySignReqHandler.setParameter("appId", "appid");
            paySignReqHandler.setParameter("nonceStr", nonceStr);
            paySignReqHandler.setParameter("timeStamp", timeStamp);
            paySignReqHandler.setParameter("package", packageValue);
            paySignReqHandler.setParameter("signType", "MD5");

            paySignReqHandler.init();


            string paySign = paySignReqHandler.CreateMd5Sign();

            //获取debug信息可纪录到日志方便查看
            string paySignDebuginfo = paySignReqHandler.getDebugInfo();

            return paySign;
        }

        /// <summary>
        /// 获取10位的时间戳
        /// </summary>
        /// <returns></returns>
        private static string GetTimestamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            string timeStamp = Convert.ToInt64(ts.TotalSeconds).ToString();

            return timeStamp;
        }

        /// <summary>
        /// 获取随机字符串
        /// </summary>
        /// <returns></returns>
        private static string GetNoncestr()
        {
            Random random = new Random();
            string nonceStr = MD5Util.GetMD5(random.Next(1000).ToString(), "GBK");

            return nonceStr;
        }

public static string GetMD5(string encypStr, string charset)
		{
			string retStr;
			MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider();

			//创建md5对象
			byte[] inputBye;
			byte[] outputBye;

			//使用GB2312编码方式把字符串转化为字节数组.
			try
			{
				inputBye = Encoding.GetEncoding(charset).GetBytes(encypStr);
			}
			catch (Exception ex)
			{
				inputBye = Encoding.GetEncoding("GB2312").GetBytes(encypStr);
			}
			outputBye = m5.ComputeHash(inputBye);

			retStr = System.BitConverter.ToString(outputBye);
			retStr = retStr.Replace("-", "").ToUpper();
			return retStr;
		}



注意不要忘了微信支付结果通知回调的接口,文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7

开发工程中需要注意安全问题,比如可在传输、获取参数时采用验证加密sign的方式,已保证程序的安全!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值