阅读目录

  • 一.支付宝

  •  二.微信

一.支付宝

支付宝提现官方名称为  单笔转账到支付宝账户  主要分为2个接口 :

1.alipay.fund.trans.toaccount.transfer(单笔转账到支付宝账户接口)  官方文档:https://docs.open.alipay.com/api_28/alipay.fund.trans.toaccount.transfer

2.alipay.fund.trans.order.query(查询转账订单接口)  官方文档https://docs.open.alipay.com/api_28/alipay.fund.trans.order.query/

ps:其中查询转账接口主要是为了防止漏单 在错误码为20000,40004,或者编码为SYSTEM_ERROR 时需要使用该接口查询是否已经转账过 根据接口返回值判断是否已经打款 如果未打款需要重新发起打款 如果重试后失败需要加入出错列表或其他处理 随业务需要而定

主要接口 alipay.fund.trans.toaccount.transfer

调用时准备步骤:

1. 创建应用并获取APPID

2.添加功能

测试时可使用沙盒内数据

3.配置应用环境

生成秘钥

4.下载sdk

5.调用

其中调用时 要注意在调用中放的公钥为支付宝公钥 而不是应用公钥 如果传错会造成转账成功但是报sign check fail: check Sign and Data Fail!错误

转账金额 小数点最多只能2位 文本类型 和微信提现有差别 微信单位为分

附代码例子:

//沙箱数据
string serverUrl = "https://openapi.alipaydev.com/gateway.do"; 
 string appId = "test appid";
//私钥
string privateKeyPem ="";
 //支付宝公钥
 string alipay_public_key = "";
try
            {
                IAopClient client = new DefaultAopClient(serverUrl, appId, privateKeyPem, "json", "1.0", "RSA2", alipay_public_key, "GBK", false);
                AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
                request.BizContent = "{" +
    "    \"out_biz_no\":\"" + sn + "\"," +//商户转账唯一订单号
    "    \"payee_type\":\"ALIPAY_LOGONID\"," +//ALIPAY_LOGONID:支付宝登录号
    "    \"payee_account\":\"" + payee + "\"," +//收款方账户
    "    \"amount\":\"" + Math.Round(amount, 2).ToString() + "\"," +//转账金额
    "    \"payee_real_name\":\"" + userName + "\"," +//收款方真实姓名
    "    \"remark\":\"用户提现打款\"," +
    "  }";
                var response = client.Execute(request);
                if (response.Code == "10000")
                {
                    return true;
                }
                else
                {
                    LogInfo(string.Format("单号:{1} 支付宝支付失败 原因:{0}", response.SubMsg, sn));
                    if (response.Code == "20000" || response.Code == "40004" || response.SubCode == "SYSTEM_ERROR")
                    {
                        //掉单
                        //查询转账结果
                        AlipayFundTransOrderQueryRequest _request = new AlipayFundTransOrderQueryRequest();
                        request.BizContent = "{" +
                        "\"out_biz_no\":\"" + sn + "\"," +
                        "\"order_id\":\"" + response.OrderId + "\"" +
                        "  }";
                        var _response = client.Execute(_request);
                        if (_response.Code == "10000" && _response.Status == "SUCCESS" && !string.IsNullOrEmpty(_response.PayDate) && string.IsNullOrEmpty(_response.ErrorCode))
                        {
                            return true;//掉单 网络问题实际转账成功
                        }
                        else
                        {
                            if (errorTimes <= 3)
                            {
                                //增加错误数
                                using (MongoDbProviderFactory factory = base.NewWashcarFactory())
                                {
                                    WithdrawalsApplyRecordAccess access = new WithdrawalsApplyRecordAccess(factory.Context);
                                    var rec = access.Single(recordId);
                                    if (rec != null)
                                    {
                                        rec.ErrorTimes = rec.ErrorTimes + 1;
                                        access.Update(rec);
                                    }
                                }
                                //重新打款一次
                                response = client.Execute(request);
                                if (response.Code == "10000")
                                {
                                    return true;
                                }
                                else
                                {
                                    LogInfo(string.Format("单号:{1} 支付宝支付失败 原因:{0}", response.SubMsg, sn));
                                    return false;
                                }
                            }
                        }
                    }
                    return false;
                }
            }
            catch (Exception ex)
            {
                LogInfo(string.Format("单号:{1} 支付宝支付失败 ex:{0}", ex.Message, sn));
                return false;
            }

