企業微信的支付自從企業號變化為企業微信後,增加了一些支付介面以及對很多介面進行了調整,企業微信的支付處理也是變化了不少,往往有時候碰到不少坑,一步一個腳印趟過來的;企業微信支付是需要結合微信商戶後臺進行處理,有時候也需要設置好商戶平臺的相關處理,才能進行發送紅包、支付到個人等等支付處理。本篇隨筆介紹... ...
企業微信的支付自從企業號變化為企業微信後,增加了一些支付介面以及對很多介面進行了調整,企業微信的支付處理也是變化了不少,往往有時候碰到不少坑,一步一個腳印趟過來的;企業微信支付是需要結合微信商戶後臺進行處理,有時候也需要設置好商戶平臺的相關處理,才能進行發送紅包、支付到個人等等支付處理。本篇隨筆介紹在企業微信的支付處理中的發送紅包的操作相關內容。
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);
最新的第一條就是直接付款的信息提示。