微信开发——H5微信支付

微信H5支付,分享下本身实际使用中踩过的坑

调用微信支付,咱们首先须要获取到微信用户的基本资料,在微信开发——网页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后端

微信H5支付接口调用

调用微信支付,首先要调用【统一下单】接口,微信接口地址参考

调用微信统一下单接口,须要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参数须要【统一下单】的返回值,因此要预先调用这个接口了

这样咱们拿到JsApiPayParamsJS支付的参数,就能够直接调用起支付了

如上就能够经过微信内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();
}
复制代码

这样服务端双方的通知就真正表示支付成功了。

相关文章
相关标签/搜索