.Net C# 微信刷卡支付接口

微信刷卡支付接口

应用场景实例

收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。

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输入框内“οnkeyup=fun()”方法即可

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

下一秒_待续

如有帮到您,给个赞赏(^.^)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值