提现关键代码片段

 二.微信

微信功能官方名称 企业付款 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2  文档很全面  

开发步骤:

1.注册服务号

2.注册微信商户号

3.在商户号后台对AppID授权 开通企业付款到零钱功能

4.下载证书

5.调用接口

注意事项:

1.打款 是预付款形式 必须先在商户平台充值

2.签名需要根据ASCII码排序 并加密 详见文档

3.证书需要放在服务器非虚拟路径处 防止被非法下载盗用 代码中必须调用 否则证书安装了没效果

4.必须通过微信的openId 获取被转账人 所以需要在调用之前根据业务绑定微信和用户账号

代码部分:

/// <summary>
        /// 微信提现
        /// </summary>
        /// <param name="sn">单号</param>
        /// <param name="wxOpenId">微信用户openid</param>
        /// <param name="userName">用户真实姓名</param>
        /// <param name="amount">金额 单位分</param>
        private bool WechatPay(string sn, string wxOpenId, string userName, int amount)
        {
            if (string.IsNullOrEmpty(sn)) { LogAndSetMsg(string.Format("单号为空 转账失败")); return false; }
            if (string.IsNullOrEmpty(wxOpenId)) { LogAndSetMsg(string.Format("单号{0} 微信用户openId为空 转账失败", sn)); return false; }
            if (string.IsNullOrEmpty(userName)) { LogAndSetMsg(string.Format("单号{0} 用户真实姓名为空 转账失败", sn)); return false; }
            #region 微信支付配置区
            string mch_appid = "";//商户账号appid
            string mchid = "";//商户号
            string key = "";//秘钥
            string cert = @"E:\certificate\wechart\apiclient_cert.p12";//证书路径
            #endregion
            string nonce_str = Guid.NewGuid().ToString().Replace("-", "");//随机字符串
            string sign = "";//签名
            string partner_trade_no = sn;//商户订单号
            string openid = wxOpenId;//用户openid
            string check_name = "FORCE_CHECK";//强校验真实姓名
            string re_user_name = userName;//收款用户姓名
            string desc = "用户提现打款";//企业付款描述信息
            string spbill_create_ip = GetLocalIP();//Ip地址
            Dictionary<string, string> parm = new Dictionary<string, string>();
            parm.Add("mch_appid", mch_appid);
            parm.Add("mchid", mchid);
            parm.Add("nonce_str", nonce_str);
            parm.Add("partner_trade_no", partner_trade_no);
            parm.Add("openid", openid);
            parm.Add("check_name", check_name);
            parm.Add("re_user_name", re_user_name);
            parm.Add("amount", amount.ToString());
            parm.Add("desc", desc);
            parm.Add("spbill_create_ip", spbill_create_ip);
            //设置签名
            var stringSignTemp = getParamSrc(parm);//签名处理中间变量
            stringSignTemp = stringSignTemp + "&key=" + key;
            sign = GetMD5(stringSignTemp).ToUpper();
            parm.Add("sign", sign);
            XDocument xml;
            try
            {//发送带证书的请求
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
                X509Certificate cer = new X509Certificate(cert, mchid);
                HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers");
                webrequest.ClientCertificates.Add(cer);
                webrequest.ContentType = "application/x-www-form-urlencoded";
                webrequest.Method = "post";
                //拼接post请求
                string parmStr = "<xml>";
                parmStr += "<amount>" + amount + "</amount>";
                parmStr += "<check_name>"+check_name+"</check_name>";
                parmStr += "<desc>"+desc+"</desc>";
                parmStr += "<mch_appid>"+ mch_appid + "</mch_appid>";
                parmStr += "<mchid>"+ mchid + "</mchid>";
                parmStr += "<nonce_str>"+ nonce_str + "</nonce_str>";
                parmStr += "<openid>"+ openid + "</openid>";
                parmStr += "<partner_trade_no>"+ partner_trade_no + "</partner_trade_no>";
                parmStr += "<re_user_name>"+ re_user_name + "</re_user_name>";
                parmStr += "<spbill_create_ip>"+ spbill_create_ip + "</spbill_create_ip>";
                parmStr += "<sign>"+ sign + "</sign>";
                parmStr += "</xml>";
                byte[] byteArray = Encoding.UTF8.GetBytes(parmStr); //转化
                webrequest.ContentLength = byteArray.Length;
                using (Stream newStream = webrequest.GetRequestStream())
                {
                    newStream.Write(byteArray, 0, byteArray.Length);//写入参数
                }
                HttpWebResponse webreponse = (HttpWebResponse)webrequest.GetResponse();
                Stream stream = webreponse.GetResponseStream();
                StreamReader sr = new StreamReader(webreponse.GetResponseStream(), Encoding.UTF8);
                string str = sr.ReadToEnd();
                xml = XDocument.Load(stream);
            }
            catch (Exception ex)
            {
                LogAndSetMsg(string.Format("单号:{1} 发送带证书的请求失败 ex:{0}", ex.Message, sn));
                return false;
            }
            //解析返回结果
            if (xml == null) { LogAndSetMsg(string.Format("解析微信返回值出错 单号:{0}", sn)); return false; }
            //获取根节点
            var root = xml.Root;
            var node = root.Element("result_code");
            if (node != null && node.Value == "SUCCESS")
            {
                return true;
            }
            else
            {
                var err = root.Element("return_msg");
                if (err != null)
                {
                    LogAndSetMsg(string.Format("支付失败:{0} 单号:{1}", err.Value, sn));
                }
                else
                {
                    LogAndSetMsg(string.Format("支付失败:原因未知 单号:{0}", sn));
                }
                return false;
            }
        }

