内容摘要:本案例客户端支付方式为微信小程序支付(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;
}

  

微信小程序支付(企业支付给用户零钱)的更多相关文章

  1. 微信小程序个人/企业开放服务类目一览表

    微信小程序个人/企业开放服务类目一览表   微信小程序个人开放服务类目表 服务类目 类目分类一 类目分类二 引导描述 出行与交通 代驾 / / 生活服务 家政.丽人.摄影/扩印.婚庆服务.环保回收/废 ...

  2. 微信小程序-form表单-获取用户输入文本框的值

    微信小程序-form表单-获取用户输入文本框的值 <input name='formnickname' class="textarea" placeholder=" ...

  3. 让你的微信小程序具有在线支付功能

    前言 最近需要在微信小程序中用到在线支付功能,于是看了一下官方的文档,发现要在小程序里实现微信支付还是很方便的,如果你以前开发过服务号下的微信支付,那么你会发现其实小程序里的微信支付和服务号里的开发过 ...

  4. 微信小程序书简易支付

    这里结合了上一篇的手机号登录接下来的实现功能 https://www.cnblogs.com/xiaoyantongxue/p/15472915.html 登录后进入课程选择页面 1:数据库填入数据 ...

  5. 微信小程序登录流程及解析用户openid session_key,获取用户信息

    为优化用户体验,使用 wx.getUserInfo 接口直接弹出授权框的开发方式将逐步不再支持.从2018年4月30日开始,小程序与小游戏的体验版.开发版调用 wx.getUserInfo 接口,将无 ...

  6. 微信小程序button授权页面,用户拒绝后仍可再次授权

    微信小程序授权页面,进入小程序如果没授权跳转到授权页面,授权后跳转到首页,如果用户点拒绝下次进入小程序还是能跳转到授权页面,授权页面如下 app.js  中的 onLaunch或onShow中加如下代 ...

  7. 微信小程序-登陆、支付、模板消息

    wx.login(OBJECT) 调用接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key).用户数据的加解密通讯需要 ...

  8. 微信小程序发布与支付

    一.小程序的发布流程 小程序协同工作和发布官网链接 1.背景 小程序的平台里,开发者完成开发之后,需要在开发者工具提交小程序的代码包,然后在小程序后台发布小程序. 2.流程 上传代码 代码管理服务器上 ...

  9. 【小程序】微信小程序绑定企业微信后怎样获取到用户信息

    一.获取access_token 1.https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRECT Cor ...

  10. 微信小程序~App.js中获取用户信息

    (1)代码:主要介绍下获取用户信息部分 onLaunch: function () { // 展示本地存储能力 var logs = wx.getStorageSync('logs') || [] l ...

随机推荐

  1. java基础篇 之 位运算符

    按位操作符 ​ 按位操作符用来操作基本数据类型中的单个"比特"(bit),即二进制位.按位操作符会对两个参数中对应的位执行布尔代数运算,并最终生成一个结果. ​ 我们常用的按位操作 ...

  2. 【mybatis】IF判断的坑

    http://cheng-xinwei.iteye.com/blog/2008200 最近在项目使用mybatis中碰到个问题 <if test="type=='y'"> ...

  3. 【x64软路由】OpenWrt(LEDE) 20200329编译 反追踪 抗污染 加速 PSW 无缝集成 UPnP NAS

    固件说明 基于Lede OpenWrt R2020.3.19版本(源码更新截止20200329)Lienol Feed及若干自行维护的软件包 结合家庭x86软路由场景需要定制 按照家庭应用场景对固件及 ...

  4. 从浅入深——理解JSONP的实现原理

    由于浏览器的安全性限制,不允许AJAX访问 协议不同.域名不同.端口号不同的 数据接口,浏览器认为这种访问不安全: 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口 ...

  5. Java面试札记

    Java面试札记  在最深的夜里,即使是你的影子也会离你而去. 背景:愿某人在中秋节之前吃上大厂月饼!!!@CDZ 1.Java的八种基本数据类型? 整型:byte.int.short.long: 浮 ...

  6. 接口测试/soapUI

    忙过了2019年的下半年终于在2020年快上线了,~鞭炮噼啪过~ 项目技术架构:XML请求数据 -> JAVA (转换)-> JOSN请求数据 项目使用工具:soapUI/Jmeter,m ...

  7. Android 开发技术周报 Issue#280

    新闻 6分钟完整视频提前看光谷歌新机Pixel 4a 统一推送官方解读:消灭Android毒瘤.待机续航猛增43% Google Play细化搜索结果:可按评分.编辑推荐.最新上线过滤 教程 了解一下 ...

  8. ketchup 注册中心consul使用

    ketcup  git地址:https://github.com/simple-gr/ketchup consul 安装 1.docker pull consul 2.docker run --nam ...

  9. Kubernetes实战 - 从零开始搭建微服务 1 - 使用kind构建一个单层架构Node/Express网络应用程序

    使用kind构建一个单层架构Node/Express网络应用程序 Kubernetes实战-从零开始搭建微服务 1 前言 准备写一个Kubernetes实战系列教程,毕竟cnblogs作为国内最早的技 ...

  10. python操作MySQL之pymysql模块

    import pymysql#pip install pymysql db=pymysql.connect(','day040') cursor=db.cursor() #创建游标 book_list ...