一.支付宝
支付宝提现官方名称为 单笔转账到支付宝账户 主要分为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位 文本类型 和微信提现有差别 微信单位为分
附代码例子:
1 //沙箱数据 2 string serverUrl = "https://openapi.alipaydev.com/gateway.do"; 3 string appId = "test appid"; 4 //私钥 5 string privateKeyPem =""; 6 //支付宝公钥 7 string alipay_public_key = ""; 8 try 9 { 10 IAopClient client = new DefaultAopClient(serverUrl, appId, privateKeyPem, "json", "1.0", "RSA2", alipay_public_key, "GBK", false); 11 AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest(); 12 request.BizContent = "{" + 13 " "out_biz_no":"" + sn + ""," +//商户转账唯一订单号 14 " "payee_type":"ALIPAY_LOGONID"," +//ALIPAY_LOGONID:支付宝登录号 15 " "payee_account":"" + payee + ""," +//收款方账户 16 " "amount":"" + Math.Round(amount, 2).ToString() + ""," +//转账金额 17 " "payee_real_name":"" + userName + ""," +//收款方真实姓名 18 " "remark":"用户提现打款"," + 19 " }"; 20 var response = client.Execute(request); 21 if (response.Code == "10000") 22 { 23 return true; 24 } 25 else 26 { 27 LogInfo(string.Format("单号:{1} 支付宝支付失败 原因:{0}", response.SubMsg, sn)); 28 if (response.Code == "20000" || response.Code == "40004" || response.SubCode == "SYSTEM_ERROR") 29 { 30 //掉单 31 //查询转账结果 32 AlipayFundTransOrderQueryRequest _request = new AlipayFundTransOrderQueryRequest(); 33 request.BizContent = "{" + 34 ""out_biz_no":"" + sn + ""," + 35 ""order_id":"" + response.OrderId + """ + 36 " }"; 37 var _response = client.Execute(_request); 38 if (_response.Code == "10000" && _response.Status == "SUCCESS" && !string.IsNullOrEmpty(_response.PayDate) && string.IsNullOrEmpty(_response.ErrorCode)) 39 { 40 return true;//掉单 网络问题实际转账成功 41 } 42 else 43 { 44 if (errorTimes <= 3) 45 { 46 //增加错误数 47 using (MongoDbProviderFactory factory = base.NewWashcarFactory()) 48 { 49 WithdrawalsApplyRecordAccess access = new WithdrawalsApplyRecordAccess(factory.Context); 50 var rec = access.Single(recordId); 51 if (rec != null) 52 { 53 rec.ErrorTimes = rec.ErrorTimes + 1; 54 access.Update(rec); 55 } 56 } 57 //重新打款一次 58 response = client.Execute(request); 59 if (response.Code == "10000") 60 { 61 return true; 62 } 63 else 64 { 65 LogInfo(string.Format("单号:{1} 支付宝支付失败 原因:{0}", response.SubMsg, sn)); 66 return false; 67 } 68 } 69 } 70 } 71 return false; 72 } 73 } 74 catch (Exception ex) 75 { 76 LogInfo(string.Format("单号:{1} 支付宝支付失败 ex:{0}", ex.Message, sn)); 77 return false; 78 }
二.微信
微信功能官方名称 企业付款 文档地址: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 获取被转账人 所以需要在调用之前根据业务绑定微信和用户账号
代码部分:
1 /// <summary> 2 /// 微信提现 3 /// </summary> 4 /// <param name="sn">单号</param> 5 /// <param name="wxOpenId">微信用户openid</param> 6 /// <param name="userName">用户真实姓名</param> 7 /// <param name="amount">金额 单位分</param> 8 private bool WechatPay(string sn, string wxOpenId, string userName, int amount) 9 { 10 if (string.IsNullOrEmpty(sn)) { LogAndSetMsg(string.Format("单号为空 转账失败")); return false; } 11 if (string.IsNullOrEmpty(wxOpenId)) { LogAndSetMsg(string.Format("单号{0} 微信用户openId为空 转账失败", sn)); return false; } 12 if (string.IsNullOrEmpty(userName)) { LogAndSetMsg(string.Format("单号{0} 用户真实姓名为空 转账失败", sn)); return false; } 13 #region 微信支付配置区 14 string mch_appid = "";//商户账号appid 15 string mchid = "";//商户号 16 string key = "";//秘钥 17 string cert = @"E:certificatewechartapiclient_cert.p12";//证书路径 18 #endregion 19 string nonce_str = Guid.NewGuid().ToString().Replace("-", "");//随机字符串 20 string sign = "";//签名 21 string partner_trade_no = sn;//商户订单号 22 string openid = wxOpenId;//用户openid 23 string check_name = "FORCE_CHECK";//强校验真实姓名 24 string re_user_name = userName;//收款用户姓名 25 string desc = "用户提现打款";//企业付款描述信息 26 string spbill_create_ip = GetLocalIP();//Ip地址 27 Dictionary<string, string> parm = new Dictionary<string, string>(); 28 parm.Add("mch_appid", mch_appid); 29 parm.Add("mchid", mchid); 30 parm.Add("nonce_str", nonce_str); 31 parm.Add("partner_trade_no", partner_trade_no); 32 parm.Add("openid", openid); 33 parm.Add("check_name", check_name); 34 parm.Add("re_user_name", re_user_name); 35 parm.Add("amount", amount.ToString()); 36 parm.Add("desc", desc); 37 parm.Add("spbill_create_ip", spbill_create_ip); 38 //设置签名 39 var stringSignTemp = getParamSrc(parm);//签名处理中间变量 40 stringSignTemp = stringSignTemp + "&key=" + key; 41 sign = GetMD5(stringSignTemp).ToUpper(); 42 parm.Add("sign", sign); 43 XDocument xml; 44 try 45 {//发送带证书的请求 46 ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); 47 X509Certificate cer = new X509Certificate(cert, mchid); 48 HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"); 49 webrequest.ClientCertificates.Add(cer); 50 webrequest.ContentType = "application/x-www-form-urlencoded"; 51 webrequest.Method = "post"; 52 //拼接post请求 53 string parmStr = "<xml>"; 54 parmStr += "<amount>" + amount + "</amount>"; 55 parmStr += "<check_name>"+check_name+"</check_name>"; 56 parmStr += "<desc>"+desc+"</desc>"; 57 parmStr += "<mch_appid>"+ mch_appid + "</mch_appid>"; 58 parmStr += "<mchid>"+ mchid + "</mchid>"; 59 parmStr += "<nonce_str>"+ nonce_str + "</nonce_str>"; 60 parmStr += "<openid>"+ openid + "</openid>"; 61 parmStr += "<partner_trade_no>"+ partner_trade_no + "</partner_trade_no>"; 62 parmStr += "<re_user_name>"+ re_user_name + "</re_user_name>"; 63 parmStr += "<spbill_create_ip>"+ spbill_create_ip + "</spbill_create_ip>"; 64 parmStr += "<sign>"+ sign + "</sign>"; 65 parmStr += "</xml>"; 66 byte[] byteArray = Encoding.UTF8.GetBytes(parmStr); //转化 67 webrequest.ContentLength = byteArray.Length; 68 using (Stream newStream = webrequest.GetRequestStream()) 69 { 70 newStream.Write(byteArray, 0, byteArray.Length);//写入参数 71 } 72 HttpWebResponse webreponse = (HttpWebResponse)webrequest.GetResponse(); 73 Stream stream = webreponse.GetResponseStream(); 74 StreamReader sr = new StreamReader(webreponse.GetResponseStream(), Encoding.UTF8); 75 string str = sr.ReadToEnd(); 76 xml = XDocument.Load(stream); 77 } 78 catch (Exception ex) 79 { 80 LogAndSetMsg(string.Format("单号:{1} 发送带证书的请求失败 ex:{0}", ex.Message, sn)); 81 return false; 82 } 83 //解析返回结果 84 if (xml == null) { LogAndSetMsg(string.Format("解析微信返回值出错 单号:{0}", sn)); return false; } 85 //获取根节点 86 var root = xml.Root; 87 var node = root.Element("result_code"); 88 if (node != null && node.Value == "SUCCESS") 89 { 90 return true; 91 } 92 else 93 { 94 var err = root.Element("return_msg"); 95 if (err != null) 96 { 97 LogAndSetMsg(string.Format("支付失败:{0} 单号:{1}", err.Value, sn)); 98 } 99 else 100 { 101 LogAndSetMsg(string.Format("支付失败:原因未知 单号:{0}", sn)); 102 } 103 return false; 104 } 105 }
1 //加密 2 public static string GetMD5(string pwd) 3 { 4 MD5 md5Hasher = MD5.Create(); 5 6 byte[] data = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(pwd)); 7 8 StringBuilder sBuilder = new StringBuilder(); 9 for (int i = 0; i < data.Length; i++) 10 { 11 sBuilder.Append(data[i].ToString("x2")); 12 } 13 14 return sBuilder.ToString(); 15 }
1 /// <summary> 2 /// 参数按照ASCII码从小到大排序 3 /// </summary> 4 /// <param name="paramsMap"></param> 5 /// <returns></returns> 6 private string getParamSrc(Dictionary<string, string> paramsMap) 7 { 8 var vDic = (from objDic in paramsMap orderby objDic.Key ascending select objDic); 9 StringBuilder str = new StringBuilder(); 10 foreach (KeyValuePair<string, string> kv in vDic) 11 { 12 string pkey = kv.Key; 13 string pvalue = kv.Value; 14 str.Append(pkey + "=" + pvalue + "&"); 15 } 16 17 string result = str.ToString().Substring(0, str.ToString().Length - 1); 18 return result; 19 }
1 /// <summary> 2 /// 获取本机ip 3 /// </summary> 4 /// <returns></returns> 5 private string GetLocalIP() 6 { 7 try 8 { 9 string HostName = Dns.GetHostName(); //得到主机名 10 IPHostEntry IpEntry = Dns.GetHostEntry(HostName); 11 for (int i = 0; i < IpEntry.AddressList.Length; i++) 12 { 13 //从IP地址列表中筛选出IPv4类型的IP地址 14 //AddressFamily.InterNetwork表示此IP为IPv4, 15 //AddressFamily.InterNetworkV6表示此地址为IPv6类型 16 if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork) 17 { 18 return IpEntry.AddressList[i].ToString(); 19 } 20 } 21 LogInfo("未能获取到本机IP"); 22 return ""; 23 } 24 catch (Exception ex) 25 { 26 LogInfo("获取本机IP出错:" + ex.Message); 27 return ""; 28 } 29 }
1 private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) 2 { 3 if (errors == SslPolicyErrors.None) 4 return true; 5 return false; 6 }
注:
1. post请求参数为xml格式
2.SYSTEMERROR 错误时 需要原单号重试 否则会多次支付 代码中未展示
3.证书出错 官方解决方案:https://pay.weixin.qq.com/wiki/doc/api/download/cert.zip 里面有一个word文档
还有个简洁的做法就是 服务的情况下 在属性--->登录--->选择此账户 选择管理员账户