开始做jsapi支付时看了好多的demo及好多的博客感觉有大坑,果不其然,一点一点从坑中爬出。
- 前期准备,首先微信公众号中的配置,现在微信支付中配置好支付授权目录,先解释一下支付授权目录时做什么的,在微信发起支付时要获取用户的openid,而获取openid之前要获取一个code,而这个code是获取openid的凭证,而在获取code时微信会有一个回调页面而这个页面就是你的授权支付目录,如果这个目录不正确则后续没有办法进行。例如:你要支付的页面为www.weixin.com/wxpay/JsApiPayPage.aspx时,你需要将目录配置为www.weixin.com/wxpay/以免出现问题。
- 其次要配置的就是网页授权获取用户基本信息接口 这个配置的作用使你能访问到支付的接口及回调页面能够返回,其中的域名必须是备案的。
- 正式开始从坑中爬,首先介绍微信调试工具微信web开发工具调微信页面神器,微信网站都可以用这个工具。首先拿到微信支付demo然后各种运行然后不行,首先在default页面中的
<a href="http://paysdk.weixin.qq.com/example/ProductPage.aspx">JSAPI支付</a>
全部都是微信的链接而不是你project的目录,这里在example目录中发现了所有的目录,你可以将default页面的中链接改为exampe目录中的页面,也可以不用改,直接运行example中你想要的接口的页面。下面看到jsapipage.aspx就是jsapi的demo了。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="JsApiPayPage.aspx.cs" Inherits="WxPayAPI.JsApiPayPage" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>微信支付样例-JSAPI支付</title>
</head>
<script type="text/javascript">
//调用微信JS api 支付
function jsApiCall()
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
<%=wxJsApiParam%>,//josn串
function (res)
{
WeixinJSBridge.log(res.err_msg);
alert(res.err_code + res.err_desc + res.err_msg);
}
);
}
function callpay()
{
if (typeof WeixinJSBridge == "undefined")
{
if (document.addEventListener)
{
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
}
else if (document.attachEvent)
{
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}
else
{
jsApiCall();
}
}
</script>
<body>
<form runat="server">
<br/>
<div align="center">
<br/><br/><br/>
<asp:Button ID="submit" runat="server" Text="立即支付" OnClientClick="callpay()" style="width:210px; height:50px; border-radius: 15px;background-color:#00CD00; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" />
</div>
</form>
</body>
</html>
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Net;
using System.IO;
using System.Threading;
using LitJson;
using System.Web.Security;
namespace WxPayAPI
{
public partial class JsApiPayPage : System.Web.UI.Page
{
public static string wxJsApiParam {get;set;} //H5调起JS API参数
protected void Page_Load(object sender, EventArgs e)
{
Log.Info(this.GetType().ToString(), "page load");
if (!IsPostBack)
{
string openid = Request.QueryString["openid"];
string total_fee = Request.QueryString["total_fee"];
//检测是否给当前页面传递了相关参数
if (string.IsNullOrEmpty(openid) || string.IsNullOrEmpty(total_fee))
{
Response.Write("<span style='color:#FF0000;font-size:20px'>" + "页面传参出错,请返回重试" + "</span>");
Log.Error(this.GetType().ToString(), "This page have not get params, cannot be inited, exit...");
submit.Visible = false;
return;
}
//若传递了相关参数,则调统一下单接口,获得后续相关接口的入口参数
JsApiPay jsApiPay = new JsApiPay(this);
jsApiPay.openid = openid;
jsApiPay.total_fee = int.Parse(total_fee);
//JSAPI支付预处理
try
{
WxPayData unifiedOrderResult = jsApiPay.GetUnifiedOrderResult();
wxJsApiParam = jsApiPay.GetJsApiParameters();//获取H5调起JS API参数
Log.Debug(this.GetType().ToString(), "wxJsApiParam : " + wxJsApiParam);
//在页面上显示订单信息
Response.Write("<span style='color:#00CD00;font-size:20px'>订单详情:</span><br/>");
Response.Write("<span style='color:#00CD00;font-size:20px'>" + unifiedOrderResult.ToPrintStr() + "</span>");
}
catch(Exception ex)
{
Response.Write("<span style='color:#FF0000;font-size:20px'>" + "下单失败,请返回重试" + "</span>");
submit.Visible = false;
}
}
}
}
}
在这第一个坑,可以看到首先是获取querystring中的openid和total_fee
这两个参数是一个是关注微信的唯一标识和订单的价格,然而首先在获取openid这就是个坑,要获取openid首先要获取到code,在获取到code之后才能获取到openid和access_token。我的做法是在pageLode中加这个。
try
{
//调用【网页授权获取用户信息】接口获取用户的openid和access_token
jsApiPay.GetOpenidAndAccessToken();
mOpenid = jsApiPay.openid;
mAccess_token = jsApiPay.access_token;
}
catch (Exception ex)
{
Response.Write("<span style='color:#FF0000;font-size:20px'>" + ex.Message + "</span>");
}
其中GetOpenidAndAccessToken方法就有将获取code带码
/**
*
* 网页授权获取用户基本信息的全部过程
* 详情请参看网页授权获取用户基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
* 第一步:利用url跳转获取code
* 第二步:利用code去获取openid和access_token
*
*/
public void GetOpenidAndAccessToken()
{
if (!string.IsNullOrEmpty(page.Request.QueryString["code"]))
{
//获取code码,以获取openid和access_token
string code = page.Request.QueryString["code"];
Log.Debug(this.GetType().ToString(), "Get code : " + code);
GetOpenidAndAccessTokenFromCode(code);
}
else
{
//构造网页授权获取code的URL
string host = page.Request.Url.Host;
string path = page.Request.Path;
string redirect_uri = HttpUtility.UrlEncode("http://www.weixin.com/wxpay.aspx");
WxPayData data = new WxPayData();
data.SetValue("appid", WxPayConfig.APPID);
data.SetValue("redirect_uri", redirect_uri);
data.SetValue("response_type", "code");
data.SetValue("scope", "snsapi_base");
data.SetValue("state", "STATE" + "#wechat_redirect");
string url = "https://open.weixin.qq.com/connect/oauth2/authorize?" + data.ToUrl();
Log.Debug(this.GetType().ToString(), "Will Redirect to URL : " + url);
try
{
//触发微信返回code码
page.Response.Redirect(url);//Redirect函数会抛出ThreadAbortException异常,不用处理这个异常
}
catch (System.Threading.ThreadAbortException ex)
{
}
}
}
其中首先会判断是否获取到了code,如果没有就会执行获取code操作,其中的redirect_uri就是回掉地址,会在这个页面返回code继续执行获取openid和access_token的操作。而在之前pageload中加的那段代码就是在同一个页面两次执行GetOpenidAndAccessToken方法。
4.下面是一个巨坑,当你获取到openid之后还有个非常重要的pay_sign要获取之后还有一个回调地址,这个地址是你在成功支付后微信返回的回调通知。每发起一次支付操作都要获取一次pay_sign操作,而这个参数是发起调用jsapi操作的必须的参数。
function jsApiCall()
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
<%=wxJsApiParam%>,//josn串
function (res)
{
WeixinJSBridge.log(res.err_msg);
alert(res.err_code + res.err_desc + res.err_msg);
}
);
}
就是<%=wxJsApiParam%>,而<%=wxJsApiParam%>是由
名称 | 变量名 | 描述 |
---|---|---|
公众号id | appId | 商户注册具有支付权限的公众号成功后即可获得 |
时间戳 | timeStamp | 当前的时间 |
随机字符串 | nonceStr | 随机字符串,不长于32位。推荐随机数生成算法 |
订单详情扩展字符串 | package | 统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=* |
签名方式 | signType | 签名算法,暂支持MD5 |
签名 | paySign | 签名,详见签名生成算法 |
在demo中lib文件夹中的WxpayApi.cs中的UnifiedOrder方法中获取这些参数,而其中的回调页面的地址是写在配置文件中的 public const string NOTIFY_URL = “http://paysdk.weixin.qq.com/example/ResultNotifyPage.aspx“;而这个配置文件并不是webcofig是在lib中的config.cs中商户号等一系列的配置也在其中。之后就可以发起支付了。
5.最后一坑,支付成功后的返回,在js中只有一个
function (res) {
// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
switch (res.err_msg)
{
case "get_brand_wcpay_request:ok":
alert("支付成功");
break;
case "get_brand_wcpay_request:cancel":
alert("支付取消");
//location.href = "JsApiPayPage.aspx?tepid=1";
break;
default:
alert("支付失败");
//location.href = "JsApiPayPage.aspx?tepid=2";
break;
}
}
真正的支付回调页面在ResultNotifyPage.aspx中,其中有回调支付的判断,更有返回的订单的信息,可以在这个页面对支付成功或失败的订单进行处理。
总结:微信jaspi的坑总算爬出来了,其中发起支付到最后的支付成功总共调用了4次接口。更深表demo很坑。