.Net C# 微信刷卡支付接口
2015-10-16 16:43
633 查看
微信刷卡支付接口
应用场景实例收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。
SDK下载
微信提供5种开发语言的SDK,选择自己的开发语言下载,项目中会有很多示例。微信代码
简略版 数据需自行获取public class ToWeixinPayBLL { /// <summary> /// 微信扫码支付 /// </summary> /// <param name="osaleh_osalehID"></param> /// <param name="txtPaymentCode"></param> /// <param name="orderType"></param> /// <param name="user"></param> /// <returns></returns> public static ResponseDTO GetWeixinPayRequestExecute(string osaleh_osalehID, string str_osaled_osaledID, string txtPaymentCode, string orderType, UsersEntity user) { ResponseDTO resp = new ResponseDTO(); try { decimal osaled_amount = 0; IList<OsaledEntity> OsaledList = new List<OsaledEntity>(); #region 提交支付信息,并接收返回结果 WxPayData data = new WxPayData(); data.SetValue("auth_code", txtPaymentCode);//授权码 data.SetValue("body", Description);//商品描述 try { //微信支付金额单位:分;所以要对支付总金额进行单位换算。 Int32 totalAmount = (Int32)(Convert.ToDecimal(osaled_amount) * 100); data.SetValue("total_fee", totalAmount);//总金额(单位:分) } catch (Exception ex) { ExceptionLog.LogExp(ex.Message); } string out_trade_no = GenerateOutTradeNo(); data.SetValue("out_trade_no", out_trade_no);//产生随机的商户订单号 WxPayData result = Micropay(data, 10); //提交被扫支付,接收返回结果 #endregion #region 如果提交被扫支付接口调用失败,则抛异常提示 if (!result.IsSet("return_code") || result.GetValue("return_code").ToString() == "FAIL") { string returnMsg = result.IsSet("return_msg") ? result.GetValue("return_msg").ToString() : ""; resp.Message = "微信支付接口调用失败,原因:" + returnMsg; resp.Status = false; return resp; } #endregion //签名验证 result.CheckSign(); //刷卡支付直接成功 if (result.GetValue("return_code").ToString() == "SUCCESS" && result.GetValue("result_code").ToString() == "SUCCESS") { #region 支付成功,业务处理 resp.Message = "支付成功"; resp.Status = true; return resp; #endregion } /****************************************************************** * 剩下的都是接口调用成功,业务失败的情况 * ****************************************************************/ //1)业务结果明确失败 if (result.GetValue("err_code").ToString() != "USERPAYING" && result.GetValue("err_code").ToString() != "SYSTEMERROR") { resp.Message = "支付失败,错误代码:" + result.GetValue("err_code").ToString() + "。描述:" + result.GetValue("err_code_des").ToString(); resp.Status = false; return resp; } //2)不能确定是否失败,需查单 //用商户订单号去查单 #region 确认支付是否成功,每隔一段时间(5s)查询一次订单,共查询6次 int queryTimes = 6;//查询次数计数器 while (queryTimes-- > 0) { int succResult = 0;//查询结果 WxPayData queryResult = Query(out_trade_no, out succResult); //如果需要继续查询,则等待5s后继续 if (succResult == 2) { Thread.Sleep(5000); continue; } //查询成功,返回订单查询接口返回的数据 else if (succResult == 1) { #region 支付成功,业务处理 resp.Message = "支付成功"; resp.Status = true; return resp; #endregion 支付成功,处理交易流程 } //订单交易失败,直接返回刷卡支付接口返回的结果,失败原因会在err_code中描述 else { resp.Message = "支付失败,错误代码:" + result.GetValue("err_code").ToString() + "。描述:" + result.GetValue("err_code_des").ToString(); try { //交易失败,则撤销订单 Cancel(out_trade_no); } catch (Exception ex) { resp.Message += "\n撤销订单异常:" + ex.Message; ExceptionLog.ToWeixinPayLog(resp.Message); } resp.Status = false; return resp; } } #endregion try { //交易超时,则撤销订单 Cancel(out_trade_no); } catch (Exception ex) { resp.Message = "支付超时,请重试。撤销订单异常:" + ex.Message + "。"; ExceptionLog.ToWeixinPayLog(resp.Message); } resp.Status = false; } else { resp.Message = "操作失败,未查询到订单信息,请联系管理员!"; resp.Status = false; } } catch (Exception ex) { ExceptionLog.ToWeixinPayLog(ex.Message); resp.Message = "支付失败," + ex.Message; resp.Status = false; } return resp; } /** * 提交被扫支付API * 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台, * 由商户收银台或者商户后台调用该接口发起支付。 * @param WxPayData inputObj 提交给被扫支付API的参数 * @param int timeOut 超时时间 * @throws WxPayException * @return 成功时返回调用结果,其他抛异常 */ public static WxPayData Micropay(WxPayData inputObj, int timeOut = 10) { string url = "https://api.mch.weixin.qq.com/pay/micropay"; //检测必填参数 if (!inputObj.IsSet("body")) { ExceptionLog.ToWeixinPayLog("提交被扫支付API接口中,缺少必填参数body!"); } else if (!inputObj.IsSet("out_trade_no")) { ExceptionLog.ToWeixinPayLog("提交被扫支付API接口中,缺少必填参数out_trade_no!"); } else if (!inputObj.IsSet("total_fee")) { ExceptionLog.ToWeixinPayLog("提交被扫支付API接口中,缺少必填参数total_fee!"); } else if (!inputObj.IsSet("auth_code")) { ExceptionLog.ToWeixinPayLog("提交被扫支付API接口中,缺少必填参数auth_code!"); } inputObj.SetValue("spbill_create_ip", ConfigHelper.WinxinPay_AdressIP);//终端ip inputObj.SetValue("appid", ConfigHelper.WinxinPay_AppID);//公众账号ID inputObj.SetValue("mch_id", ConfigHelper.WWinxinPay_MCHID);//商户号 inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串 inputObj.SetValue("sign", inputObj.MakeSign());//签名 string xml = inputObj.ToXml(); var start = DateTime.Now;//请求开始时间 string response = HttpHelper.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API var end = DateTime.Now; int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时 //将xml格式的结果转换为对象以返回 WxPayData result = new WxPayData(); result.FromXml(response); return result; } /// <summary> /// 查询订单情况 /// @param string out_trade_no 商户订单号 /// @param int succCode 查询订单结果:0表示订单不成功,1表示订单成功,2表示继续查询 /// @return 订单查询接口返回的数据,参见协议接口 /// </summary> /// <param name="out_trade_no"></param> /// <param name="succCode"></param> /// <returns></returns> public static WxPayData Query(string out_trade_no, out int succCode) { WxPayData queryOrderInput = new WxPayData(); queryOrderInput.SetValue("out_trade_no", out_trade_no); WxPayData result = OrderQuery(queryOrderInput); if (result.GetValue("return_code").ToString() == "SUCCESS" && result.GetValue("result_code").ToString() == "SUCCESS") { //支付成功 if (result.GetValue("trade_state").ToString() == "SUCCESS") { succCode = 1; return result; } //用户支付中,需要继续查询 else if (result.GetValue("trade_state").ToString() == "USERPAYING") { succCode = 2; return result; } } if (result.GetValue("return_code").ToString() == "SUCCESS" && result.GetValue("result_code").ToString() == "FAIL") { //如果返回错误码为“此交易订单号不存在”则直接认定失败 if (result.GetValue("err_code").ToString() == "ORDERNOTEXIST") { succCode = 0; } else { //如果是系统错误,则后续继续 succCode = 2; } return result; } succCode = 3; return result; } /// <summary> /// 查询订单 /// @param WxPayData inputObj 提交给查询订单API的参数 /// @param int timeOut 超时时间 /// @throws WxPayException /// @return 成功时返回订单查询结果,其他抛异常 /// </summary> /// <param name="inputObj"></param> /// <param name="timeOut"></param> /// <returns></returns> public static WxPayData OrderQuery(WxPayData inputObj, int timeOut = 6) { string url = "https://api.mch.weixin.qq.com/pay/orderquery"; //检测必填参数 if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id")) { ExceptionLog.ToWeixinPayLog("订单查询接口中,out_trade_no、transaction_id至少填一个!"); } inputObj.SetValue("appid", ConfigHelper.WinxinPay_AppID);//公众账号ID inputObj.SetValue("mch_id", ConfigHelper.WWinxinPay_MCHID);//商户号 inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串 inputObj.SetValue("sign", inputObj.MakeSign());//签名 string xml = inputObj.ToXml(); string response = HttpHelper.Post(xml, url, false, timeOut);//调用HTTP通信接口提交数据 //将xml格式的数据转化为对象以返回 WxPayData result = new WxPayData(); result.FromXml(response); return result; } /// <summary> /// 撤销订单API接口 /// @param WxPayData inputObj 提交给撤销订单API接口的参数,out_trade_no和transaction_id必填一个 /// @param int timeOut 接口超时时间 /// @throws WxPayException /// @return 成功时返回API调用结果,其他抛异常 /// </summary> /// <param name="inputObj"></param> /// <param name="timeOut"></param> /// <returns></returns> public static WxPayData Reverse(WxPayData inputObj, int timeOut = 6) { string url = "https://api.mch.weixin.qq.com/secapi/pay/reverse"; //检测必填参数 if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id")) { ExceptionLog.ToWeixinPayLog("撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个!"); } inputObj.SetValue("appid", ConfigHelper.WinxinPay_AppID);//公众账号ID inputObj.SetValue("mch_id", ConfigHelper.WWinxinPay_MCHID);//商户号 inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串 inputObj.SetValue("sign", inputObj.MakeSign());//签名 string xml = inputObj.ToXml(); string response = HttpHelper.Post(xml, url, true, timeOut); WxPayData result = new WxPayData(); result.FromXml(response); return result; } /// <summary> /// 撤销订单,如果失败会重复调用10次 /// @param string out_trade_no 商户订单号 /// @param depth 调用次数,这里用递归深度表示 /// @return false表示撤销失败,true表示撤销成功 /// </summary> /// <param name="out_trade_no"></param> /// <param name="depth"></param> /// <returns></returns> public static bool Cancel(string out_trade_no, int depth = 0) { if (depth > 10) { return false; } WxPayData reverseInput = new WxPayData(); reverseInput.SetValue("out_trade_no", out_trade_no); WxPayData result = Reverse(reverseInput); //接口调用失败 if (result.GetValue("return_code").ToString() != "SUCCESS") { return false; } //如果结果为success且不需要重新调用撤销,则表示撤销成功 if (result.GetValue("result_code").ToString() != "SUCCESS" && result.GetValue("recall").ToString() == "N") { return true; } else if (result.GetValue("recall").ToString() == "Y") { return Cancel(out_trade_no, ++depth); } return false; } /** * 根据当前系统时间加随机序列来生成订单号 * @return 订单号 */ public static string GenerateOutTradeNo() { var ran = new Random(); return string.Format("{0}{1}{2}", ConfigHelper.WWinxinPay_MCHID, DateTime.Now.ToString("yyyyMMddHHmmss"), ran.Next(999)); } } public class WxPayData { public WxPayData() { } //采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序 private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>(); /** * 设置某个字段的值 * @param key 字段名 * @param value 字段值 */ public void SetValue(string key, object value) { m_values[key] = value; } /** * 根据字段名获取某个字段的值 * @param key 字段名 * @return key对应的字段值 */ public object GetValue(string key) { object o = null; m_values.TryGetValue(key, out o); return o; } /** * 判断某个字段是否已设置 * @param key 字段名 * @return 若字段key已被设置,则返回true,否则返回false */ public bool IsSet(string key) { object o = null; m_values.TryGetValue(key, out o); if (null != o) return true; else return false; } /** * @将Dictionary转成xml * @return 经转换得到的xml串 * @throws WxPayException **/ public string ToXml() { //数据为空时不能转化为xml格式 if (0 == m_values.Count) { ExceptionLog.LogExp("WxPayData数据为空!"); //throw new WxPayException("WxPayData数据为空!"); } string xml = "<xml>"; foreach (KeyValuePair<string, object> pair in m_values) { //字段值不能为null,会影响后续流程 if (pair.Value == null) { ExceptionLog.LogExp("WxPayData内部含有值为null的字段!"); //throw new WxPayException("WxPayData内部含有值为null的字段!"); } 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 + ">"; } else//除了string和int类型不能含有其他数据类型 { ExceptionLog.LogExp("WxPayData字段数据类型错误!"); //throw new WxPayException("WxPayData字段数据类型错误!"); } } xml += "</xml>"; return xml; } /** * @将xml转为WxPayData对象并返回对象内部的数据 * @param string 待转换的xml串 * @return 经转换得到的Dictionary * @throws WxPayException */ public SortedDictionary<string, object> FromXml(string xml) { if (string.IsNullOrEmpty(xml)) { ExceptionLog.LogExp("将空的xml串转换为WxPayData不合法!"); //throw new WxPayException("将空的xml串转换为WxPayData不合法!"); } XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml); XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml> XmlNodeList nodes = xmlNode.ChildNodes; foreach (XmlNode xn in nodes) { XmlElement xe = (XmlElement)xn; m_values[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中 } try { //2015-06-29 错误是没有签名 if (m_values["return_code"].ToString() != "SUCCESS") { return m_values; } CheckSign();//验证签名,不通过会抛异常 } catch (Exception ex) { ExceptionLog.LogExp(ex.Message); } return m_values; } /** * @Dictionary格式转化成url参数格式 * @ return url格式串, 该串不包含sign字段值 */ public string ToUrl() { string buff = ""; foreach (KeyValuePair<string, object> pair in m_values) { if (pair.Value == null) { ExceptionLog.LogExp("WxPayData内部含有值为null的字段!"); //throw new WxPayException("WxPayData内部含有值为null的字段!"); } if (pair.Key != "sign" && pair.Value.ToString() != "") { buff += pair.Key + "=" + pair.Value + "&"; } } buff = buff.Trim('&'); return buff; } /** * @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串) */ public string ToPrintStr() { string str = ""; foreach (KeyValuePair<string, object> pair in m_values) { if (pair.Value == null) { ExceptionLog.LogExp("WxPayData内部含有值为null的字段!"); //throw new WxPayException("WxPayData内部含有值为null的字段!"); } str += string.Format("{0}={1}<br>", pair.Key, pair.Value.ToString()); } return str; } /** * @生成签名,详见签名生成算法 * @return 签名, sign字段不参加签名 */ public string MakeSign() { //转url格式 string str = ToUrl(); //在string后加入API KEY str += "&key=" + ConfigHelper.WinxinPay_Key; //MD5加密 var md5 = 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(); } /** * * 检测签名是否正确 * 正确返回true,错误抛异常 */ public bool CheckSign() { //如果没有设置签名,则跳过检测 if (!IsSet("sign")) { ExceptionLog.LogExp("WxPayData签名存在但不合法!"); //throw new WxPayException("WxPayData签名存在但不合法!"); } //如果设置了签名但是签名为空,则抛异常 else if (GetValue("sign") == null || GetValue("sign").ToString() == "") { ExceptionLog.LogExp("WxPayData签名存在但不合法!"); //throw new WxPayException("WxPayData签名存在但不合法!"); } //获取接收到的签名 string return_sign = GetValue("sign").ToString(); //在本地计算新的签名 string cal_sign = MakeSign(); if (cal_sign == return_sign) { return true; } ExceptionLog.LogExp("WxPayData签名验证错误!"); //throw new WxPayException("WxPayData签名验证错误!"); return false; } /** * @获取Dictionary */ public SortedDictionary<string, object> GetValues() { return m_values; } }
前端效果图
扫描枪自动提交,input输入框内“onkeyup=fun()”方法即可
相关文章推荐
- 社交巨头三国杀:微信、WhatsApp、Line到底有啥区别?
- 微信悄悄升级群聊功能:个人微信营销号的福音
- 我是运营,我没有假期
- 如何做到日消息量100万的微信公众号?
- 论微信取消推送功能的可能性(原创)
- Android ADT 23.0.0无法更新到23.0.2问题解决方案
- 微信的成功,靠的是QQ导流吗?
- SDKMAN:轻松管理多个软件开发套件 (SDK) 的命令行工具
- C#.NET获取拨号连接的宽带连接方法
- C#.Net ArrayList的使用方法
- 微信服务号推送模板消息接口
- PowerShell中使用.NET将程序集加入全局程序集缓存
- .net(c#)中的new关键字详细介绍
- 由vbs sort引发.NET Framework之间的关系说明
- C#难点逐个击破(6):C#数据类型与.net framework数据类型
- .NET中的async和await关键字使用及Task异步调用实例
- Parse正式发布开源PHP SDK