C#完成JSAPI支付
一、为什么要写JSAPI微信支付的文章
- 最近有微信H5支付的相关业务,希望下次遇到类似业务时不迷路
- 微信H5支付配置纷繁复杂,对关键点进行记录,加强记忆
二、准备资料
- 微信商户号
登录微信商户,进入账户中心,点击个人信息,查看商户号
- 微信支付API密钥
登录微信商户,进入账户中心,点击API安全,然后在该页的API密钥部分,查看微信支付API密钥
- 配置支付目录
登录微信支付商户平台(pay.weixin.qq.com)-->产品中心-->开发配置。支付授权目录校验规则说明:
- 如果支付授权目录设置为顶级域名(例如:https://www.weixin.com/ ),那么只校验顶级域名,不校验后缀;
- 如果支付授权目录设置为多级目录,就会进行全匹配,例如设置支付授权目录为https://www.weixin.com/abc/123/,则实 际请求页面目录不能为https://www.weixin.com/abc/,也不能为https://www.weixin.com/abc/123/pay/,必须为 https://www.weixin.com/abc/123/;
- 配置微信公众号IP白名单
将我们服务器的IP地址配置到微信公众号IP地址白名单,登录微信公众号,安全中心-->Ip白名单
点击Ip白名单,配置我们的服务器地址。
- 下载JSAPI支付SDK。
下载地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
进入页面之后,选择.NET C#的SDK下载
解压后的目录如下,其实在开发过程中,我们主要使用两部分,分别为Lib跟business文件夹中的对象。故仅需要将这两部分代码Copy至我们项目工程中即可。
三、开发工作
关于微信H5开发,分两部分完成。
- 后端C#代码
- 前端Jquery代码
我认为,我在完成微信H5支付之前,需要先了解微信支付的执行步骤。
- 调用微信统一下单接口
- Html页,前端发起支付
- 支付成功,微信回调我们服务器完成支付。
C#代码
第一步:赋值WxPayConfig对象
微信支付的所有配置信息都在Lib文件中的WxPayConfig对象中,我们在调用统一下单接口前,需将我们实际支付环境中的配置信息更新至WxPayConfig对象。WxPayConfig类的结构如下:
public class WxPayConfig
{
#region 微信支付信息
/// <summary>
/// 微信公众号AppId
/// </summary>
public static string APPID
{
get;set;
}
/// <summary>
/// MCHID:商户号(必须配置)
/// </summary>
public static string MCHID
{
get; set;
}
/// <summary>
/// 商户系统后台机器IP,此参数可手动配置也可在程序中自动获取
/// </summary>
public static string IP
{
get; set;
}
/// <summary>
/// 支付结果通知回调url,用于商户接收支付结果
/// </summary>
public static string NOTIFY_URL
{
get; set;
}
/// <summary>
/// 微信支付统一下单接口地址
/// </summary>
public static string UnifiedorderUrl
{
get; set;
}
#endregion
/// <summary>
/// 微信支付有效时间
/// </summary>
public static string Time_expire
{
get; set;
}
/// <summary>
/// APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置)
/// </summary>
public static string APPSECRET
{
get; set;
}
/// <summary>
/// KEY:商户支付密钥,参考开户邮件设置(必须配置)
/// </summary>
public static string KEY
{
get; set;
}
/// <summary>
/// 【日志级别】,日志等级,0.不输出日志;1.只输出错误信息; 2.输出错误和正常信息; 3.输出错误信息、正常信息和调试信息
/// </summary>
public static int LOG_LEVENL
{
get; set;
}
//=======【证书路径设置】=====================================
/* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要)
*/
public static string SSLCERT_PATH
{
get; set;
}
public static string SSLCERT_PASSWORD
{
get; set;
}
//=======【代理服务器设置】===================================
/* 默认IP和端口号分别为0.0.0.0和0,此时不开启代理(如有需要才设置)
*/
public const string PROXY_URL = "http://10.152.18.220:8080";
//=======【上报信息配置】===================================
/* 测速上报等级,0.关闭上报; 1.仅错误时上报; 2.全量上报
*/
public const int REPORT_LEVENL = 1;
/// <summary>
/// 证书路径
/// </summary>
public static string WxSSLPath { get; set; }
/// <summary>
/// 证书秘钥
/// </summary>
public static string WxSSLPwd { get; set; }
}
我们需要更新的配置信息如下:
//统一下单请求地址
WxPayConfig.UnifiedorderUrl = https://api.mch.weixin.qq.com/pay/unifiedorder;
//微信支付成功后的回调地址
WxPayConfig.NOTIFY_URL = "回调地址";
//微信公众号的AppId
WxPayConfig.APPID = "公众号AppId";
//微信商户的商户号
WxPayConfig.MCHID = "微信商户号";
//服务器的Ip地址
WxPayConfig.IP = "Ip地址";
//微信商户的支付密钥
WxPayConfig.KEY = "支付密钥";
第二步:创建JsApiPay对象,并赋值
JsApiPay对象中有四个属性,三个方法,一个空构造函数
三个属性分别为:
/// <summary>
/// openid用于调用统一下单接口【支付者的OpenId】
/// </summary>
public string openid { get; set; }
/// <summary>
/// access_token用于获取收货地址js函数入口参数【该参数我们不会用到】
/// </summary>
public string access_token { get; set; }
/// <summary>
/// 商品金额,用于统一下单【支付金额】
/// </summary>
public int total_fee { get; set; }
/// <summary>
/// 统一下单接口返回结果【统一下单返回结果】
/// </summary>
public WxPayData unifiedOrderResult { get; set; }
声明JsApiPay对象, 并依次为我们需要赋值的参数赋值。
//声明JsApiPay对象
JsApiPay jsApiPay = new JsApiPay();
//微信支付金额是以分为单位,所以我们需对钱数进行转换,*100为最终支付金额
string strFee = (data.pay_price * 100).ToString("0");
//支付人的OpenId
jsApiPay.openid = data.openid;
//为支付金额赋值
jsApiPay.total_fee = Convert.ToInt32(strFee);
第三步:调用JsApiPay对象中的统一下单方法
调用jsApiPay中的方法GetUnifiedOrderResult()获取统一下单结果。
WxPayData unifiedOrderResult = jsApiPay.GetUnifiedOrderResult();
微信提供的示例中,GetUnifiedOrderResult的方法体如下:
/**
* 调用统一下单,获得下单结果
* @return 统一下单结果
* @失败时抛异常WxPayException
*/
public WxPayData GetUnifiedOrderResult()
{
//统一下单
WxPayData data = new WxPayData();
data.SetValue("body", "test");
data.SetValue("attach", "test");
data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());
data.SetValue("total_fee", total_fee);
data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));
data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));
data.SetValue("goods_tag", "test");
data.SetValue("trade_type", "JSAPI");
data.SetValue("openid", openid);
WxPayData result = WxPayApi.UnifiedOrder(data);
if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
{
throw new WxPayException("UnifiedOrder response error!");
}
unifiedOrderResult = result;
return result;
}
我们需要做的就是替换方法体中的body,attach,goods_tag参数。
请注意:如果我们没有任何优惠券信息,goods_tag参数可为空,我们可以不赋值或者无需拼接该参数。
第四步:调用JsApiPay中的方法GetJsApiParameters().获取Js支付参数完成支付。
如果统一下单方法调用成功,我们还需调用JsApiPay中的方法GetJsApiParameters()。完成最终支付。微信提供的GetJsApiParameters方法体示例如下,我们无需修改。
/**
*
* 从统一下单成功返回的数据中获取微信浏览器调起jsapi支付所需的参数,
* 微信浏览器调起JSAPI时的输入参数格式如下:
* {
* "appId" : "wx2421b1c4370ec43b", //公众号名称,由商户传入
* "timeStamp":" 1395712654", //时间戳,自1970年以来的秒数
* "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串
* "package" : "prepay_id=u802345jgfjsdfgsdg888",
* "signType" : "MD5", //微信签名方式:
* "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
* }
* @return string 微信浏览器调起JSAPI时的输入参数,json格式可以直接做参数用
* 更详细的说明请参考网页端调起支付API:http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7
*
*/
public string GetJsApiParameters()
{
WxPayData jsApiParam = new WxPayData();
jsApiParam.SetValue("appId", unifiedOrderResult.GetValue("appid"));
jsApiParam.SetValue("timeStamp", WxPayApi.GenerateTimeStamp());
jsApiParam.SetValue("nonceStr", WxPayApi.GenerateNonceStr());
jsApiParam.SetValue("package", "prepay_id=" + unifiedOrderResult.GetValue("prepay_id"));
jsApiParam.SetValue("signType", "MD5");
jsApiParam.SetValue("paySign", jsApiParam.MakeSign());
string parameters = jsApiParam.ToJson();
return parameters;
}
将GetJsApiParameters方法的结果返回至前端Html代码即可。就这样我们的C#部分代码就完成了。
Html代码
第一步 判断是否为微信浏览器内完成支付
根据微信要求,微信支付必须在微信浏览器内完成,我们如何判断当前客户打开该页面时是否启用了微信浏览器,微信提供了对象以及方法
if (typeof WeixinJSBridge == "undefined") {
// 提交支付信息
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
// 提交支付信息
onBridgeReady();
} else {
// 提交支付信息
onBridgeReady();
}
在onBridgeReady方法内,我们调用统一下单的ajax请求,并使用返回的jsApiParam对象完成支付。
function onBridgeReady() {
$.ajax({
url: "统一下单请求Url",
data: {"请求的业务数据"},
type: "post",
dataType: "json",
success: function (data) {
var resultData = "返回的jsApiParam的Json数据结果";
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
JSON.parse(resultData),
function (res) {
switch (res.err_msg) {
case "get_brand_wcpay_request:ok":
//支付成功
break;
case "get_brand_wcpay_request:cancel":
//支付取消
break;
default:
//支付失败
break;
}
}
);
},
error: function () {
}
});
}
至此我们JSAPI完成H5支付的全部过程就已经完成。下一篇,我会着重写一下我们应该如何处理微信的支付回调请求。