关键方法
//加密
        public static string GetMD5(string pwd)
        {
            MD5 md5Hasher = MD5.Create();

            byte[] data = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(pwd));

            StringBuilder sBuilder = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            return sBuilder.ToString();
        }

MD5
/// <summary>
        /// 参数按照ASCII码从小到大排序
        /// </summary>
        /// <param name="paramsMap"></param>
        /// <returns></returns>
        private string getParamSrc(Dictionary<string, string> paramsMap)
        {
            var vDic = (from objDic in paramsMap orderby objDic.Key ascending select objDic);
            StringBuilder str = new StringBuilder();
            foreach (KeyValuePair<string, string> kv in vDic)
            {
                string pkey = kv.Key;
                string pvalue = kv.Value;
                str.Append(pkey + "=" + pvalue + "&");
            }

            string result = str.ToString().Substring(0, str.ToString().Length - 1);
            return result;
        }

根据AscII码排序参数为文本
/// <summary>
        /// 获取本机ip
        /// </summary>
        /// <returns></returns>
        private string GetLocalIP()
        {
            try
            {
                string HostName = Dns.GetHostName(); //得到主机名
                IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
                for (int i = 0; i < IpEntry.AddressList.Length; i++)
                {
                    //从IP地址列表中筛选出IPv4类型的IP地址
                    //AddressFamily.InterNetwork表示此IP为IPv4,
                    //AddressFamily.InterNetworkV6表示此地址为IPv6类型
                    if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
                    {
                        return IpEntry.AddressList[i].ToString();
                    }
                }
                LogInfo("未能获取到本机IP");
                return "";
            }
            catch (Exception ex)
            {
                LogInfo("获取本机IP出错:" + ex.Message);
                return "";
            }
        }

获取本机IP
private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{        
     if (errors == SslPolicyErrors.None)
             return true;5 
     return false;6        
}

注:

1. post请求参数为xml格式 

2.SYSTEMERROR 错误时 需要原单号重试 否则会多次支付 代码中未展示

3.证书出错  官方解决方案:https://pay.weixin.qq.com/wiki/doc/api/download/cert.zip 里面有一个word文档

还有个简洁的做法就是 服务的情况下 在属性--->登录--->选择此账户 选择管理员账户


相关推荐