最近在做微信支付项目,记录一下: 1、需要现在微信公众平台上设置,具体在 菜单:设置-公众号设置-功能设置,把开发程序所在的域名增加,具体在业务域名、JS接口安全域名 、网页授权域名(这个主要是用于授权用的) 都要增加相应的域名目录。 2、在微信支付平台,产品中心-开发配置-支付设置,公众号支付-JSAPI授权目录增加域名目录,否则不能成功调用。 以上两步必须同时设置好。 3、然后阅读开发微信支付开发文档,根据文档开发即可。虽然大家都说有很多坑,但是慢慢解决,能够很好的实现调用。自己感觉微信支付比支付宝支付要简单。如果还是无法成功,或者提示签名错误,有的时候需要重置API密钥试试(调试了几天 找了好多办法都没有解决,结果最后真的重置了API密钥就好了)。 4、还有一个问题,就是微信调起支付后,没有支付,然后再支付的时候提示重复下单或这缺少total_fee参数(这个实际上有的时候并不是真少这个参数),这个时候应该是body内容不一样但是订单号一样。 我的解决办法是这样的:每次调起支付的时候,将生成的支付信息保存到数据库,如果当时调起支付未支付,那么再次调起的时候会先搜索一下数据库,如果有这条记录,那么将当时的timestamp、NonceStr、paysign、Prepay_id等信息提取出来,生成支付数据。如果数据库没有数据,则启用新的支付参数。这样就能保证统一支付下单的是一致的,不至于重复下单。在这里插入代码片
几个关键代码参考:
public class UnifiedOrder { /// <summary> /// <summary> /// 微信支付分配的终端设备号 /// </summary> public string _device_info = ""; /// <summary> /// 公共号ID(微信分配的公众账号 ID) /// </summary> public string appid = ""; /// <summary> /// 商户号(微信支付分配的商户号) /// </summary> public string mch_id = ""; /// <summary> /// 微信支付分配的终端设备号 /// </summary> public string device_info = ""; /// <summary> /// 随机字符串,不长于 32 位 /// </summary> public string nonce_str = ""; /// <summary> /// 签名 /// </summary> public string sign = ""; /// <summary> /// 商品描述 /// </summary> public string body = ""; /// <summary> /// 附加数据,原样返回 /// </summary> public string attach = ""; /// <summary> /// 商户系统内部的订单号,32个字符内、可包含字母,确保在商户系统唯一,详细说明 /// </summary> public string out_trade_no = ""; /// <summary> /// 订单总金额,单位为分,不能带小数点 /// </summary> public int total_fee = 0; /// <summary> /// 终端IP /// </summary> public string spbill_create_ip = ""; /// <summary> /// 订 单 生 成 时 间 , 格 式 为yyyyMMddHHmmss,如 2009 年12 月 25 日 9 点 10 分 10 秒表示为 20091225091010。时区为 GMT+8 beijing。该时间取自商户服务器 /// </summary> public string time_start = ""; /// <summary> /// 交易结束时间 /// </summary> public string time_expire = ""; /// <summary> /// 商品标记 商品标记,该字段不能随便填,不使用请填空,使用说明详见第 5 节 /// </summary> public string goods_tag = ""; /// <summary> /// 接收微信支付成功通知 /// </summary> public string notify_url = ""; /// <summary> /// JSAPI、NATIVE、APP /// </summary> public string trade_type = ""; /// <summary> /// 用户标识 trade_type 为 JSAPI时,此参数必传 /// </summary> public string openid = ""; /// <summary> /// 只在 trade_type 为 NATIVE时需要填写。 /// </summary> public string product_id = ""; }这个是主要的代码
// <summary> ///TenpayUtil 的摘要说明 /// </summary> public class TenpayUtil { public TenpayUtil() { // } //TODO: 在此处添加构造函数逻辑 // /// <summary> /// 统一支付接口 /// </summary> const string UnifiedPayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; /// <summary> /// 网页授权接口 /// </summary> const string access_tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token"; /// <summary> /// 微信订单查询接口 /// </summary> const string OrderQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery"; /// <summary> /// 随机串 /// </summary> public static string getNoncestr() { Random random = new Random(); return MD5Util.GetMD5(random.Next(1000).ToString(), "GBK").ToLower().Replace("s", "S"); } /// <summary> /// 时间截,自1970年以来的秒数 /// </summary> public static string getTimestamp() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds).ToString(); } /// <summary> /// 网页授权接口 /// </summary> public static string getAccess_tokenUrl() { return access_tokenUrl; } /// <summary> /// 获取微信签名 /// </summary> /// <param name="sParams"></param> /// <returns></returns> public string getsign(SortedDictionary<string, string> sParams, string key) { int i = 0; string sign = string.Empty; StringBuilder sb = new StringBuilder(); foreach (KeyValuePair<string, string> temp in sParams) { if (temp.Value == "" || temp.Value == null || temp.Key.ToLower() == "sign" ) { continue; } i++; sb.Append(temp.Key.Trim() + "=" + temp.Value.Trim() + "&"); } sb.Append("key=" + key.Trim() + ""); string signkey = sb.ToString(); sign = MD5Util.GetMD5(signkey, "utf-8"); return sign; } /// <summary> /// post数据到指定接口并返回数据 /// </summary> public string PostXmlToUrl(string url, string postData) { //string returnmsg = ""; //using (System.Net.WebClient wc = new System.Net.WebClient()) //{ // returnmsg = wc.UploadString(url, "POST", postData); //} //return returnmsg; HttpWebRequest hwr = (HttpWebRequest)HttpWebRequest.Create(url); hwr.Method = "POST"; Stream stream = hwr.GetRequestStream(); StreamWriter sw = new StreamWriter(stream, System.Text.Encoding.UTF8); sw.Write(postData); sw.Close(); stream = hwr.GetResponse().GetResponseStream(); StreamReader sr = new StreamReader(stream, System.Text.Encoding.UTF8); string ret = sr.ReadToEnd(); sr.Close(); return ret; } /// <summary> /// 获取prepay_id /// </summary> public string getPrepay_id(UnifiedOrder order, string key) { string prepay_id = ""; string post_data = getUnifiedOrderXml(order, key); string request_data = PostXmlToUrl(UnifiedPayUrl, post_data); SortedDictionary<string, string> requestXML = GetInfoFromXml(request_data); foreach (KeyValuePair<string, string> k in requestXML) { if (k.Key == "prepay_id") { prepay_id = k.Value; break; } } return prepay_id; } /// <summary> /// 获取微信订单明细 /// </summary> public OrderDetail getOrderDetail(QueryOrder queryorder, string key) { string post_data = getQueryOrderXml(queryorder, key); string request_data = PostXmlToUrl(OrderQueryUrl, post_data); OrderDetail orderdetail = new OrderDetail(); SortedDictionary<string, string> requestXML = GetInfoFromXml(request_data); foreach (KeyValuePair<string, string> k in requestXML) { switch (k.Key) { case "retuen_code": orderdetail.result_code = k.Value; break; case "return_msg": orderdetail.return_msg = k.Value; break; case "appid": orderdetail.appid = k.Value; break; case "mch_id": orderdetail.mch_id = k.Value; break; case "nonce_str": orderdetail.nonce_str = k.Value; break; case "sign": orderdetail.sign = k.Value; break; case "result_code": orderdetail.result_code = k.Value; break; case "err_code": orderdetail.err_code = k.Value; break; case "err_code_des": orderdetail.err_code_des = k.Value; break; case "trade_state": orderdetail.trade_state = k.Value; break; case "device_info": orderdetail.device_info = k.Value; break; case "openid": orderdetail.openid = k.Value; break; case "is_subscribe": orderdetail.is_subscribe = k.Value; break; case "trade_type": orderdetail.trade_type = k.Value; break; case "bank_type": orderdetail.bank_type = k.Value; break; case "total_fee": orderdetail.total_fee = k.Value; break; case "coupon_fee": orderdetail.coupon_fee = k.Value; break; case "fee_type": orderdetail.fee_type = k.Value; break; case "transaction_id": orderdetail.transaction_id = k.Value; break; case "out_trade_no": orderdetail.out_trade_no = k.Value; break; case "attach": orderdetail.attach = k.Value; break; case "time_end": orderdetail.time_end = k.Value; break; default: break; } } return orderdetail; } /// <summary> /// 把XML数据转换为SortedDictionary<string, string>集合 /// </summary> /// <param name="strxml"></param> /// <returns></returns> protected 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; } /// <summary> /// 微信统一下单接口xml参数整理 /// </summary> /// <param name="order">微信支付参数实例</param> /// <param name="key">密钥</param> /// <returns></returns> protected string getUnifiedOrderXml(UnifiedOrder order, string key) { string return_string = string.Empty; SortedDictionary<string, string> sParams = new SortedDictionary<string, string>(); sParams.Add("appid", order.appid); sParams.Add("attach", order.attach); sParams.Add("body", order.body); sParams.Add("device_info", order.device_info); sParams.Add("mch_id", order.mch_id); sParams.Add("nonce_str", order.nonce_str); sParams.Add("notify_url", order.notify_url); sParams.Add("openid", order.openid); sParams.Add("out_trade_no", order.out_trade_no); sParams.Add("spbill_create_ip", order.spbill_create_ip); sParams.Add("total_fee", order.total_fee.ToString()); sParams.Add("trade_type", order.trade_type); order.sign = getsign(sParams, key); sParams.Add("sign", order.sign); //拼接成XML请求数据 StringBuilder sbPay = new StringBuilder(); foreach (KeyValuePair<string, string> k in sParams) { //if (k.Key == "attach" || k.Key == "body" || k.Key == "sign") //{ // sbPay.Append("<" + k.Key + "><![CDATA[" + k.Value + "]]></" + k.Key + ">"); //} //else //{ sbPay.Append("<" + k.Key + ">" + k.Value + "</" + k.Key + ">"); // } } return_string = string.Format("<xml>{0}</xml>", sbPay.ToString()); byte[] byteArray = Encoding.UTF8.GetBytes(return_string); return_string = Encoding.GetEncoding("UTF-8").GetString(byteArray); return return_string; } /// <summary> /// 微信订单查询接口XML参数整理 /// </summary> /// <param name="queryorder">微信订单查询参数实例</param> /// <param name="key">密钥</param> /// <returns></returns> protected string getQueryOrderXml(QueryOrder queryorder, string key) { string return_string = string.Empty; SortedDictionary<string, string> sParams = new SortedDictionary<string, string>(); sParams.Add("appid", queryorder.appid); sParams.Add("mch_id", queryorder.mch_id); sParams.Add("transaction_id", queryorder.transaction_id); sParams.Add("out_trade_no", queryorder.out_trade_no); sParams.Add("nonce_str", queryorder.nonce_str); queryorder.sign = getsign(sParams, key); sParams.Add("sign", queryorder.sign); //拼接成XML请求数据 StringBuilder sbPay = new StringBuilder(); foreach (KeyValuePair<string, string> k in sParams) { if (k.Key == "attach" || k.Key == "body" || k.Key == "sign") { sbPay.Append("<" + k.Key + "><![CDATA[" + k.Value + "]]></" + k.Key + ">"); } else { sbPay.Append("<" + k.Key + ">" + k.Value + "</" + k.Key + ">"); } } return_string = string.Format("<xml>{0}</xml>", sbPay.ToString().TrimEnd(',')); return return_string; } }MD5加密:
public class MD5Util { public MD5Util() { // // TODO: 在此处添加构造函数逻辑 // } /** 获取大写的MD5签名结果 */ 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; } } Pay.aspx主要内容 //支付信息 function Pay() { var appId = "<%=appId %>"; var timeStamp = "<%=timeStamp %>"; var nonceStr = "<%=nonceStr %>"; var prepay_id = "<%=prepay_id %>"; var paySign = "<%=paySign %>"; var OrderID = "<%=OrderID %>"; //alert("appId:" + appId + ",timeStamp:" + timeStamp + ",nonceStr:" + nonceStr + ",prepay_id:" + prepay_id + ",paySign:" + paySign); //return; function onBridgeReady() { WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId": appId, //公众号名称,由商户传入 "timeStamp": timeStamp, //时间戳,自1970年以来的秒数 "nonceStr": nonceStr, //随机串 "package": "prepay_id=" + prepay_id, "signType": "MD5", //微信签名方式: "paySign": paySign //微信签名 }, function (res) { if (res.err_msg == "get_brand_wcpay_request:ok") { $.showLoading("正在校验支付"); //检查是否支付成功 $.get("Tools/CheckPay.aspx?OrderId=" + OrderID, function (data) { $.hideLoading(); if (data == "success") { alert("支付成功"); window.location.href = "Order.aspx?OrderID=" + OrderID + "&OpenId=" + '<%=OpenID %>' + "&M=" + Math.random(); } else { alert("支付待验证,返回结果:" + data); window.location.href = "Order.aspx?OrderID=" + OrderID + "&OpenId=" + '<%=OpenID %>' + "&M=" + Math.random(); } }); //get结束 } // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 else { alert("交易取消"); window.location.href = "Order.aspx?OrderID=" + OrderID + "&OpenId=" + '<%=OpenID %>' + "&M=" + Math.random(); } } ); } if (typeof WeixinJSBridge == "undefined") { if (document.addEventListener) { document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); } else if (document.attachEvent) { document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } } else { onBridgeReady(); } } Pay.aspx.cs 主要内容 private void PayInfo(string OpenId, string OrderId, string OrderAmount, string paySignKey) { try { TenpayUtil tenpay = new TenpayUtil(); UnifiedOrder order = new UnifiedOrder(); order.appid = "这里是APPID"; order.attach = "随便写"; order.body = "这里是订单内容"; order.device_info = ""; order.mch_id = "这里是微信支付的商户号";//商户号 order.nonce_str = TenpayUtil.getNoncestr(); order.notify_url = "回调通知地址"; order.openid = OpenId; order.out_trade_no = OrderId; order.trade_type = "JSAPI"; order.spbill_create_ip = Page.Request.UserHostAddress; order.total_fee = int.Parse(OrderAmount); //order.total_fee = int.Parse("12") * 100; prepay_id = tenpay.getPrepay_id(order, paySignKey); timeStamp = TenpayUtil.getTimestamp(); nonceStr = TenpayUtil.getNoncestr(); SortedDictionary<string, string> sParams = new SortedDictionary<string, string>(); sParams.Add("appId", appId); sParams.Add("timeStamp", timeStamp); sParams.Add("nonceStr", nonceStr); sParams.Add("package", "prepay_id=" + prepay_id); sParams.Add("signType", "MD5"); paySign = tenpay.getsign(sParams, paySignKey); } catch (Exception ex) { Response.Write(ex.Message); } }