码农之家

专注优质代码开发,为软件行业发展贡献力量

C#实现微信小程序提现

企业微信小程序开通需要的申请资料:
一个邮箱(作为登录帐号,未在微信公众平台、微信开放平台注册,未绑定个人微信号的邮箱)
一个手机号码(建议使用法人的手机号码)
一张身份证(法人身份证/股东身份证)
一个微信号(建议为法人/股东的微信)
公司营业执照(加盖公章执照复印件或扫描件)
公司对公账户信息(开户银行信息,账号,验证账户)
小程序需要开通微信支付(微信认证300元,支付费率一般为0.6%)
一台服务器(建议选择阿里云、腾讯云)
如果需要添加到附近的小程序显示,需要一个腾讯地图
以上就是企业微信小程序开通需要的申请资料。小程序现在正与各种行业连接。随着用户习惯的逐步培养和通信能力的逐步提高,正逐步向三线城市和四线城市蔓延。因此,许多商家开始认识到微信小程序是一个巨大的商机,开发小程序已经成为了商家势在必行的趋势。

实现微信小程序提现
实现微信小程序提现到微信钱包功能首先需要准备一个商户号。

提现请求接口是https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers,详细请求参数信息见微信支付开发文档,请使用如下类进行配合调试完成:

1、JsApiPay.cs
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Runtime.Serialization;
using System.IO;
using System.Text;
using System.Net;
using System.Web.Security;
using LitJson;
/// <summary>
///JsApiPay 的摘要说明
/// </summary>

    public class JsApiPay
    {
        /// <summary>
        /// 保存页面对象,因为要在类的方法中使用Page的Request对象
        /// </summary>
        private Page page { get; set; }

        /// <summary>
        /// openid用于调用统一下单接口
        /// </summary>
        public string openid { get; set; }

        /// <summary>
        /// access_token用于获取收货地址js函数入口参数
        /// </summary>
        public string access_token { get; set; }

        /// <summary>
        /// 订单id
        /// </summary>
        public string orderid { get; set; }

        /// <summary>
        /// 商品详情
        /// </summary>
        public string productName { get; set; }

        /// <summary>
        /// 商品金额,用于统一下单
        /// </summary>
        public int total_fee { get; set; }

        /// <summary>
        /// 统一下单接口返回结果
        /// </summary>
        public WxPayData unifiedOrderResult { get; set; }

        public JsApiPay(Page page)
        {
            this.page = page;
        }


        /**
        * 
        * 网页授权获取用户基本信息的全部过程
        * 详情请参看网页授权获取用户基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
        * 第一步:利用url跳转获取code
        * 第二步:利用code去获取openid和access_token
        * 
        */
        public void GetOpenidAndAccessToken()
        {
            if (!string.IsNullOrEmpty(page.Request.QueryString["code"]))
            {
                //获取code码,以获取openid和access_token
                string code = page.Request.QueryString["code"];
                Log.Debug(this.GetType().ToString(), "Get code : " + code);
                GetOpenidAndAccessTokenFromCode(code);
            }
            else
            {
                //构造网页授权获取code的URL
                string host = page.Request.Url.Host;
                string path = page.Request.Path;
                string redirect_uri = HttpUtility.UrlEncode("http://" + host + path);
                WxPayData data = new WxPayData();
                data.SetValue("appid", WxPayConfig.APPID);
                data.SetValue("redirect_uri", redirect_uri);
                data.SetValue("response_type", "code");
                data.SetValue("scope", "snsapi_base");
                data.SetValue("state", "STATE" + "#wechat_redirect");
                string url = "https://open.weixin.qq.com/connect/oauth2/authorize?" + data.ToUrl();
                Log.Debug(this.GetType().ToString(), "Will Redirect to URL : " + url);
                try
                {
                    //触发微信返回code码         
                    page.Response.Redirect(url);//Redirect函数会抛出ThreadAbortException异常,不用处理这个异常
                }
                catch (System.Threading.ThreadAbortException ex)
                {
                }
            }
        }


        /**
    * 
    * 通过code换取网页授权access_token和openid的返回数据,正确时返回的JSON数据包如下:
    * {
    *  "access_token":"ACCESS_TOKEN",
    *  "expires_in":7200,
    *  "refresh_token":"REFRESH_TOKEN",
    *  "openid":"OPENID",
    *  "scope":"SCOPE",
    *  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
    * }
    * 其中access_token可用于获取共享收货地址
    * openid是微信支付jsapi支付接口统一下单时必须的参数
        * 更详细的说明请参考网页授权获取用户基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
        * @失败时抛异常WxPayException
    */
        public void GetOpenidAndAccessTokenFromCode(string code)
        {
            try
            {
                //构造获取openid及access_token的url
                WxPayData data = new WxPayData();
                data.SetValue("appid", WxPayConfig.APPID);
                data.SetValue("secret", WxPayConfig.APPSECRET);
                data.SetValue("code", code);
                data.SetValue("grant_type", "authorization_code");
                string url = "https://api.weixin.qq.com/sns/oauth2/access_token?" + data.ToUrl();

                //请求url以获取数据
                string result = HttpService.Get(url);

                Log.Debug(this.GetType().ToString(), "GetOpenidAndAccessTokenFromCode response : " + result);

                //保存access_token,用于收货地址获取
                JsonData jd = JsonMapper.ToObject(result);
                access_token = (string)jd["access_token"];

                //获取用户openid
                openid = (string)jd["openid"];

                Log.Debug(this.GetType().ToString(), "Get openid : " + openid);
                Log.Debug(this.GetType().ToString(), "Get access_token : " + access_token);
            }
            catch (Exception ex)
            {
                Log.Error(this.GetType().ToString(), ex.ToString());
                throw new WxPayException(ex.ToString());
            }
        }

        /**
         * 调用统一下单,获得下单结果
         * @return 统一下单结果
         * @失败时抛异常WxPayException
         */
        public WxPayData GetUnifiedOrderResult()
        {
            //统一下单
            WxPayData data = new WxPayData();
            data.SetValue("body", productName);
            data.SetValue("attach", productName);
            data.SetValue("out_trade_no", orderid);//WxPayApi.GenerateOutTradeNo()
            data.SetValue("total_fee", total_fee);
            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));
            data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));
            data.SetValue("goods_tag", productName);
            data.SetValue("trade_type", "JSAPI");
            data.SetValue("openid", openid);

            WxPayData result = WxPayApi.UnifiedOrder(data);
            if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
            {
                Log.Error(this.GetType().ToString(), "UnifiedOrder response error!");
                throw new WxPayException("UnifiedOrder response error!");
            }

            unifiedOrderResult = result;
            return result;
        }
    /**
    * 调用下单
    * @return 统一提现结果
    * @失败时抛异常WxPayException
    */
    public WxPayData GetWithdraw()
    {
        //统一提现
        WxPayData data = new WxPayData();
        data.SetValue("partner_trade_no", orderid);//WxPayApi.GenerateOutTradeNo()

        data.SetValue("check_name", "NO_CHECK");
        data.SetValue("re_user_name", productName);
        data.SetValue("amount", total_fee);
        data.SetValue("desc", "订单结算");
        data.SetValue("openid", openid);

        WxPayData result = WxPayApi.UnifiedWithdraw(data);

        unifiedOrderResult = result;
        return result;
    }

    /**
    *  
    * 从统一下单成功返回的数据中获取微信浏览器调起jsapi支付所需的参数,
    * 微信浏览器调起JSAPI时的输入参数格式如下:
    * {
    *   "appId" : "",//公众号名称,由商户传入
    *   "timeStamp":" ",//时间戳,自1970年以来的秒数
    *   "nonceStr" : "",//随机串
    *   "package" : "prepay_id=",
    *   "signType" : "MD5",//微信签名方式
    *   "paySign" : ""//微信签名
    * }
    * @return string 微信浏览器调起JSAPI时的输入参数,json格式可以直接做参数用
    * 更详细的说明请参考网页端调起支付API:http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7
    * 
    */
    public string GetData()
    {
        return unifiedOrderResult.GetValue("result_code").ToString()+":"+ unifiedOrderResult.GetValue("return_msg").ToString();
    }
    public string GetJsApiParameters()
        {
            Log.Debug(this.GetType().ToString(), "JsApiPay::GetJsApiParam is processing...");

            WxPayData jsApiParam = new WxPayData();
            jsApiParam.SetValue("appId", unifiedOrderResult.GetValue("appid"));
            jsApiParam.SetValue("timeStamp", WxPayApi.GenerateTimeStamp());
            jsApiParam.SetValue("nonceStr", WxPayApi.GenerateNonceStr());
            jsApiParam.SetValue("package", "prepay_id=" + unifiedOrderResult.GetValue("prepay_id"));
            jsApiParam.SetValue("signType", "MD5");
            jsApiParam.SetValue("paySign", jsApiParam.MakeSign());

            string parameters = jsApiParam.ToJson();

            Log.Debug(this.GetType().ToString(), "Get jsApiParam : " + parameters);
            return parameters;
        }


        /**
    * 
    * 获取收货地址js函数入口参数,详情请参考收货地址共享接口:http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_9
    * @return string 共享收货地址js函数需要的参数,json格式可以直接做参数使用
    */
        public string GetEditAddressParameters()
        {
            string parameter = "";
            try
            {
                string host = page.Request.Url.Host;
                string path = page.Request.Path;
                string queryString = page.Request.Url.Query;
                //这个地方要注意,参与签名的是网页授权获取用户信息时微信后台回传的完整url
                string url = "http://" + host + path + queryString;

                //构造需要用SHA1算法加密的数据
                WxPayData signData = new WxPayData();
                signData.SetValue("appid", WxPayConfig.APPID);
                signData.SetValue("url", url);
                signData.SetValue("timestamp", WxPayApi.GenerateTimeStamp());
                signData.SetValue("noncestr", WxPayApi.GenerateNonceStr());
                signData.SetValue("accesstoken", access_token);
                string param = signData.ToUrl();

                Log.Debug(this.GetType().ToString(), "SHA1 encrypt param : " + param);
                //SHA1加密
                string addrSign = FormsAuthentication.HashPasswordForStoringInConfigFile(param, "SHA1");
                Log.Debug(this.GetType().ToString(), "SHA1 encrypt result : " + addrSign);

                //获取收货地址js函数入口参数
                WxPayData afterData = new WxPayData();
                afterData.SetValue("appId", WxPayConfig.APPID);
                afterData.SetValue("scope", "jsapi_address");
                afterData.SetValue("signType", "sha1");
                afterData.SetValue("addrSign", addrSign);
                afterData.SetValue("timeStamp", signData.GetValue("timestamp"));
                afterData.SetValue("nonceStr", signData.GetValue("noncestr"));

                //转为json格式
                parameter = afterData.ToJson();
                Log.Debug(this.GetType().ToString(), "Get EditAddressParam : " + parameter);
            }
            catch (Exception ex)
            {
                Log.Error(this.GetType().ToString(), ex.ToString());
                throw new WxPayException(ex.ToString());
            }

            return parameter;
        }
    }


