企业微信支付的发送红包及相关接口使用
企业微信的支付自从企业号变化为企业微信后,增加了一些支付接口以及对很多接口进行了调整,企业微信的支付处理也是变化了不少,往往有时候碰到不少坑,一步一个脚印趟过来的;企业微信支付是需要结合微信商户后台进行处理,有时候也需要设置好商户平台的相关处理,才能进行发送红包、支付到个人等等支付处理。本篇随笔介绍在企业微信的支付处理中的发送红包的操作相关内容。
1、企业微信的支付接口
我们查看企业微信api的目录,可以看到企业微信支付的相关介绍,如下所示。
1)常见错误处理
企业微信支付,经常见到的错误信息,就是签名错误这个操作,这个很多人出招,解决方法各种各样,其实很多可能是不符合的,这样排查问题起来就很吃力。
这里需要遵循官方的解析进行排查,特别对参数的顺序和数量进行核对,注意不要增加多一个参数,否则都容易出现签名错误。
我就是在官方需要参数都有了,打印输出的格式也没问题,就是不小心多了一个参数(还是升级前有的一个),导致错误很难排查,弄得很头大。
一般来说发送企业红包,很容易发生签名错误的情况,请检查以下内容
1、企业微信的corpid/corpsecret
2、企业微信的支付secret和商户的api支付秘钥
3、参数不能多也不能少(重要),如很多时候由于版本原因这里不小心多了一个total_num导致签名错误
4、商户平台的证书和密码是否正确
另外,除了这些问题外,重要的问题就是签名的处理了,微信支付除了有一个常规的签名sign参数外,还增加了一个workwx_sign的参数,两者的规则是不同的。
workwx_sign参数在前,使用系统给出的计算方式计算后,然后在计算sign参数,sign参数的计算是包含本身之外的所有参数进行计算,包括了workwx_sign参数。
2)签名参数处理
对于企业微信的签名workwx_sign参数,不要将参数全部参与计算签名,否则会返回微信签名错误!
发红包api固定如下几个字段参与签名:
act_name
mch_billno
mch_id
nonce_str
re_openid
total_amount
wxappid
付款api固定如下几个字段参与签名:
amount
appid
desc
mch_id
nonce_str
openid
partner_trade_no
ww_msg_type
计算企业微信签名的字符串最后拼的secret是企业微信管理端支付应用页面的secret,如下图所示。
示例:请求内容:
act_name xxx
mch_billno 11111234567890
mch_id 10000098
nonce_str qfkegfig76df9912fewmkp
re_openid oxtwiugait6gtksqrlau2m0yl16e
total_amount 100
wxappid wx123456789
第一步: 对参数按照key=value的格式,并按照参数名ascii字典序排序如下
stringa=”act_name=xxx&mch_billno=11111234567890&mch_id=10000098&nonce_str=qfkegfig76df9912fewmkp&re_openid=oxtwiugait6gtksqrlau2m0yl16e&total_amount=100&wxappid=wx123456789
第二步:拼接企业微信支付应用secret(参见企业微信管理端支付应用页面):
stringsigntemp=”stringa&secret=192006250b4c09247ec02edce69f6a2d”
sign=md5(stringsigntemp).touppercase()
2、企业微信发送红包
测试企业微信发送红包和直接支付的接口,响应效果如下所示
在企业微信中,常用到的企业微信的userid,不过发送红包则需要把userid转换为微信的openid进行使用,转换函数根据userid 换取用户的openid 如下。
一般封装一个函数来使用即可。
private string getopenid(string userid) { //根据userid 换取用户的openid icorpbasicapi basicapi = new corpbasicapi(); return basicapi.converttoopenid(this.token, userid); }
发送企业红包调用如下代码所示
//构建发送红包的参数信息 sendredpackjson packjson = new sendredpackjson() { act_name = "恭喜发财", client_ip = networkutil.getipaddress(), remark = "企业红包", wishing = "企业红包", total_amount = 100, total_num = 1, re_openid = openid //发送给用户的openid }; //调用发送企业红包接口发送 var result = hbapi.sendworkredpack(packjson);
函数sendworkredpack的实现内容如下所示。
/// <summary> /// 发放企业红包。需要商户证书 /// </summary> /// <param name="json"></param> /// <returns></returns> public sendredpackresult sendworkredpack(sendredpackjson json) { checkaccount();//检查accountinfo的对象属性值 //加入常规的参数 wxpaydata data = new wxpaydata(); data.setvalue("nonce_str", data.generatenoncestr());//随机字符串 //商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 //接口根据商户订单号支持重入,如出现超时可再调用。 data.setvalue("mch_billno", data.generateouttradeno(accountinfo.mchid)); data.setvalue("mch_id", accountinfo.mchid);//商户号 data.setvalue("wxappid", accountinfo.appid);//公众账号appid data.setvalue("sender_name", accountinfo.name);//红包发送者名称 //发送者头像,此id为微信默认的头像(如果想自定义头像,请参见第三部分) data.setvalue("sender_header_media_id", "1g6nrlmr5ec3mmb_-zk1dddzmd0p7cnliyu9v5w7o8k0"); //以企业应用的名义发红包,企业应用id,整型, //可在企业微信管理端应用的设置页面查看。与sender_name互斥,二者只能填一个。 //data.setvalue("agentid", "3010046");// 企业应用id //发放红包使用场景,红包金额大于200时必传 if (!string.isnullorempty(json.scene_id)) { data.setvalue("scene_id", json.scene_id); } data.setvalue("re_openid", json.re_openid);//接受红包的用户.用户在wxappid下的openid。 data.setvalue("total_amount", json.total_amount);//金额 data.setvalue("wishing", json.wishing);//红包祝福语 data.setvalue("act_name", json.act_name);//项目名称 data.setvalue("remark", json.remark);//备注 data.setvalue("workwx_sign", data.makeworkwxsign(accountinfo.corppaysecret));//企业微信签名 data.setvalue("sign", data.makesign(accountinfo.payapikey)); //发送企业红包,很容易发生签名错误的情况,请检查以下内容 //1、企业微信的corpid/corpsecret //2、企业微信的支付secret和商户的api支付秘钥 //3、参数不能多也不能少(重要),很多时候由于版本原因这里不小心多了一个total_num导致签名错误 //4、商户平台的证书和密码是否正确 var url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendworkwxredpack"; return helper.getpayresultwithcert<sendredpackresult>(data, url, accountinfo.certpath, accountinfo.certpassword); }
其实以上很多参数大家应该都很了解,相对于来说makeworkwxsign 和 makesign 就是这里的关键处理,而前者正是很多人没有处理好的问题所在。
下面把相关函数贴出来,方便对照了解下吧,其实下面这些函数是放在wxpaydata类里面,统一管理处理对应的签名的。
/// <summary> /// 拼接用来签名的几个参数,发送红包和付款的签名字段不同 /// </summary> /// <param name="isredpack">是否为发送红包操作,或者是付款操作,两者需要签名的字段不同</param> /// <returns></returns> private string toworkwxurl(bool isredpack) { list<string> paramredpack = new list<string>() { "act_name", "mch_billno", "mch_id", "nonce_str", "re_openid", "total_amount", "wxappid" }; list<string> parampay = new list<string>() { "amount", "appid", "desc", "mch_id", "nonce_str", "openid", "partner_trade_no", "ww_msg_type" }; string buff = ""; foreach (keyvaluepair<string, object> pair in values) { if (pair.value != null && pair.value.tostring() != "") { if (isredpack) { //发送红包的签名字段 if (paramredpack.contains(pair.key)) { buff += pair.key + "=" + pair.value + "&"; } } else { //付款的签名字段 if (parampay.contains(pair.key)) { buff += pair.key + "=" + pair.value + "&"; } } } } buff = buff.trim('&'); return buff; } /// <summary> /// 生成企业微信签名 /// </summary> /// <param name="corppaysecret">企业支付的secret</param> /// <param name="isredpack">是否为发送红包操作,或者是付款操作,两者需要签名的字段不同</param> /// <returns></returns> public string makeworkwxsign(string corppaysecret, bool isredpack = true) { //转url格式 string str = toworkwxurl(isredpack); //在string后加入secret str += "&secret=" + corppaysecret; //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(); }
/// <summary> /// 生成签名,详见签名生成算法 /// </summary> /// <returns>签名, sign字段不参加签名</returns> public string makesign(string payapikey) { //转url格式 string str = tourl(); //在string后加入api key str += "&key=" + payapikey; //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")); } //所有字符转为大写 var result = sb.tostring().toupper(); return result; }
3、企业微信直接支付的接口
对照这些官方资料,我们可以编写对应的接口api来处理。
/// <summary> /// 企业付款(请求需要双向证书) /// 企业付款业务是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款, /// 针对部分有开发能力的商户,提供通过api完成企业付款的功能。 比如目前的保险行业向客户退保、给付、理赔。 /// 企业付款将使用商户的可用余额,需确保可用余额充足。查看可用余额、充值、提现请登录商户平台“资金管理”进行操作。https://pay.weixin.qq.com/ /// 注意:与商户微信支付收款资金并非同一账户,需要单独充值。 /// </summary> /// <param name="json">企业支付数据</param> /// <returns></returns> public corppayresult corppay(corppayjson json) { checkaccount();//检查accountinfo的对象属性值 wxpaydata data = new wxpaydata(); data.setvalue("mch_appid", accountinfo.appid);//公众账号appid, 注意是mch_appid,而非wxappid data.setvalue("mchid", accountinfo.mchid);//商户号, 注意是mchid而非mch_id data.setvalue("nonce_str", data.generatenoncestr());//随机字符串 data.setvalue("spbill_create_ip", networkutil.getipaddress());//终端ip data.setvalue("partner_trade_no", data.generateouttradeno(accountinfo.mchid));//随机字符串 data.setvalue("device_info", json.device_info);//终端ip data.setvalue("openid", json.openid); data.setvalue("check_name", json.check_name); data.setvalue("re_user_name", json.re_user_name); data.setvalue("amount", json.amount); data.setvalue("desc", json.desc); data.setvalue("sign", data.makesign(accountinfo.payapikey));//最后生成签名 var url = string.format("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"); return helper.getpayresultwithcert<corppayresult>(data, url, accountinfo.certpath, accountinfo.certpassword); }
其中里面的很多参数的处理是和前面支付差不多的,因此不再赘述
调用的处理代码如下所示
//构建处理信息 corppayjson json = new corppayjson() { amount = 100, check_name = paycheckname.force_check.tostring(), desc = "测试退款", openid = openid, device_info = "", re_user_name = "伍华聪", spbill_create_ip = networkutil.getipaddress() }; //直接付款到员工账号 var result = api.corppay(json);
最新的第一条就是直接付款的信息提示。
上一篇: 第一眼看完之后不吃了