在我前面的几篇博客,有介绍了微信支付、微信红包、企业付款等各种和支付相关的操作,不过上面都是基于微信普通API的封装,本篇随笔继续微信支付这一主题,继续介绍基于微信网页JSAPI的方式发起的微信支付功能实现,微信的JSAPI相对于普通的API操作,调试没有那么方便,而且有时候有些错误需要反复核实。本篇随笔基于实际的微信网页支付案例,对微信JSAPI的支付实现进行介绍。
1、微信JS-SDK的知识
在我前面《C#开发微信门户及应用(39)--使用微信JSSDK实现签到的功能》介绍的内容里面,有介绍了很多JS-SDK的基础知识,我们基于网页发起的微信支付,我们也是基于JS-SDK的基础上进行发起的,因此需要了解这些JS-SDK的使用步骤。
一般来说,我们网页JSAPI发起的微信支付,需要使用wx.chooseWXPay的操作方法,而这个方法也是需要在完成wx.config初始化的时候,由界面元素进行触发处理的。
例如我们可以这样实现整个微信支付的处理过程:
1)先使用JS对API进行初始化配置
wx.config({
debug: false,
appId: appid, // 必填,公众号的唯一标识
timestamp: timestamp, // 必填,生成签名的时间戳
nonceStr: noncestr, // 必填,生成签名的随机串
signature: signature, // 必填,签名,见附录1
jsApiList: [
'checkJsApi',
'chooseWXPay',
'hideOptionMenu'
]
});
2)使用wx.chooseWXPay发起微信支付
wx.chooseWXPay({
timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: '', // 支付签名随机串,不长于 32 位
package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: '', // 支付签名
success: function (res) {
// 支付成功后的回调函数
}
});
备注:prepay_id 通过微信支付统一下单接口拿到,paySign 采用统一的微信支付 Sign 签名生成方法,注意这里 appId 也要参与签名,appId 与 config 中传入的 appId 一致,即最后参与签名的参数有appId, timeStamp, nonceStr, package, signType。
3)获取用户的openid
在一些JSAPI操作里面,有时候需要传入当前用户的openid,由于这个值,一般是不能直接获得的,但可以通过用户授权代码获取,因此我们可以在菜单中配置好重定向的URL,根据URL获取对应的code,然后解析code为对应的openid即可。
通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。
获取code后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
2、微信支付JSAPI初始化的参数处理
要获取相关的JS-SDK的相关接口参数,我们需要先生成JSAPI-Ticket凭证,生成这个凭证代码接口实现如下所示。一般来说,这个接口的数据需要缓存起来的,具体可以自己实现处理。
///
/// 获取JSAPI_TICKET接口
///
/// 调用接口凭证
///
public string GetJSAPI_Ticket(string accessToken)
{
var url = string.Format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi", accessToken);
var result = JsonHelper.ConvertJson(url);
return result != null ? result.ticket : null;
}
我们要实现JSSDK签名的处理,必须先根据几个变量,构建好URL字符串,具体的处理过程,我们可以把它们逐一放在一个Hashtable里面,如下代码所示。
///
/// 获取JSSDK所需要的参数信息,返回Hashtable结合
///
/// 微信AppID
/// 根据Token获取到的JSSDK ticket
/// 页面URL
///
public static Hashtable GetParameters(string appId, string jsTicket, string url)
{
string timestamp = GetTimeStamp();
string nonceStr = GetNonceStr();
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
string rawstring = "jsapi_ticket=" + jsTicket + "&noncestr=" + nonceStr + "×tamp=" + timestamp + "&url=" + url + "";
string signature = GetSignature(rawstring);
Hashtable signPackage = new Hashtable();
signPackage.Add("appid", appId);
signPackage.Add("noncestr", nonceStr);
signPackage.Add("timestamp", timestamp);
signPackage.Add("url", url);
signPackage.Add("signature", signature);
signPackage.Add("jsapi_ticket", jsTicket);
signPackage.Add("rawstring", rawstring);
return signPackage;
}
这样把它们放在哈希表里面,方便我们提取出来使用。
wx.config({
debug: false,
appId: appid, // 必填,公众号的唯一标识
timestamp: timestamp, // 必填,生成签名的时间戳
nonceStr: noncestr, // 必填,生成签名的随机串
signature: signature, // 必填,签名,见附录1
jsApiList: [
'checkJsApi',
'chooseWXPay',
'hideOptionMenu'
]
});
为了在MVC视图页面里面,设置我们计算出来的值,一般我们需要在后台进行计算好,并把它们放在ViewBag变量中就可以在页面前端使用了,如下所示是MVC视图页面的后台代码。
///
/// 刷新JS-SDK的票据
///
protected virtual void RefreshTicket(AccountInfo accountInfo)
{
Hashtable ht = baseApi.GetJSAPI_Parameters(accountInfo.AppID, accountInfo.AppSecret, Request.Url.AbsoluteUri);
ViewBag.appid = ht["appid"].ToString();
ViewBag.nonceStr = ht["noncestr"].ToString();
ViewBag.timestamp = ht["timestamp"].ToString();
ViewBag.signature = ht["signature"].ToString();
}
这样,在MVC的视图页面里面,我们的代码可以这样实现JSAPI变量的初始化。