调用微信支付,咱们首先须要获取到微信用户的基本资料,在微信开发——网页H5受权,获取微信用户资料一文中介绍到了如何获取微信用户信息,获取到后,咱们保存在webstorage
中,其余的页面也都能使用了,本文记录一下微信H5支付的配置以及须要注意的问题。php
在公众号里面接入支付申请html
注意:公众号须要关联商户号,不然提示 appid和mch_id不匹配 前端
商户秘钥须要动态获取,商户支付密钥本身进行配置32位字母web
进入pay.weixin.qq.com配置微信支付的相关开发项ajax
支付目录的配置,通常到控制器层就行了好比:http://www.testxxx.com/ticketSys/
数据库
注意:若是使用阿里云ECS,调用微信【统一下单】支付接口可能存在超时问题,是DNS解析的问题,将服务器的DNS配置为腾讯公共DNS:119.29.29.29 json
阿里云dns: 首选:10.202.72.116 备选:10.202.72.118后端
调用微信统一下单接口,须要appId,商户号,商户支付秘钥,可是咱们是在html前台中进行调用,这些秘钥信息是不能暴露在客户端的,须要服务端去调用,前端传递公开信息(金额,备注)就行api
支付,前端请求代码,获取微信客户端支付须要的参数(都是服务端生成好的参数返回给前端JsPayApiParams):浏览器
$.ajax({
type: "post",
data: {
totalfee: vChargeVal, // 用户支付金额
openId: app.wxUser.openId // 当前用户的openId,必须
},
url: "/Home/GetJsPayApi",
dataType: "json",
success: function (data) {
if (data.isSuccess) {
// 获取到后端返回的微信jsApi支付参数,后面代码有详解,调用微信客户端支付
onBridgeReady(data.data);
}
else {
alert("调用微信支付模块失败,可能网络服务器缘由,请您重试:" + data.msg);
}
},
error: function () {
alert("调用微信支付模块失败,可能网络服务器缘由,请您重试");
}
})
//调用微信支付模块 WeixinJSBridge 这个是在微信客户端浏览器才会有的对象
function onBridgeReady(json) {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": json.appId, //公众号名称,由商户传入
"timeStamp": json.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr": json.nonceStr, //随机串
"package": json.package,
"signType": "MD5", //微信签名方式:
"paySign": json.paySign //微信签名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
alert('支付成功,请稍后查询,正在保存订票信息,若有疑问,请联系管理员..')
// 提交用户数据到数据库操做...
location.href = "/Home/MyTicket";
//不要在这里提交最后的数据到数据库,这里可能不正常(微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。)
}
else {
console.log('关闭了微信支付')
}
});
}
复制代码
再看关键的服务端代码,返回微信H5JSAPI
支付须要的参数:
/// <summary>
/// 预下单,获取jspay api参数
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<JsonResult> GetJsPayApi(UserOrder buyer)
{
try
{
#region 获取客户端参数 & 校验
string strTotal_fee = Request.Form["totalfee"];
string strFee = (double.Parse(strTotal_fee) * 100).ToString();
//JSAPI支付预处理
//若传递了相关参数,则调统一下单接口,得到后续相关接口的入口参数
string openId = string.Empty; //注意这里须要当前用户openId
if (Session["openid"] != null)
{
openId = Session["openid"].ToString(); //注意这里须要当前用户openId
}
int totalFee = int.Parse(strFee);
var wxPayConfig = new WeiXinPayConfig()
{
AppId = WxPayConfig.APPID,
MchId = WxPayConfig.MCHID,
OpenId = openId,
NotifyUrl = WxPayConfig.NOTIFY_URL,
MchPayKey = WxPayConfig.KEY,
ServerIp = WxPayConfig.IP,
TradeType = "JSAPI",
OutTradeNo = Guid.NewGuid().ToString("N").ToUpper()
};
#endregion
buyer.PayStatus = false;
buyer.PayOrderNo = wxPayConfig.OutTradeNo;
_userOrder.Insert(buyer);
var unifiedOrderData = await WxPayHelper.UnifiedOrder(wxPayConfig, totalFee);
var jsApiPayData = WxPayHelper.GetJsApiParams(wxPayConfig.MchPayKey, unifiedOrderData);
return Success(jsApiPayData);
}
catch (Exception ex)
{
return Fail(ex.Message);
}
}
复制代码
代码说明:
await WxPayHelper.UnifiedOrder(wxPayConfig, totalFee)
这个是封装的调用微信支付的【统一下单接口】,至关于预下单,参考接口说明,比较简单,须要注意的是这里的金额是分为单位,这样的设计在电商中比较广泛,避免小数点的不少问题。
WxPayHelper.GetJsApiParams(wxPayConfig.MchPayKey, unifiedOrderData)
这个是获取H5客户端JSAPI
支付须要的参数,参考接口文档,也就是上面调用onBridgeReady
方法唤起支付须要的参数,
微信H5内唤起支付package
参数须要【统一下单】的返回值,因此要预先调用这个接口了
这样咱们拿到JsApiPayParams
JS支付的参数,就能够直接调用起支付了
如上就能够经过微信内H5唤起微信客户端的支付功能进行付款了,可是这样就行了吗?想的太美了
在上线后,陆陆续续的发现部分安卓机支付了,后台没有数据,检查了日志,服务端也没有报错,那么确定的是,微信客户端没有执行我回调成功的代码
if (res.err_msg == "get_brand_wcpay_request:ok") { // 提交用户数据到数据库... }
复制代码
去查阅了微信支付这块的文档,//不要在这里提交最后的数据到数据库,这里可能不正常(微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。)
,表示微信客户端显示支付成功,并不必定可靠啊,微信本身都不相信本身,我信你个鬼啊,因此部分机器没有执行这里的回调逻辑...
在统一下单的时候,咱们会传递一个回调地址给微信,若是真正支付成功了,微信会将支付成功的回调信息传递给咱们,双方确认,这是异构系统双方识别成功的重要条件吧
根据微信的回调,咱们才能判断哪些订单真正支付成功了,而后更新咱们数据库的数据,参考微信支付结果通知接口文档
在咱们调用【统一下单】的时候,咱们会自动生成一个订单号 out_trade_no
传递给微信,咱们将客户下单的信息和这个订单号关联起来(以前代码不是这么写的)
微信支付成功结果通知给咱们,会把订单号带上,咱们就能够根据这个订单号进行判断,若是有回调通知,就表示支付成功了,那么改变这个订单号的状态为支付成成功就行了
微信支付通知回调报文:
<xml><appid><![CDATA[wx1393620e886ef***]]></appid>
<attach><![CDATA[附加信息:]]></attach>
<bank_type><![CDATA[ABC_DEBIT]]></bank_type>
<cash_fee><![CDATA[9950]]></cash_fee>
<device_info><![CDATA[WEB]]></device_info>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[N]]></is_subscribe>
<mch_id><![CDATA[1546577***]]></mch_id>
<nonce_str><![CDATA[804b9ff2ad7d4f2180a872694c608***]]></nonce_str>
<openid><![CDATA[oysLsw_FjFSgiAx0R6PBniyS1***]]></openid>
<out_trade_no><![CDATA[C8D24B9622B144A4B5AFF5D4DD8F3***]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[3D10FCE425D8D104A551C1800CEC2***]]></sign>
<time_end><![CDATA[20190727204913]]></time_end>
<total_fee>9950</total_fee>
<trade_type><![CDATA[JSAPI]]></trade_type>
<transaction_id><![CDATA[4200000384201907273227620***]]></transaction_id>
</xml>
复制代码
解析通知的xml报文,服务端代码示例:
/// <summary>
/// 微信支付回调
/// </summary>
/// <returns></returns>
[Route("notify")]
public ActionResult Notify()
{
string requestData = GetPostedString();
_log.Info("收到微信notify支付回调通知:" + requestData);
WeiXinPayData notifyResut = new WeiXinPayData();
notifyResut.FromXml(requestData);
if (!notifyResut.IsSet("transaction_id") || notifyResut.Get("transaction_id").ToString() == "") //若微信支付订单号不存在,直接响应错误给微信
{
return Content(GetNotifyMsg(false, "微信订单号不存在,响应失败!"));
};
string outTradeNo = notifyResut.Get("out_trade_no").ToString(); //商户系统内部订单号
string payResultCode = notifyResut.Get("result_code").ToString();
if (!string.IsNullOrEmpty(outTradeNo) && payResultCode == "SUCCESS")
{
var userOrder = _userOrder.Find(c => c.PayOrderNo == outTradeNo);
userOrder.PayStatus = true;
_userOrder.SaveChanges();
_log.Info($"微信回调商户支付单号:【{outTradeNo}】,购票人openId:{userOrder.OpenId},姓名:{userOrder.Name},电话:{userOrder.MobileNo},总金额:【{userOrder.Amount}】,支付确认成功!");
}
_log.Info("回调成功信息响应给微信:" + GetNotifyMsg(true, "OK"));
//订单验证成功后,将成功消息应答给微信回调 至关于再次给微信确认信息,表示已经收到了通知,否则微信会觉得你没有收到通知消息,会再重试发给你几回
return Content(GetNotifyMsg(true, "OK"));
}
/// <summary>
/// 生成成功/失败信息应答给微信支付回调
/// </summary>
/// <param name="errorMsg"></param>
/// <returns></returns>
private string GetNotifyMsg(bool isSuccess, string msg)
{
WeiXinPayData replyToWx = new WeiXinPayData();
replyToWx.Set("return_code", isSuccess ? "SUCCESS" : "FAIL");
replyToWx.Set("return_msg", msg);
// _log.Info("生成回推微信消息:" + replyToWx.ToXml());
return replyToWx.ToXml();
}
复制代码
这样服务端双方的通知就真正表示支付成功了。