公司最近在做微信小程序,被分配到做支付這一塊,現(xiàn)在對(duì)這一塊做一個(gè)簡(jiǎn)單的總結(jié)和梳理。
支付,對(duì)于購(gòu)物來(lái)說(shuō),可以說(shuō)是占據(jù)了十分重要的一塊,畢竟能收到錢(qián)才是重點(diǎn)。
當(dāng)然在開(kāi)發(fā)之前,我們需要有下面這些東西:
當(dāng)然這些是不用我們自己申請(qǐng)的,公司會(huì)有人申請(qǐng)好,然后要什么跟這個(gè)人說(shuō),讓他提供就可以了。
首先來(lái)看一下官方給出的業(yè)務(wù)流程時(shí)序圖
這個(gè)圖很清晰的表達(dá)了在小程序支付中的整個(gè)流程,每一步要做些什么。
一個(gè)完整的支付,一般情況下都是包含了下面三個(gè)主要的點(diǎn);
下面就重點(diǎn)來(lái)簡(jiǎn)單實(shí)現(xiàn)一下上面說(shuō)的第一點(diǎn),支付,也是可以進(jìn)行下面兩步的在大前提。
簡(jiǎn)單起見(jiàn),在index.wxml中添加一個(gè)輸入框和一個(gè)button,綁定一下相應(yīng)的事件,輸入框主要是用于輸入訂單號(hào),按鈕用于模擬提交一個(gè)訂單并發(fā)起支付。
<!--index.wxml-->
<view class="container">
<input type="text" bindinput="getOrderCode" style="border:1px solid #ccc;" />
<button bindtap="pay">立即支付</button>
</view>
然后在index.js中寫(xiě)上一小段代碼,主要是處理上面按鈕的點(diǎn)擊事件。
Page({
data: {
txtOrderCode: ''
},
pay: function () {
var ordercode = this.data.txtOrderCode;
wx.login({
success: function (res) {
if (res.code) {
wx.request({
url: 'https://www.yourdomain.com/pay',
data: {
code: res.code,//要去換取openid的登錄憑證
ordercode: ordercode
},
method: 'GET',
success: function (res) {
console.log(res.data)
wx.requestPayment({
timeStamp: res.data.timeStamp,
nonceStr: res.data.nonceStr,
package: res.data.package,
signType: 'MD5',
paySign: res.data.paySign,
success: function (res) {
// success
console.log(res);
},
fail: function (res) {
// fail
console.log(res);
},
complete: function (res) {
// complete
console.log(res);
}
})
}
})
} else {
console.log('獲取用戶(hù)登錄態(tài)失敗!' + res.errMsg)
}
}
});
},
getOrderCode: function (event) {
this.setData({
txtOrderCode: event.detail.value
});
}
})
可以看到,在這里Catcher先通過(guò)wx.login這個(gè)API先取到了登錄的憑證code,并把這個(gè)憑證code做為請(qǐng)求參數(shù)用wx.request這個(gè)API發(fā)起一個(gè)網(wǎng)絡(luò)請(qǐng)求。
在這個(gè)網(wǎng)絡(luò)請(qǐng)求處理后會(huì)返回小程序支付所需要的相關(guān)參數(shù)。拿到這些參數(shù)后,再調(diào)用wx.requestPayment這個(gè)支付API,此時(shí)才算是真正的發(fā)起支付。
至此,小程序這邊的事已經(jīng)做完了,接下來(lái)就是要去處理接口那邊的事了,其實(shí)接口要做的就是返回小程序需要的幾個(gè)參數(shù)。但是要拿到這幾個(gè)參數(shù)還是需要做不少事情的。
據(jù)悉最新版的Senparc.Weixin.MP已經(jīng)支付了小程序相關(guān)的內(nèi)容,但是公司用的版本還是比較低
并且近期也沒(méi)有打算對(duì)這個(gè)組件進(jìn)行升級(jí)。所以就從白紙一張開(kāi)始了。
用的是mvc,所以這個(gè)小程序發(fā)起的網(wǎng)絡(luò)請(qǐng)求會(huì)由下面的action的執(zhí)行,里面的實(shí)現(xiàn),每一步做了什么應(yīng)該也已經(jīng)很清晰了。
public ActionResult Pay(string code, string ordercode)
{
var paramter = new Parameters();
paramter.out_trade_no = ordercode;
//使用登錄憑證 code 獲取 session_key 和 openid
var unifiedorderRes = GetOpenIdAndSessionKey(paramter.appid, paramter.secret, code);
//反序列化session_key 和 openid成ChangeResponseEntity實(shí)體
var tmp = JsonConvert.DeserializeObject<ChangeResponseEntity>(unifiedorderRes);
//統(tǒng)一下單的url和參數(shù)
var payUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
var param = GetUnifiedOrderParam(tmp.openid, paramter);
//統(tǒng)一下單后拿到的xml結(jié)果
var payResXML = Helper.DoPost(param, payUrl);
var payRes = XDocument.Parse(payResXML);
var root = payRes.Element("xml");
//序列化相應(yīng)參數(shù)返回給小程序
var res = GetPayRequestParam(root, paramter.appid, paramter.key);
return Json(res, JsonRequestBehavior.AllowGet);
}
由于只是一個(gè)演示的過(guò)程,不想這些數(shù)據(jù)經(jīng)常以字符串的形式頻繁出現(xiàn)在代碼中,所以把相關(guān)的參數(shù)全部都放到了一個(gè)名為Parameters的類(lèi)中(放到配置文件中也是可以的),除了訂單號(hào)是從小程序傳過(guò)來(lái)的,當(dāng)然在實(shí)際中這是不合理的,畢竟像金額這些東西,不可能每次都是同一個(gè)!這點(diǎn)是要注意的。
下面先來(lái)看看這個(gè)Parameters類(lèi)的定義:
public class Parameters
{
public string appid { get { return "申請(qǐng)的appid"; } }
public string mchid { get { return "申請(qǐng)的商戶(hù)號(hào)"; } }
public string nonce { get { return Helper.GetNoncestr(); } }
public string notify_url { get { return "http://yourdomain.com/notifyurl"; } }
public string body { get { return "testpay"; } }
public string out_trade_no { get; set; }
public string spbill_create_ip { get { return "IP地址"; } }
public string total_fee { get { return "1"; } }
public string trade_type { get { return "JSAPI"; } }
public string key { get { return "在商家后臺(tái)設(shè)置的密鑰"; } }
public string secret { get { return "在配置小程序時(shí)的密鑰"; } }
}
首先是獲取到登錄憑證后發(fā)起的這個(gè)網(wǎng)絡(luò)請(qǐng)求。這個(gè)網(wǎng)絡(luò)請(qǐng)求是決定了這次支付能否成功的第一步!
下面要做的是用登錄憑證去換我們要的openid。
/// <summary>
/// 取openid和session_key
/// </summary>
/// <param name="appid"></param>
/// <param name="secret"></param>
/// <param name="js_code"></param>
/// <returns></returns>
private string GetOpenIdAndSessionKey(string appid, string secret, string js_code)
{
var url = string.Format("https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code"
, appid,secret,js_code);
var request = WebRequest.Create(url) as HttpWebRequest;
var response = request.GetResponse();
var respStream = response.GetResponseStream();
var res = string.Empty;
using (var reader = new StreamReader(respStream, Encoding.UTF8))
{
res = reader.ReadToEnd();
}
return res;
}
要換取openid,就要向微信提供的地址發(fā)起一個(gè)網(wǎng)絡(luò)請(qǐng)求,并在URL帶上appid,secret和憑證code這三個(gè)參數(shù)。
然后就可以拿到一個(gè)下面形式的json字符串
{
"openid": "OPENID",
"session_key": "SESSIONKEY"
}
拿到之后自然就是要對(duì)這個(gè)字符串進(jìn)行json的反序列化,這里用到了json.net這個(gè)包。
根據(jù)時(shí)序圖,下面要調(diào)用統(tǒng)一下單這個(gè)接口了。
上面的代碼,在統(tǒng)一下單這一塊,又分為下面幾個(gè)步驟
參數(shù)的處理:
具體規(guī)則參見(jiàn):https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
/// <summary>
/// 取統(tǒng)一下單的請(qǐng)求參數(shù)
/// </summary>
/// <param name="openid"></param>
/// <param name="param"></param>
/// <returns></returns>
private string GetUnifiedOrderParam(string openid, Parameters param)
{
//參與統(tǒng)一下單簽名的參數(shù),除最后的key外,已經(jīng)按參數(shù)名ASCII碼從小到大排序
var unifiedorderSignParam = string.Format("appid={0}&body={1}&mch_id={2}&nonce_str={3}¬ify_url={4}&openid={5}&out_trade_no={6}&spbill_create_ip={7}&total_fee={8}&trade_type={9}&key={10}"
, param.appid, param.body, param.mchid, param.nonce, param.notify_url
, openid, param.out_trade_no, param.spbill_create_ip, param.total_fee, param.trade_type, param.key);
//MD5
var unifiedorderSign = Helper.GetMD5(unifiedorderSignParam).ToUpper();
//構(gòu)造統(tǒng)一下單的請(qǐng)求參數(shù)
return string.Format(@"<xml>
<appid>{0}</appid>
<body>{1}</body>
<mch_id>{2}</mch_id>
<nonce_str>{3}</nonce_str>
<notify_url>{4}</notify_url>
<openid>{5}</openid>
<out_trade_no>{6}</out_trade_no>
<spbill_create_ip>{7}</spbill_create_ip>
<total_fee>{8}</total_fee>
<trade_type>{9}</trade_type>
<sign>{10}</sign>
</xml>
", param.appid, param.body, param.mchid, param.nonce, param.notify_url, openid
, param.out_trade_no, param.spbill_create_ip, param.total_fee, param.trade_type, unifiedorderSign);
}
這里要注意一點(diǎn),由于我們的傳的trade_type是JSAPI,所以這里必須是要加上openid進(jìn)行處理的。
然后就是解析統(tǒng)一下單返回的XML了,說(shuō)是解析,其實(shí)也就是要拿到我們需要的數(shù)據(jù)罷了。這里最后會(huì)得到一個(gè)小程序支付API需要的參數(shù)實(shí)體。
/// <sum
上一篇:APP測(cè)試流程
下一篇:微信小程序音樂(lè)播放器