微信小程序支付简单小结与梳理
2019-12-10 14:29:34
公司最近在做微信小程序,被分配到做支付这一块,现在对这一块做一个简单的总结和梳理。
支付,对于购物来说,可以说是占据了十分重要的一块,毕竟能收到钱才是重点。
当然在开发之前,我们需要有下面这些东西:
当然这些是不用我们自己申请的,公司会有人申请好,然后要什么跟这个人说,让他提供就可以了。
首先来看一下官方给出的业务流程时序图
这个图很清晰的表达了在小程序支付中的整个流程,每一步要做些什么。
一个完整的支付,一般情况下都是包含了下面三个主要的点;
下面就重点来简单实现一下上面说的第一点,支付,也是可以进行下面两步的在大前提。
简单起见,在index.wxml中添加一个输入框和一个button,绑定一下相应的事件,输入框主要是用于输入订单号,按钮用于模拟提交一个订单并发起支付。
<!--index.wxml-->
<view class="container">
<input type="text" bindinput="getOrderCode" style="border:1px solid #ccc;" />
<button bindtap="pay">立即支付</button>
</view>
然后在index.js中写上一小段代码,主要是处理上面按钮的点击事件。
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('获取用户登录态失败!' + res.errMsg)
}
}
});
},
getOrderCode: function (event) {
this.setData({
txtOrderCode: event.detail.value
});
}
})
可以看到,在这里Catcher先通过wx.login这个API先取到了登录的凭证code,并把这个凭证code做为请求参数用wx.request这个API发起一个网络请求。
在这个网络请求处理后会返回小程序支付所需要的相关参数。拿到这些参数后,再调用wx.requestPayment这个支付API,此时才算是真正的发起支付。
至此,小程序这边的事已经做完了,接下来就是要去处理接口那边的事了,其实接口要做的就是返回小程序需要的几个参数。但是要拿到这几个参数还是需要做不少事情的。
据悉最新版的Senparc.Weixin.MP已经支付了小程序相关的内容,但是公司用的版本还是比较低
并且近期也没有打算对这个组件进行升级。所以就从白纸一张开始了。
用的是mvc,所以这个小程序发起的网络请求会由下面的action的执行,里面的实现,每一步做了什么应该也已经很清晰了。
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实体
var tmp = JsonConvert.DeserializeObject<ChangeResponseEntity>(unifiedorderRes);
//统一下单的url和参数
var payUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
var param = GetUnifiedOrderParam(tmp.openid, paramter);
//统一下单后拿到的xml结果
var payResXML = Helper.DoPost(param, payUrl);
var payRes = XDocument.Parse(payResXML);
var root = payRes.Element("xml");
//序列化相应参数返回给小程序
var res = GetPayRequestParam(root, paramter.appid, paramter.key);
return Json(res, JsonRequestBehavior.AllowGet);
}
由于只是一个演示的过程,不想这些数据经常以字符串的形式频繁出现在代码中,所以把相关的参数全部都放到了一个名为Parameters的类中(放到配置文件中也是可以的),除了订单号是从小程序传过来的,当然在实际中这是不合理的,毕竟像金额这些东西,不可能每次都是同一个!这点是要注意的。
下面先来看看这个Parameters类的定义:
public class Parameters
{
public string appid { get { return "申请的appid"; } }
public string mchid { get { return "申请的商户号"; } }
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 "在商家后台设置的密钥"; } }
public string secret { get { return "在配置小程序时的密钥"; } }
}
首先是获取到登录凭证后发起的这个网络请求。这个网络请求是决定了这次支付能否成功的第一步!
下面要做的是用登录凭证去换我们要的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,就要向微信提供的地址发起一个网络请求,并在URL带上appid,secret和凭证code这三个参数。
然后就可以拿到一个下面形式的json字符串
{
"openid": "OPENID",
"session_key": "SESSIONKEY"
}
拿到之后自然就是要对这个字符串进行json的反序列化,这里用到了json.net这个包。
根据时序图,下面要调用统一下单这个接口了。
上面的代码,在统一下单这一块,又分为下面几个步骤
参数的处理:
具体规则参见:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
/// <summary>
/// 取统一下单的请求参数
/// </summary>
/// <param name="openid"></param>
/// <param name="param"></param>
/// <returns></returns>
private string GetUnifiedOrderParam(string openid, Parameters param)
{
//参与统一下单签名的参数,除最后的key外,已经按参数名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();
//构造统一下单的请求参数
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);
}
这里要注意一点,由于我们的传的trade_type是JSAPI,所以这里必须是要加上openid进行处理的。
然后就是解析统一下单返回的XML了,说是解析,其实也就是要拿到我们需要的数据罢了。这里最后会得到一个小程序支付API需要的参数实体。
/// <sum
扫描二维码分享到微信