在網上找到了做微信對接的C#SDK:Senparc.Weixin.MP。這個類庫的封裝,在我看來,還是不錯的,基本囊貨了微信的功能,并且在不斷的完善。類庫是開源的,每個功能都有寫簡單的單元測試,看起來一目了然。主要對接DLL對應的功能如下:
公衆号+微信支付 SDK:Senparc.Weixin.MP.dll
企業号 SDK:Senparc.Weixin.QY.dll
開放平台 SDK:Senparc.Weixin.Open.dll
官方位址:http://weixin.senparc.com/
當然,我們要完成公衆号微信支付功能的開發,需要使用Senparc.Weixin.MP.dll這個DLL,查閱了一下官方提供的DEMO以及教程,并沒有載入微信支付相關的說明,沒辦法,既然拿到源碼了,自己找吧。
打開Senparc.Weixin.MP.sln,根據英文檔案夾名稱的分類,可以初步判斷,關于微信支付,被封裝在TenPayLib檔案夾 中,但是我還發現,裡面存在名稱叫“TenPayLibV3”的檔案夾,那如何選擇呢?網上搜尋了一下,得出這個結論:2014年9月10号之前申請的為 v2版,之後申請的為v3版。我用來測試微信支付的服務号是在16年剛申請,并且通過驗證的,那麼果斷使用V3吧。
打開TenPayLibV3檔案夾:
這裡發現多個類庫,每一個都是做什麼的呢?我們這裡不一一叙述,感興趣的朋友可以下載下傳來看,每一個類的檔案頭都有功能說明與描述,對照微信官方支付說明,我們直接開始做支付。
進入微信公衆号,點選功能菜單中的微信支付:并相應點選 使用教程-公衆号支付
迅速對文檔内容重溫、浏覽,以友善在Senparc.Weixin.MP.dll中查找相應的功能。先配置支付授權目錄,添加支付測試白名單,支付目錄隻支援三個,并且域名必須經過ICP備案。授權目錄的作用是,如果要發起微信支付請求,請求 的連結位址必須在授權目錄下,否則身份無效,支付不能成功。測試白名單中添加的個人微信号,才能完成微信支付測試目錄支付的測試,不在白名單中人員發起支 付申請,支付不能成功。
配置完成後,如何調用呢?我們繼續看官方說明:H5調起支付API
“在微信浏覽器裡面打開H5網頁中執行JS調起支付。接口輸入輸出資料格式為JSON。
注意:WeixinJSBridge内置對象在其他浏覽器中無效。
清單中參數名區分大小,大小寫錯誤簽名驗證會失敗。”
OK,這裡說明了幾個事情,第一必須在微信浏覽器進行;第二,參數區分大小寫;第三,資料格式為JSON。
官方說明,隻要在頁面中調用如下腳本,即可開啟微信支付功能:
1 function onBridgeReady(){
2 WeixinJSBridge.invoke(
3 'getBrandWCPayRequest', {
4 "appId" : "wx2421b1c4370ec43b", //公衆号名稱,由商戶傳入
5 "timeStamp":" 1395712654", //時間戳,自1970年以來的秒數
6 "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随機串
7 "package" : "prepay_id=u802345jgfjsdfgsdg888",
8 "signType" : "MD5", //微信簽名方式:
9 "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名
10 },
11 function(res){
12 if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判斷前端傳回,微信團隊鄭重提示:res.err_msg将在使用者支付成功後傳回 ok,但并不保證它絕對可靠。
13 }
14 );
15 }
16 if (typeof WeixinJSBridge == "undefined"){
17 if( document.addEventListener ){
18 document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
19 }else if (document.attachEvent){
20 document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
21 document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
22 }
23 }else{
24 onBridgeReady();
25 }
1 function onBridgeReady(){
2 WeixinJSBridge.invoke(
3 'getBrandWCPayRequest', {
4 "appId" : "wx2421b1c4370ec43b", //公衆号名稱,由商戶傳入
5 "timeStamp":" 1395712654", //時間戳,自1970年以來的秒數
6 "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随機串
7 "package" : "prepay_id=u802345jgfjsdfgsdg888",
8 "signType" : "MD5", //微信簽名方式:
9 "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名
10 },
11 function(res){
12 if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判斷前端傳回,微信團隊鄭重提示:res.err_msg将在使用者支付成功後傳回 ok,但并不保證它絕對可靠。
13 }
14 );
15 }
16 if (typeof WeixinJSBridge == "undefined"){
17 if( document.addEventListener ){
18 document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
19 }else if (document.attachEvent){
20 document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
21 document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
22 }
23 }else{
24 onBridgeReady();
25 }
我的調用代碼:因為我要在點選按鈕确認支付之後,在調用微信支付進行後續操作,把官方代碼提出到方法中
function onBridgeReady() {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": $('#APPID').val(), //公衆号名稱,由商戶傳入
"timeStamp": $('#Timestamp').val(), //時間戳,自1970年以來的秒數
"nonceStr": $('#Noncestr').val(), //随機串
"package": $('#package').val(),
"signType": "MD5", //微信簽名方式:
"paySign": $('#paySign').val() //微信簽名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
//支付成功,後續自行處理
}
else
{
//支付取消,或者其他錯誤,自行處理
}
}
);
}
好吧,那這堆參數是從哪來的,都是啥玩意兒?我們逐個分析一下:
appId:這個做微信開發都應該知道,公衆号在開發者菜單就能找到
timeStamp:時間戳,官方描述為“自1970年以來的秒數”,不用擔心,肯定能從支付類庫裡找到
nonceStr:官方解釋是随機串“e61463f8efa94090b1f366cccfbbb444”,靠啥玩意兒?詳見随機數生成算法,原來就是一套加密規則和算法,做過URL請求接口的朋友應該知道,有些公司JSON串的簽名方式和這比較類似。
package:預支付ID,調用官方API統一下單接口可以獲得
signType:字元串"MD5"
paySign:官方解釋是微信簽名“70EA570631E4BB79628FBCA90534C63FF7FADD89”,好吧,我忍了,在看下簽名生成算法,看來和随機串一個鳥樣
到這裡,官方的接口說明已經了解的很清楚了,那麼下面就要解決調用微信支付的這幾個參數了,通過Senparc.Weixin.MP.dll應該如何使用呢?既然需要先調用統一下單接口擷取預支付訂單ID,好吧,我們先來研究一下,如何獲得這個ID吧。
官方給出了詳細說明,我們不在贅述,各參數研究按照上述接口的方式自行研究解決,唯一差別在于,調用官方接口需要傳入一個XML,那很好辦,拼接一下就可以了,預支付調用方法如下:
//這裡通過官方的一個實體,使用者自行使用,我這裡是直接讀取的CONFIG檔案
private static Senparc.Weixin.MP.TenPayLibV3.TenPayV3Info tenPayV3Info = new Senparc.Weixin.MP.TenPayLibV3.TenPayV3Info(ConfigurationManager.AppSettings["corpId"], ConfigurationManager.AppSettings["corpSecret"], ConfigurationManager.AppSettings["mch_id"]
, ConfigurationManager.AppSettings["key"], ConfigurationManager.AppSettings["v3url"]);
/// <summary>
/// 微信預支付
/// </summary>
/// <param name="attach"></param>
/// <param name="body"></param>
/// <param name="openid"></param>
/// <param name="price"></param>
/// <param name="orderNum"></param>
/// <returns></returns>
public static string PayInfo(string attach, string body, string openid, string price, string orderNum = "1833431773763549")
{
RequestHandler requestHandler = new RequestHandler(HttpContext.Current);
//微信配置設定的公衆賬号ID(企業号corpid即為此appId)
requestHandler.SetParameter("appid", tenPayV3Info.AppId);
//附加資料,在查詢API和支付通知中原樣傳回,該字段主要用于商戶攜帶訂單的自定義資料
requestHandler.SetParameter("attach", attach);
//商品或支付單簡要描述
requestHandler.SetParameter("body", body);
//微信支付配置設定的商戶号
requestHandler.SetParameter("mch_id", tenPayV3Info.MchId);
//随機字元串,不長于32位。
requestHandler.SetParameter("nonce_str", TenPayUtil.GetNoncestr());
//接收微信支付異步通知回調位址,通知url必須為直接可通路的url,不能攜帶參數。
requestHandler.SetParameter("notify_url", tenPayV3Info.TenPayV3Notify);
//trade_type=JSAPI,此參數必傳,使用者在商戶公衆号appid下的唯一辨別。
requestHandler.SetParameter("openid", openid);
//商戶系統内部的訂單号,32個字元内、可包含字母,自己生成
requestHandler.SetParameter("out_trade_no", orderNum);
//APP和網頁支付送出使用者端ip,Native支付填調用微信支付API的機器IP。
requestHandler.SetParameter("spbill_create_ip", "127.0.0.1");
//訂單總金額,機關為分,做過銀聯支付的朋友應該知道,代表金額為12位,末位分分
requestHandler.SetParameter("total_fee", price);
//取值如下:JSAPI,NATIVE,APP,我們這裡使用JSAPI
requestHandler.SetParameter("trade_type", "JSAPI");
//設定KEY
requestHandler.SetKey(tenPayV3Info.Key);
requestHandler.CreateMd5Sign();
requestHandler.GetRequestURL();
requestHandler.CreateSHA1Sign();
string data = requestHandler.ParseXML();
requestHandler.GetDebugInfo();
//擷取并傳回預支付XML資訊
return TenPayV3.Unifiedorder(data);
}
}
好的,拿到預支付訂單的傳回資料,一切又都好辦了,根據傳回參數的不同,自行解決,我們隻關心調用正确的過程,操作繼續,在傳回的正确XML資料 中,我們擷取到了 <prepay_id><! [CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>(官方示 例),好的,開始在頁面做支付吧!
這裡,我封裝了一個實體,用來傳輸常用的資料,當然,各位也可以參考Senparc.Weixin.MP.dll提供的實體類。
public class ShareInfo
{
string corpId = string.Empty;
public string CorpId
{
get { return corpId; }
set { corpId = value; }
}
string ticket = string.Empty;
public string Ticket
{
get { return ticket; }
set { ticket = value; }
}
string noncestr = string.Empty;
public string Noncestr
{
get { return noncestr; }
set { noncestr = value; }
}
string timestamp = string.Empty;
public string Timestamp
{
get { return timestamp; }
set { timestamp = value; }
}
private string paySign = string.Empty;
public string PaySign
{
get { return paySign; }
set { paySign = value; }
}
private string package = string.Empty;
public string Package
{
get { return package; }
set { package = value; }
}
}
我們繼續,來看一下支付接口需要用到的參數如何擷取:
public static ShareInfo GetPayInfo(string prepayid)
{
shareInfo = new ShareInfo();
//檢查是否已經注冊jssdk
if (!JsApiTicketContainer.CheckRegistered(corpId))
{
JsApiTicketContainer.Register(corpId, corpSecret);
}
JsApiTicketResult jsApiTicket = JsApiTicketContainer.GetTicketResult(corpId);
JSSDKHelper jssdkHelper = new JSSDKHelper();
shareInfo.Ticket = jsApiTicket.ticket;
shareInfo.CorpId = corpId.ToLower();
shareInfo.Noncestr = JSSDKHelper.GetNoncestr().ToLower();
shareInfo.Timestamp = JSSDKHelper.GetTimestamp().ToLower();
shareInfo.Package="prepay_id=" + prepayid.ToLower();
RequestHandler requestHandler = new RequestHandler(HttpContext.Current);
requestHandler.SetParameter("appId", shareInfo.CorpId);
requestHandler.SetParameter("timeStamp", shareInfo.Timestamp);
requestHandler.SetParameter("nonceStr", shareInfo.Noncestr);
requestHandler.SetParameter("package", shareInfo.Package);
requestHandler.SetParameter("signType", "MD5");
requestHandler.SetKey(tenPayV3Info.Key);
requestHandler.CreateMd5Sign();
requestHandler.GetRequestURL();
requestHandler.CreateSHA1Sign();
shareInfo.PaySign = (requestHandler.GetAllParameters()["sign"]).ToString();
return shareInfo;
}
這樣,支付接口需要用到的參數,就都封裝在ShareInfo裡了,好吧,調用之後,我們回到頁面的後置代碼中,或者你采用的ORM對應代碼中去,将參數輸出到頁面
//處理頁面支付調用資訊
ShareInfo shareInfo = TenPayModule.GetPayInfo(prepayid);
System.Web.HttpContext.Current.Response.Write(string.Format("<input type=\"hidden\" id=\"Noncestr\" runat=\"server\" value=\"{0}\" />", shareInfo.Noncestr));
System.Web.HttpContext.Current.Response.Write(string.Format("<input type=\"hidden\" id=\"Timestamp\" runat=\"server\" value=\"{0}\" />", shareInfo.Timestamp));
System.Web.HttpContext.Current.Response.Write(string.Format("<input type=\"hidden\" id=\"APPID\" runat=\"server\" value=\"{0}\" />", shareInfo.CorpId));
System.Web.HttpContext.Current.Response.Write(string.Format("<input type=\"hidden\" id=\"paySign\" runat=\"server\" value=\"{0}\" />", shareInfo.PaySign));
System.Web.HttpContext.Current.Response.Write(string.Format("<input type=\"hidden\" id=\"package\" runat=\"server\" value=\"{0}\" />", shareInfo.Package));
好的,寫到這裡,大家參照上面的JS代碼,就可以完成整個的支付功能了。最後,再附送一個生成商家訂單号的方法,代碼如下: