微信小程序支付(企业支付给用户零钱)
内容摘要:本案例客户端支付方式为微信小程序支付(JSAPI)。商户运营一段时间后,在微信商户平台开通企业支付服务后,即可调用微信支付提供的企业付款接口将佣金等金额通过微信零钱返现给C端用户零钱。
服务端开发环境:.NET MVC 开发语言C#;
一、准备工作:
1、微信商户平台企业付款服务的开通;开通规则如下:
a、 商户号(或同主体其他非服务商商户号)已入驻90日;
b、截止今日回推30天,商户号(或同主体其他非服务商商户号)连续不间断保持有交易;
c、所有交易必须为真实交易,最好是金额在10元以上,本人的开通经历为:开通日反推30天,每日有正常的购买订单支付流水,期满30天后,商户平台企业支付选项出现,点击开通即可使用此服务;
2、微信公众平台下载商户证书(证书下载参考路径:微信商户平台(pay.weixin.qq.com)-->账户中心-->账户设置-->API安全),并将证书上传至目标服务器(本人为阿里云ECS服务器,IIS 10.0);
3、微信支付相关数据:微信商户号、微信商户密钥等等
二、官方文档参考路径(https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=1_2)
三、查询企业付款接口
1、企业支付接口路径:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
2、企业查询支付结果接口路径:https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo
企业发起付款到用户零钱
/// <summary>
/// 企业付款至用户微信零钱
/// </summary>
/// <param name="openId">待付款的用户openId</param>
/// <param name="applyNo">提现订单号,系统内保持唯一</param>
/// <param name="price">提现金额,单位为“分”</param>
/// <returns></returns>
public JsonResult EnterprisePayToStaffWechatChange(string openId, string applyNo, string price)
{
Result result = new Result();
try
{
#region 参数准备 Dictionary<string, string> parameters = new Dictionary<string, string>(); // 支付金额
parameters.Add("amount", price); // 是否强制校验用户真是姓名
parameters.Add("check_name", WxPayModel.NOCheckRealName); // 企业付款备注
parameters.Add("desc", WxPayModel.PayDescription); // 小程序APPID
parameters.Add("mch_appid", WxPayModel.AppID);
// 微信商户号
parameters.Add("mchid", WxPayModel.mchid); // 随机32位字符串
parameters.Add("nonce_str", WxPayModel.nonceStr); // 用户openid
parameters.Add("openid", openId); // 商户订单号
parameters.Add("partner_trade_no", applyNo + ""); // 企业付款IP地址,当前商家接口服务所在IP地址
parameters.Add("spbill_create_ip", WxPayModel.EnterpriseIPAddress); // 签名信息 WxPayModel.WxMerchantKey为获取微信商户平台密钥
parameters.Add("sign", WxPayCore.GetSignInfo(parameters, WxPayModel.WxMerchantKey)); #endregion #region 向微信提交付款申请,获取返回值,并解析返回值 // 记录一些日志信息... // 获取接口返回xml字符串,WxPayModel.EnterpriseWxPay为企业支付接口地址,即:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
string return_xml_msg = WxPayCore.PostHttpResponseWithCertificate(WxPayModel.EnterpriseWxPay, WxPayCore.CreateXmlParam(parameters)); // 获取提交状态
string return_code = WxPayCore.GetXmlValue(return_xml_msg, "return_code"); // 获取返回信息
string return_msg = WxPayCore.GetXmlValue(return_xml_msg, "return_msg"); // 获取商户appid
string mch_appid = WxPayCore.GetXmlValue(return_xml_msg, "mch_appid"); // 获取商户号
string mchid = WxPayCore.GetXmlValue(return_xml_msg, "mchid"); // 获取随机数
string nonce_str = WxPayCore.GetXmlValue(return_xml_msg, "nonce_str"); // 获取业务结果
string result_code = WxPayCore.GetXmlValue(return_xml_msg, "result_code"); // 获取业务结果错误代码
string err_code = WxPayCore.GetXmlValue(return_xml_msg, "err_code"); // 获取业务结果错误描述
string err_code_des = WxPayCore.GetXmlValue(return_xml_msg, "err_code_des"); // 获取业务结果商户订单号
string partner_trade_no = WxPayCore.GetXmlValue(return_xml_msg, "partner_trade_no"); // 获取业务结果微信付款单号
string payment_no = WxPayCore.GetXmlValue(return_xml_msg, "payment_no"); // 获取业务结果微信付款成功时间
string payment_time = WxPayCore.GetXmlValue(return_xml_msg, "payment_time"); // 针对提交结果进行判断,是否成功提交至微信后台
if (return_code == "SUCCESS")
{
// 记录一些日志信息.... // 微信是否返回付款成功业务处理结果
if (result_code == "SUCCESS")
{
// 记录一些日志信息....
result.ResultCode = Infrastructure.Enum.ReusltCode.OK;
result.ResultMsg = "提现申请已受理,款项预计1-2个工作日到账,请提醒相关人员注意核对!";
}
else
{
// 记录一些日志信息....
result.ResultCode = Infrastructure.Enum.ReusltCode.Fail;
result.ResultMsg = "微信支付业务处理错误,错误代码:【" + err_code + "】,错误信息:【" + err_code_des + "】";
}
}
else
{
// 记录一些日志信息....
result.ResultCode = Infrastructure.Enum.ReusltCode.Fail;
result.ResultMsg = return_msg;
}
#endregion
}
catch (Exception ex)
{
// 记录一些日志信息....
result.ResultCode = Infrastructure.Enum.ReusltCode.OK;
result.ResultMsg = ex.Message;
}
return new JsonResult(result);
}
当然,企业操作打款成功后,需要校验本次业务操作时候已完成,此时,需要使用查询企业付款API
/// <summary>
/// 查询企业是否成功付款至用户微信零钱
/// </summary>
/// <param name="businessNo">提现订单号</param>
/// <returns></returns>
public static JsonResult GetTransferinfo(string businessNo)
{
Result result = new Result();
try
{
#region 参数准备 Dictionary<string, string> parameters = new Dictionary<string, string>(); // 小程序ID
parameters.Add("appid", WxPayModel.AppID); // 商户号
parameters.Add("mch_id", WxPayModel.mchid); // 随机32位字符串
parameters.Add("nonce_str", WxPayModel.nonceStr); // 查询单号
parameters.Add("partner_trade_no", TZR_WxWithdrawalNo); // 签名信息
parameters.Add("sign", WxPayCore.GetSignInfo(parameters, WxPayModel.WxMerchantKey)); #endregion #region 向微信提交查询申请,获取返回值,并解析返回值 // 获取接口返回xml字符串
string return_xml_msg = WxPayCore.PostHttpResponseWithCertificate(WxPayModel.EnterpriseWxPayResultQuery, WxPayCore.CreateXmlParam(parameters),1); // 获取提交状态
string return_code = WxPayCore.GetXmlValue(return_xml_msg, "return_code"); // 获取返回信息
string return_msg = WxPayCore.GetXmlValue(return_xml_msg, "return_msg"); // 获取业务结果
string result_code = WxPayCore.GetXmlValue(return_xml_msg, "result_code"); // 获取业务结果错误代码
string err_code = WxPayCore.GetXmlValue(return_xml_msg, "err_code"); // 获取业务结果错误描述
string err_code_des = WxPayCore.GetXmlValue(return_xml_msg, "err_code_des"); // 加入一些日志信息 if(return_code == "SUCCESS" && result_code == "SUCCESS")
{
// 获取业务结果商户订单号
string partner_trade_no = WxPayCore.GetXmlValue(return_xml_msg, "partner_trade_no"); // 获取商户appid
string mch_appid = WxPayCore.GetXmlValue(return_xml_msg, "appid"); // 获取商户号
string mchid = WxPayCore.GetXmlValue(return_xml_msg, "mch_id"); // 获取付款单号
string detail_id = WxPayCore.GetXmlValue(return_xml_msg, "detail_id"); // 获取付款单号
string status = WxPayCore.GetXmlValue(return_xml_msg, "status"); // 获取失败原因
string reason = WxPayCore.GetXmlValue(return_xml_msg, "reason"); // 获取收款用户openid
string openid = WxPayCore.GetXmlValue(return_xml_msg, "openid"); // 获取收款用户姓名
string transfer_name = WxPayCore.GetXmlValue(return_xml_msg, "transfer_name"); // 获取付款金额(付款金额单位为“分”)
string payment_amount = WxPayCore.GetXmlValue(return_xml_msg, "payment_amount"); // 获取转账时间
string transfer_time = WxPayCore.GetXmlValue(return_xml_msg, "transfer_time"); // 获取付款成功时间
string payment_time = WxPayCore.GetXmlValue(return_xml_msg, "payment_time"); // 获取企业付款备注
string desc = WxPayCore.GetXmlValue(return_xml_msg, "desc"); //获取付款单号
string statusName = "处理中"; //判断状态
if(status== "SUCCESS")
{
// 加入一些日志信息
statusName = "成功,到账时间:"+ payment_time;
}
else if (status == "FAILED")
{
// 加入一些日志信息
statusName = "转账失败,原因:"+ reason;
}
else
{
// 加入一些日志信息
statusName = "处理中,请耐心等待,提交时间:" + transfer_time;
} //处理金额
decimal price = 0M; decimal.TryParse(payment_amount, out price); price = price / 100; // 微信是否返回付款成功业务处理结果
if (result_code == "SUCCESS")
{
// 加入一些日志信息
result.ResultCode = Infrastructure.Enum.ReusltCode.OK;
result.ResultMsg = "查询成功,订单【"+ partner_trade_no + "】,金额【"+ price + "】,提现"+ statusName;
}
else
{
// 加入一些日志信息
result.ResultCode = Infrastructure.Enum.ReusltCode.Fail;
result.ResultMsg = "微信支付业务处理错误,错误代码:【" + err_code + "】,错误信息:【" + err_code_des + "】";
}
}
else
{
// 加入一些日志信息
result.ResultCode = Infrastructure.Enum.ReusltCode.Fail;
result.ResultMsg = return_msg;
}
#endregion
}
catch (Exception ex)
{
// 加入一些日志信息
result.ResultCode = Infrastructure.Enum.ReusltCode.OK;
result.ResultMsg = ex.Message;
}
return new JsonResult(result);
}
至此,企业付款到用户零钱业务基本走完。
其实,微信还开放了付款至用户银行卡服务接口,操作流程与上述流程大同小异,在此不再赘述。
四、公共方法
字典转换XML数据 (拼接成微信要求的XML请求数据格式)
/// <summary>
/// 集合转换XML数据 (拼接成XML请求数据)
/// </summary>
/// <param name="strParam">参数</param>
/// <returns></returns>
public static string CreateXmlParam(Dictionary<string, string> strParam)
{
StringBuilder sb = new StringBuilder();
try
{
sb.Append("<xml>");
foreach (KeyValuePair<string, string> k in strParam)
{
if (k.Key == "attach" || k.Key == "body" || k.Key == "sign")
{
sb.Append("<" + k.Key + "><![CDATA[" + k.Value + "]]></" + k.Key + ">");
}
else
{
sb.Append("<" + k.Key + ">" + k.Value + "</" + k.Key + ">");
}
}
sb.Append("</xml>");
}
catch (Exception ex)
{
throw ex;
// AddLog("PayHelper", "CreateXmlParam", ex.Message, ex);
}
var a = sb.ToString();
return sb.ToString();
}
生成随机32位字符串
/// <summary>
/// 随机字符串不长于 32 位
/// </summary>
public static string nonceStr = RandomNum.CreateRandomNum(32).ToUpper(); /// <summary>
/// 生产随机数
/// </summary>
/// <param name="NumCount"></param>
/// <returns></returns>
public static string CreateRandomNum(int NumCount)
{
string allChar = "2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,P,Q,R,S,T,U,W,X,Y,Z,a,b,c,d,e,f,g,h,j,k,m,n,o,p,q,s,t,u,w,x,y,z";
string[] allCharArray = allChar.Split(',');//拆分成数组
string randomNum = "";
int temp = -1; //记录上次随机数的数值,尽量避免产生几个相同的随机数
Random rand = new Random();
for (int i = 0; i < NumCount; i++)
{
if (temp != -1)
{
rand = new Random(i * temp * ((int)DateTime.Now.Ticks));
}
int t = rand.Next(35);
if (temp == t)
{
return CreateRandomNum(NumCount);
}
temp = t;
randomNum += allCharArray[t];
}
return randomNum;
}
获取签名
/// <summary>
/// 获取签名数据
///</summary>
/// <param name="strParam">支付参数</param>
/// <param name="key">商户密钥</param>
/// <returns></returns>
public static string GetSignInfo(Dictionary<string, string> strParam, string key)
{
int i = 0;
string sign = string.Empty;
StringBuilder sb = new StringBuilder();
try
{
foreach (KeyValuePair<string, string> temp in strParam)
{
if (temp.Value == "" || temp.Value == null || temp.Key.ToLower() == "sign")
{
continue;
}
i++;
sb.Append(temp.Key.Trim() + "=" + temp.Value.Trim() + "&");
}
sb.Append("key=" + key.Trim() + "");
sign = CryptoService.Md5EncryptStr(sb.ToString()).ToUpper();
}
catch (Exception ex)
{
throw ex;
}
return sign;
}
POST方式向微信服务器提交数据
/// <summary>
/// post提交至微信服务器(需要微信API证书)
/// </summary>
/// <param name="url">微信企业支付路径</param>
/// <param name="xmlParam">服务提交的签名参数</param>
/// <returns></returns>
public static string PostHttpResponseWithCertificate(string url, string xmlParam)
{
//微信API证书相对路径
string SSLCERT_PATH = "";
string SSLCERT_PASSWORD = ""; //微信API证书相对路径
SSLCERT_PATH = WxPayModel.SSLCERT_PATH; //垃圾回收,回收没有正常关闭的http连接
System.GC.Collect(); //微信服务器返回结果
string result = ""; //HTTP请求对象
HttpWebRequest request = null; //HTTP响应对象
HttpWebResponse response = null; //数据流对象
Stream reqStream = null; try
{
//设置最大连接数
ServicePointManager.DefaultConnectionLimit = 200; //设置https验证方式,是否为https请求
if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
{
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
} /***************************************************************
* 下面设置HttpWebRequest的相关属性
* ************************************************************/
request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST"; //设置超时时间 默认时长为100秒(100,000 ms)
request.Timeout = 60000; //设置POST的数据类型和长度
request.ContentType = "text/xml";
byte[] data = System.Text.Encoding.UTF8.GetBytes(xmlParam);
request.ContentLength = data.Length; //使用证书
string path = HttpContext.Current.Request.PhysicalApplicationPath; X509Certificate2 cert = new X509Certificate2(SSLCERT_PATH, SSLCERT_PASSWORD, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet); //添加证书凭据
request.ClientCertificates.Add(cert); //往服务器写入数据
reqStream = request.GetRequestStream();
reqStream.Write(data, 0, data.Length);
reqStream.Close(); //获取服务端返回
response = (HttpWebResponse)request.GetResponse(); //获取服务端返回数据
StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
result = sr.ReadToEnd().Trim();
sr.Close();
}
catch (System.Threading.ThreadAbortException e)
{
System.Threading.Thread.ResetAbort();
}
catch (WebException e)
{
throw new Exception(e.ToString());
}
catch (Exception e)
{
throw new Exception(e.ToString());
}
finally
{
//关闭连接和流
if (response != null)
{
response.Close();
}
if (request != null)
{
request.Abort();
}
}
return result;
}
解析微信返回的XML对象
/// <summary>
/// 获取XML值
/// </summary>
/// <param name="strXml">XML字符串</param>
/// <param name="strData">节点值</param>
/// <returns></returns>
public static string GetXmlValue(string strXml, string strData)
{
string xmlValue = string.Empty;
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(strXml);
var selectSingleNode = xmlDocument.DocumentElement.SelectSingleNode(strData);
if (selectSingleNode != null)
{
xmlValue = selectSingleNode.InnerText;
}
return xmlValue;
}
微信小程序支付(企业支付给用户零钱)的更多相关文章
- 微信小程序个人/企业开放服务类目一览表
微信小程序个人/企业开放服务类目一览表 微信小程序个人开放服务类目表 服务类目 类目分类一 类目分类二 引导描述 出行与交通 代驾 / / 生活服务 家政.丽人.摄影/扩印.婚庆服务.环保回收/废 ...
- 微信小程序-form表单-获取用户输入文本框的值
微信小程序-form表单-获取用户输入文本框的值 <input name='formnickname' class="textarea" placeholder=" ...
- 让你的微信小程序具有在线支付功能
前言 最近需要在微信小程序中用到在线支付功能,于是看了一下官方的文档,发现要在小程序里实现微信支付还是很方便的,如果你以前开发过服务号下的微信支付,那么你会发现其实小程序里的微信支付和服务号里的开发过 ...
- 微信小程序书简易支付
这里结合了上一篇的手机号登录接下来的实现功能 https://www.cnblogs.com/xiaoyantongxue/p/15472915.html 登录后进入课程选择页面 1:数据库填入数据 ...
- 微信小程序登录流程及解析用户openid session_key,获取用户信息
为优化用户体验,使用 wx.getUserInfo 接口直接弹出授权框的开发方式将逐步不再支持.从2018年4月30日开始,小程序与小游戏的体验版.开发版调用 wx.getUserInfo 接口,将无 ...
- 微信小程序button授权页面,用户拒绝后仍可再次授权
微信小程序授权页面,进入小程序如果没授权跳转到授权页面,授权后跳转到首页,如果用户点拒绝下次进入小程序还是能跳转到授权页面,授权页面如下 app.js 中的 onLaunch或onShow中加如下代 ...
- 微信小程序-登陆、支付、模板消息
wx.login(OBJECT) 调用接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key).用户数据的加解密通讯需要 ...
- 微信小程序发布与支付
一.小程序的发布流程 小程序协同工作和发布官网链接 1.背景 小程序的平台里,开发者完成开发之后,需要在开发者工具提交小程序的代码包,然后在小程序后台发布小程序. 2.流程 上传代码 代码管理服务器上 ...
- 【小程序】微信小程序绑定企业微信后怎样获取到用户信息
一.获取access_token 1.https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRECT Cor ...
- 微信小程序~App.js中获取用户信息
(1)代码:主要介绍下获取用户信息部分 onLaunch: function () { // 展示本地存储能力 var logs = wx.getStorageSync('logs') || [] l ...
随机推荐
- 学习Vue第二节,v-cloak,v-text,v-html,v-bind,v-on使用
v-cloak,v-text,v-html,v-bind,v-on使用 <!DOCTYPE html> <html> <head> <meta charset ...
- springboot配置静态资源访问路径
其实在springboot中静态资源的映射文件是在resources目录下的static文件夹,springboot推荐我们将静态资源放在static文件夹下,因为默认配置就是classpath:/s ...
- AbstractList源码分析
AbstractList 1 类图 2 字段 // 默认容量 private static final int DEFAULT_CAPACITY = 10; // 共享的空数组 private sta ...
- vue 如何实现 Input 输入框模糊查询方法
原理:原生js的indexOf() 方法,该方法将从头到尾地检索数组,看它是否含有对应的元素.开始检索的位置在数组 start 处或数组的开头(没有指定 start 参数时).如果找到一个 item, ...
- [hdu4585]离线,并查集
题意:把一些数加到集合里面,每个数有两个属性,分别是编号和值,加进去之前询问集合里面距离自己“最近”的数的编号.最近的意思是值的差的绝对值最小,如果有相等的,则取值小的.并且已知所有数的id和valu ...
- Azure AD(二)调用受Microsoft 标识平台保护的 ASP.NET Core Web API 下
一,引言 上一节讲到如何在我们的项目中集成Azure AD 保护我们的API资源,以及在项目中集成Swagger,并且如何把Swagger作为一个客户端进行认证和授权去访问我们的WebApi资源的?本 ...
- js canvas压缩图片和jQuery ajax上传图片简单demo
原来用的插件,里面东西太乱了,一会jq,一会原生js,本来原生js就不熟,看起来更难受,而且感觉好多东西都是没用的,而且后端php转存文件一直不是很熟悉,正好一起整理一下.就是很简单的一个demo,如 ...
- Cannot parse "1986-05-04": Illegal instant due to time zone offset transition (Asia/Shanghai)
调查系统错误时,发现了一个很奇怪的现象,出生日期1986-05-04号的用户始终无法注册.发现后台使用使用jodatime的代码demo如下: public static DateTime parse ...
- spring cloud系列教程第四篇-Eureka基础知识
通过前三篇文章学习,我们搭建好了两个微服务工程.即:order80和payment8001这两个服务.有了这两个基础的框架之后,我们将要开始往里面添加东西了.还记得分布式架构的几个维度吗?我们要通过一 ...
- Java的泛型详解(一)
Java的泛型详解 泛型的好处 编写的代码可以被不同类型的对象所重用. 因为上面的一个优点,泛型也可以减少代码的编写. 泛型的使用 简单泛型类 public class Pair<T> { ...