2、WxPayApi.cs
using System;
using System.Collections.Generic;
using System.Web;
using System.Net;
using System.IO;
using System.Text;

/// <summary>
///WxPayApi 的摘要说明
/// </summary>
public class WxPayApi
{
    /**
    * 提交被扫支付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"))
        {
            throw new WxPayException("提交被扫支付API接口中,缺少必填参数body!");
        }
        else if (!inputObj.IsSet("out_trade_no"))
        {
            throw new WxPayException("提交被扫支付API接口中,缺少必填参数out_trade_no!");
        }
        else if (!inputObj.IsSet("total_fee"))
        {
            throw new WxPayException("提交被扫支付API接口中,缺少必填参数total_fee!");
        }
        else if (!inputObj.IsSet("auth_code"))
        {
            throw new WxPayException("提交被扫支付API接口中,缺少必填参数auth_code!");
        }

        inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip
        inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
        inputObj.SetValue("sign", inputObj.MakeSign());//签名
        string xml = inputObj.ToXml();

        var start = DateTime.Now;//请求开始时间

        Log.Debug("WxPayApi", "MicroPay request : " + xml);
        string response = HttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API
        Log.Debug("WxPayApi", "MicroPay response : " + response);

        var end = DateTime.Now;
        int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时

        //将xml格式的结果转换为对象以返回
        WxPayData result = new WxPayData();
        result.FromXml(response);

        ReportCostTime(url, timeCost, result);//测速上报

        return result;
    }


    /**
    *    
    * 查询订单
    * @param WxPayData inputObj 提交给查询订单API的参数
    * @param int timeOut 超时时间
    * @throws WxPayException
    * @return 成功时返回订单查询结果,其他抛异常
    */
    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"))
        {
            throw new WxPayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
        }

        inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("nonce_str", WxPayApi.GenerateNonceStr());//随机字符串
        inputObj.SetValue("sign", inputObj.MakeSign());//签名

        string xml = inputObj.ToXml();

        var start = DateTime.Now;

        Log.Debug("WxPayApi", "OrderQuery request : " + xml);
        string response = HttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口提交数据
        Log.Debug("WxPayApi", "OrderQuery response : " + response);

        var end = DateTime.Now;
        int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时

        //将xml格式的数据转化为对象以返回
        WxPayData result = new WxPayData();
        result.FromXml(response);

        ReportCostTime(url, timeCost, result);//测速上报

        return result;
    }


    /**
    * 
    * 撤销订单API接口
    * @param WxPayData inputObj 提交给撤销订单API接口的参数,out_trade_no和transaction_id必填一个
    * @param int timeOut 接口超时时间
    * @throws WxPayException
    * @return 成功时返回API调用结果,其他抛异常
    */
    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"))
        {
            throw new WxPayException("撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个!");
        }

        inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
        inputObj.SetValue("sign", inputObj.MakeSign());//签名
        string xml = inputObj.ToXml();

        var start = DateTime.Now;//请求开始时间

        Log.Debug("WxPayApi", "Reverse request : " + xml);

        string response = HttpService.Post(xml, url, true, timeOut);

        Log.Debug("WxPayApi", "Reverse response : " + response);

        var end = DateTime.Now;
        int timeCost = (int)((end - start).TotalMilliseconds);

        WxPayData result = new WxPayData();
        result.FromXml(response);

        ReportCostTime(url, timeCost, result);//测速上报

        return result;
    }


    /**
    * 
    * 申请退款
    * @param WxPayData inputObj 提交给申请退款API的参数
    * @param int timeOut 超时时间
    * @throws WxPayException
    * @return 成功时返回接口调用结果,其他抛异常
    */
    public static WxPayData Refund(WxPayData inputObj, int timeOut = 6)
    {
        string url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
        //检测必填参数
        if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
        {
            throw new WxPayException("退款申请接口中,out_trade_no、transaction_id至少填一个!");
        }
        else if (!inputObj.IsSet("out_refund_no"))
        {
            throw new WxPayException("退款申请接口中,缺少必填参数out_refund_no!");
        }
        else if (!inputObj.IsSet("total_fee"))
        {
            throw new WxPayException("退款申请接口中,缺少必填参数total_fee!");
        }
        else if (!inputObj.IsSet("refund_fee"))
        {
            throw new WxPayException("退款申请接口中,缺少必填参数refund_fee!");
        }
        else if (!inputObj.IsSet("op_user_id"))
        {
            throw new WxPayException("退款申请接口中,缺少必填参数op_user_id!");
        }

        inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
        inputObj.SetValue("sign", inputObj.MakeSign());//签名

        string xml = inputObj.ToXml();
        var start = DateTime.Now;

        Log.Debug("WxPayApi", "Refund request : " + xml);
        string response = HttpService.Post(xml, url, true, timeOut);//调用HTTP通信接口提交数据到API
        Log.Debug("WxPayApi", "Refund response : " + response);

        var end = DateTime.Now;
        int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时

        //将xml格式的结果转换为对象以返回
        WxPayData result = new WxPayData();
        result.FromXml(response);

        ReportCostTime(url, timeCost, result);//测速上报

        return result;
    }


    /**
    * 
    * 查询退款
    * 提交退款申请后,通过该接口查询退款状态。退款有一定延时,
    * 用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
    * out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
    * @param WxPayData inputObj 提交给查询退款API的参数
    * @param int timeOut 接口超时时间
    * @throws WxPayException
    * @return 成功时返回,其他抛异常
    */
    public static WxPayData RefundQuery(WxPayData inputObj, int timeOut = 6)
    {
        string url = "https://api.mch.weixin.qq.com/pay/refundquery";
        //检测必填参数
        if (!inputObj.IsSet("out_refund_no") && !inputObj.IsSet("out_trade_no") &&
            !inputObj.IsSet("transaction_id") && !inputObj.IsSet("refund_id"))
        {
            throw new WxPayException("退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个!");
        }

        inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
        inputObj.SetValue("sign", inputObj.MakeSign());//签名

        string xml = inputObj.ToXml();

        var start = DateTime.Now;//请求开始时间

        Log.Debug("WxPayApi", "RefundQuery request : " + xml);
        string response = HttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API
        Log.Debug("WxPayApi", "RefundQuery response : " + response);

        var end = DateTime.Now;
        int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时

        //将xml格式的结果转换为对象以返回
        WxPayData result = new WxPayData();
        result.FromXml(response);

        ReportCostTime(url, timeCost, result);//测速上报

        return result;
    }


    /**
    * 下载对账单
    * @param WxPayData inputObj 提交给下载对账单API的参数
    * @param int timeOut 接口超时时间
    * @throws WxPayException
    * @return 成功时返回,其他抛异常
    */
    public static WxPayData DownloadBill(WxPayData inputObj, int timeOut = 6)
    {
        string url = "https://api.mch.weixin.qq.com/pay/downloadbill";
        //检测必填参数
        if (!inputObj.IsSet("bill_date"))
        {
            throw new WxPayException("对账单接口中,缺少必填参数bill_date!");
        }

        inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
        inputObj.SetValue("sign", inputObj.MakeSign());//签名

        string xml = inputObj.ToXml();

        Log.Debug("WxPayApi", "DownloadBill request : " + xml);
        string response = HttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API
        Log.Debug("WxPayApi", "DownloadBill result : " + response);

        WxPayData result = new WxPayData();
        //若接口调用失败会返回xml格式的结果
        if (response.Substring(0, 5) == "<xml>")
        {
            result.FromXml(response);
        }
        //接口调用成功则返回非xml格式的数据
        else
            result.SetValue("result", response);

        return result;
    }


    /**
    * 
    * 转换短链接
    * 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),
    * 减小二维码数据量,提升扫描速度和精确度。
    * @param WxPayData inputObj 提交给转换短连接API的参数
    * @param int timeOut 接口超时时间
    * @throws WxPayException
    * @return 成功时返回,其他抛异常
    */
    public static WxPayData ShortUrl(WxPayData inputObj, int timeOut = 6)
    {
        string url = "https://api.mch.weixin.qq.com/tools/shorturl";
        //检测必填参数
        if (!inputObj.IsSet("long_url"))
        {
            throw new WxPayException("需要转换的URL,签名用原串,传输需URL encode!");
        }

        inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
        inputObj.SetValue("sign", inputObj.MakeSign());//签名
        string xml = inputObj.ToXml();

        var start = DateTime.Now;//请求开始时间

        Log.Debug("WxPayApi", "ShortUrl request : " + xml);
        string response = HttpService.Post(xml, url, false, timeOut);
        Log.Debug("WxPayApi", "ShortUrl response : " + response);

        var end = DateTime.Now;
        int timeCost = (int)((end - start).TotalMilliseconds);

        WxPayData result = new WxPayData();
        result.FromXml(response);
        ReportCostTime(url, timeCost, result);//测速上报

        return result;
    }


    /**
    * 
    * 统一下单
    * @param WxPaydata inputObj 提交给统一下单API的参数
    * @param int timeOut 超时时间
    * @throws WxPayException
    * @return 成功时返回,其他抛异常
    */
    public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 6)
    {
        string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

        //检测必填参数
        if (!inputObj.IsSet("out_trade_no"))
        {
            throw new WxPayException("缺少统一支付接口必填参数out_trade_no!");
        }
        else if (!inputObj.IsSet("body"))
        {
            throw new WxPayException("缺少统一支付接口必填参数body!");
        }
        else if (!inputObj.IsSet("total_fee"))
        {
            throw new WxPayException("缺少统一支付接口必填参数total_fee!");
        }
        else if (!inputObj.IsSet("trade_type"))
        {
            throw new WxPayException("缺少统一支付接口必填参数trade_type!");
        }

        //关联参数
        if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
        {
            throw new WxPayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
        }
        if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
        {
            throw new WxPayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
        }

        //异步通知url未设置,则使用配置文件中的url
        if (!inputObj.IsSet("notify_url"))
        {
            inputObj.SetValue("notify_url", WxPayConfig.NOTIFY_URL);//异步通知url
        }

        inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip       
        inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串

        //签名
        inputObj.SetValue("sign", inputObj.MakeSign());
        string xml = inputObj.ToXml();

        var start = DateTime.Now;

        Log.Debug("WxPayApi", "UnfiedOrder request : " + xml);
        string response = HttpService.Post(xml, url, false, timeOut);
        Log.Debug("WxPayApi", "UnfiedOrder response : " + response);

        var end = DateTime.Now;
        int timeCost = (int)((end - start).TotalMilliseconds);

        WxPayData result = new WxPayData();
        result.FromXml(response);

        ReportCostTime(url, timeCost, result);//测速上报

        return result;
    }

    /**
    * 
    * 统一提现
    * @param WxPaydata inputObj 提交给统一提现API的参数
    * @param int timeOut 超时时间
    * @throws WxPayException
    * @return 成功时返回,其他抛异常
    */
    public static WxPayData UnifiedWithdraw(WxPayData inputObj, int timeOut = 6)
    {
        string url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; //获取退款的api接口

        inputObj.SetValue("mch_appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mchid", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip
        inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串

        //签名
        inputObj.SetValue("sign", inputObj.MakeSign());
        string xml = inputObj.ToXml();

        var start = DateTime.Now;

        Log.Debug("WxPayApi", "UnfiedOrder request : " + xml);
        string response = HttpService.Post(xml, url, true, timeOut);
        Log.Debug("WxPayApi", "UnfiedOrder response : " + response);

        var end = DateTime.Now;
        int timeCost = (int)((end - start).TotalMilliseconds);

        WxPayData result = new WxPayData();
        result.FromXml(response);

        //ReportCostTime(url, timeCost, result);//测速上报

        return result;
    }


    /**
    * 
    * 关闭订单
    * @param WxPayData inputObj 提交给关闭订单API的参数
    * @param int timeOut 接口超时时间
    * @throws WxPayException
    * @return 成功时返回,其他抛异常
    */
    public static WxPayData CloseOrder(WxPayData inputObj, int timeOut = 6)
    {
        string url = "https://api.mch.weixin.qq.com/pay/closeorder";
        //检测必填参数
        if (!inputObj.IsSet("out_trade_no"))
        {
            throw new WxPayException("关闭订单接口中,out_trade_no必填!");
        }

        inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
        inputObj.SetValue("sign", inputObj.MakeSign());//签名
        string xml = inputObj.ToXml();

        var start = DateTime.Now;//请求开始时间

        string response = HttpService.Post(xml, url, false, timeOut);

        var end = DateTime.Now;
        int timeCost = (int)((end - start).TotalMilliseconds);

        WxPayData result = new WxPayData();
        result.FromXml(response);

        ReportCostTime(url, timeCost, result);//测速上报

        return result;
    }


    /**
    * 
    * 测速上报
    * @param string interface_url 接口URL
    * @param int timeCost 接口耗时
    * @param WxPayData inputObj参数数组
    */
    private static void ReportCostTime(string interface_url, int timeCost, WxPayData inputObj)
    {
        //如果不需要进行上报
        if (WxPayConfig.REPORT_LEVENL == 0)
        {
            return;
        }

        //如果仅失败上报
        if (WxPayConfig.REPORT_LEVENL == 1 && inputObj.IsSet("return_code") && inputObj.GetValue("return_code").ToString() == "SUCCESS" &&
         inputObj.IsSet("result_code") && inputObj.GetValue("result_code").ToString() == "SUCCESS")
        {
            return;
        }

        //上报逻辑
        WxPayData data = new WxPayData();
        data.SetValue("interface_url", interface_url);
        data.SetValue("execute_time_", timeCost);
        //返回状态码
        if (inputObj.IsSet("return_code"))
        {
            data.SetValue("return_code", inputObj.GetValue("return_code"));
        }
        //返回信息
        if (inputObj.IsSet("return_msg"))
        {
            data.SetValue("return_msg", inputObj.GetValue("return_msg"));
        }
        //业务结果
        if (inputObj.IsSet("result_code"))
        {
            data.SetValue("result_code", inputObj.GetValue("result_code"));
        }
        //错误代码
        if (inputObj.IsSet("err_code"))
        {
            data.SetValue("err_code", inputObj.GetValue("err_code"));
        }
        //错误代码描述
        if (inputObj.IsSet("err_code_des"))
        {
            data.SetValue("err_code_des", inputObj.GetValue("err_code_des"));
        }
        //商户订单号
        if (inputObj.IsSet("out_trade_no"))
        {
            data.SetValue("out_trade_no", inputObj.GetValue("out_trade_no"));
        }
        //设备号
        if (inputObj.IsSet("device_info"))
        {
            data.SetValue("device_info", inputObj.GetValue("device_info"));
        }

        try
        {
            Report(data);
        }
        catch (WxPayException ex)
        {
            //不做任何处理
        }
    }


    /**
    * 
    * 测速上报接口实现
    * @param WxPayData inputObj 提交给测速上报接口的参数
    * @param int timeOut 测速上报接口超时时间
    * @throws WxPayException
    * @return 成功时返回测速上报接口返回的结果,其他抛异常
    */
    public static WxPayData Report(WxPayData inputObj, int timeOut = 1)
    {
        string url = "https://api.mch.weixin.qq.com/payitil/report";
        //检测必填参数
        if (!inputObj.IsSet("interface_url"))
        {
            throw new WxPayException("接口URL,缺少必填参数interface_url!");
        }
        if (!inputObj.IsSet("return_code"))
        {
            throw new WxPayException("返回状态码,缺少必填参数return_code!");
        }
        if (!inputObj.IsSet("result_code"))
        {
            throw new WxPayException("业务结果,缺少必填参数result_code!");
        }
        if (!inputObj.IsSet("user_ip"))
        {
            throw new WxPayException("访问接口IP,缺少必填参数user_ip!");
        }
        if (!inputObj.IsSet("execute_time_"))
        {
            throw new WxPayException("接口耗时,缺少必填参数execute_time_!");
        }

        inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
        inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
        inputObj.SetValue("user_ip", WxPayConfig.IP);//终端ip
        inputObj.SetValue("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商户上报时间  
        inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
        inputObj.SetValue("sign", inputObj.MakeSign());//签名
        string xml = inputObj.ToXml();

        Log.Info("WxPayApi", "Report request : " + xml);

        string response = HttpService.Post(xml, url, false, timeOut);

        Log.Info("WxPayApi", "Report response : " + response);

        WxPayData result = new WxPayData();
        result.FromXml(response);
        return result;
    }

    /**
    * 根据当前系统时间加随机序列来生成订单号
     * @return 订单号
    */
    public static string GenerateOutTradeNo()
    {
        var ran = new Random();
        return string.Format("{0}{1}{2}", WxPayConfig.MCHID, DateTime.Now.ToString("yyyyMMddHHmmss"), ran.Next(999));
    }

    /**
    * 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
     * @return 时间戳
    */
    public static string GenerateTimeStamp()
    {
        TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
        return Convert.ToInt64(ts.TotalSeconds).ToString();
    }

    /**
    * 生成随机串,随机串包含字母或数字
    * @return 随机串
    */
    public static string GenerateNonceStr()
    {
        return Guid.NewGuid().ToString().Replace("-", "");
    }
}

3、WxPayData.cs
using System;
using System.Collections.Generic;
using System.Web;
using System.Xml;
using System.Security.Cryptography;
using System.Text;
using LitJson;

/// <summary>
///Data 的摘要说明
/// </summary>
/// <summary>
/// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构,
/// 在调用接口之前先填充各个字段的值,然后进行接口通信,
/// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构,
/// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构
/// </summary>
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)
        {
            Log.Error(this.GetType().ToString(), "WxPayData数据为空!");
            throw new WxPayException("WxPayData数据为空!");
        }

        string xml = "<xml>";
        foreach (KeyValuePair<string, object> pair in m_values)
        {
            //字段值不能为null,会影响后续流程
            if (pair.Value == null)
            {
                Log.Error(this.GetType().ToString(), "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类型不能含有其他数据类型
            {
                Log.Error(this.GetType().ToString(), "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))
        {
            Log.Error(this.GetType().ToString(), "将空的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
        {
            //错误是没有签名
            if ((string) m_values["return_code"] != "SUCCESS")
            {
                return m_values;
            }
            //CheckSign();//验证签名,不通过会抛异常
        }
        catch (WxPayException ex)
        {
            throw new WxPayException(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)
            {
                Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!");
                throw new WxPayException("WxPayData内部含有值为null的字段!");
            }

            if (pair.Key != "sign" && pair.Value.ToString() != "")
            {
                buff += pair.Key + "=" + pair.Value + "&";
            }
        }
        buff = buff.Trim('&');
        return buff;
    }


    /**
    * @Dictionary格式化成Json
     * @return json串数据
    */
    public string ToJson()
    {
        string jsonStr = JsonMapper.ToJson(m_values);
        return jsonStr;
    }

    /**
    * @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串)
    */
    public string ToPrintStr()
    {
        string str = "";
        foreach (KeyValuePair<string, object> pair in m_values)
        {
            if (pair.Value == null)
            {
                Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!");
                throw new WxPayException("WxPayData内部含有值为null的字段!");
            }

            str += string.Format("{0}={1}<br>", pair.Key, pair.Value.ToString());
        }
        Log.Debug(this.GetType().ToString(), "Print in Web Page : " + str);
        return str;
    }

    /**
    * @生成签名,详见签名生成算法
    * @return 签名, sign字段不参加签名
    */
    public string MakeSign()
    {
        //转url格式
        string str = ToUrl();
        //在string后加入API KEY
        str += "&key=" + WxPayConfig.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"))
        {
            Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法!");
            throw new WxPayException("WxPayData签名存在但不合法!");
        }
        //如果设置了签名但是签名为空,则抛异常
        else if (GetValue("sign") == null || GetValue("sign").ToString() == "")
        {
            Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法!");
            throw new WxPayException("WxPayData签名存在但不合法!");
        }

        //获取接收到的签名
        string return_sign = GetValue("sign").ToString();

        //在本地计算新的签名
        string cal_sign = MakeSign();

        if (cal_sign == return_sign)
        {
            return true;
        }

        Log.Error(this.GetType().ToString(), "WxPayData签名验证错误!");
        throw new WxPayException("WxPayData签名验证错误!");
    }

    /**
    * @获取Dictionary
    */
    public SortedDictionary<string, object> GetValues()
    {
        return m_values;
    }
}

4、Log.cs
using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
/// <summary>
///Log 的摘要说明
/// </summary>
public class Log
{
    //在网站根目录下创建日志目录
    public static string path = HttpContext.Current.Request.PhysicalApplicationPath + "logs";

    /**
     * 向日志文件写入调试信息
     * @param className 类名
     * @param content 写入内容
     */
    public static void Debug(string className, string content)
    {
        if (WxPayConfig.LOG_LEVENL >= 3)
        {
            WriteLog("DEBUG", className, content);
        }
    }

    /**
    * 向日志文件写入运行时信息
    * @param className 类名
    * @param content 写入内容
    */
    public static void Info(string className, string content)
    {
        if (WxPayConfig.LOG_LEVENL >= 2)
        {
            WriteLog("INFO", className, content);
        }
    }

    /**
    * 向日志文件写入出错信息
    * @param className 类名
    * @param content 写入内容
    */
    public static void Error(string className, string content)
    {
        if (WxPayConfig.LOG_LEVENL >= 1)
        {
            WriteLog("ERROR", className, content);
        }
    }

    /**
    * 实际的写日志操作
    * @param type 日志记录类型
    * @param className 类名
    * @param content 写入内容
    */
    protected static void WriteLog(string type, string className, string content)
    {
        if (!Directory.Exists(path))//如果日志目录不存在就创建
        {
            Directory.CreateDirectory(path);
        }

        string time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");//获取当前系统时间
        string filename = path + "/" + DateTime.Now.ToString("yyyy-MM-dd") + ".log";//用日期对日志文件命名

        //创建或打开日志文件,向日志文件末尾追加记录
        StreamWriter mySw = File.AppendText(filename);

        //向日志文件写入内容
        string write_content = time + " " + type + " " + className + ": " + content;
        mySw.WriteLine(write_content);

        //关闭日志文件
        mySw.Close();
    }
}

把上方的方法全部添加后,只需要再后端调用如下方法即可提现,以上是安豆云开提交的代码,快试试吧:
JsApiPay jsApiPay = new JsApiPay(this);
jsApiPay.openid = openid;//openid
jsApiPay.orderid = orderid;//订单id
jsApiPay.productName = productName;//商品名称
jsApiPay.total_fee = int.Parse(total_fee);//提现金额
WxPayData unifiedOrderResult = jsApiPay.GetWithdraw();//执行提现方法
string wxJsApiParam = jsApiPay.GetData();//获取返回数据



0 评论数