zoukankan      html  css  js  c++  java
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结

    最近负责的一些项目开发,都用到了微信支付(微信公众号支付、微信H5支付、微信扫码支付、APP微信支付)。在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存。

    先说注意的第一点,所有支付的第一步都是请求统一下单,统一下单,统一下单,请求URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder。统一下单的目的是拿到预支付交易会话标识prepay_id,这个是必须的。所有的支付调用都是通过prepay_id来识别。

    再说一个微信官方提供的一个很重要的工具,微信支付接口签名校验工具(网址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1),此工具旨在帮助开发者检测调用【微信支付接口API】时发送的请求参数中生成的签名是否正确,提交相关信息后可获得签名校验结果。特别实用!特别实用!特别实用!签名只要正确了,一切就OK了!

     还有就是官方提供的几种支付方式的对比说明,如下图所示。

    第一部分 微信公众号支付

    微信公众号支付需要配置的参数有:APPID(微信公众号开发者ID)、APPSECRET(微信公众号开发者密码)、MCHID(商户ID)、KEY(商户密钥)。

    微信公众号支付应用的场景是在微信内部的H5环境中是用的支付方式。因为要通过网页授权获取用户的OpenId,所以必须要配置网页授权域名。同时要配置JS接口安全域名,如下图所示:

    以PHP为例,使用官方demo一个最常见的问题就是500错误,回调没反应,这个一般情况下是xml数据解析出现的问题(错误在这里WxPay.Data.php中WxPayDataBase类的FromXml()方法),解决方案如下:

    复制代码
    public function FromXml($xml) {   
        if(!$xml) {  
            throw new WxPayException("xml数据异常!");  
        }  
        //将XML转为array  
        //禁止引用外部xml实体  
        libxml_disable_entity_loader(true); //这句导致出现上述问题  
        $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);       
        return $this->values;  
    }  
    复制代码

    问题就出现在libxml_disable_entity_loader(),它的作用是设置是否禁止从外部加载XML实体,设为true就是禁止。可以使用将代码改成以下内容进行解决:

    复制代码
    public function FromXml($xml) {  
        if(!$xml) {  
            throw new WxPayException("xml数据异常!");  
        }  
        //将XML转为array  
        //禁止引用外部xml实体  
        $disableLibxmlEntityLoader = libxml_disable_entity_loader(true); //改为这句  
        $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);  
        libxml_disable_entity_loader($disableLibxmlEntityLoader); //添加这句  
        return $this->values;  
    }  
    复制代码

    本人也尝试过这样一个简单的方案,如下,直接屏蔽(在低版本PHP5.2中测试通过)libxml_disable_entity_loader():

    复制代码
    public function FromXml($xml) {   
        if(!$xml) {  
            throw new WxPayException("xml数据异常!");  
        }  
        //将XML转为array  
        //禁止引用外部xml实体  
        //libxml_disable_entity_loader(true); //或者是把这句屏蔽
        $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);       
        return $this->values;  
    }  
    复制代码

    把这个解决之后就OK了。

    还有一个问题就是“curl出错,错误码:60”,这个错误是由于服务器和PHP版本导致的,最近一次出现是在一个PHP5.2版本的原生项目上。微信官方对支付的数据传输提出了三点建议:

    ◆ 使用HTTPS确保网络传输安全性。
    ◆ 禁用SSL等不安全协议和算法,建议使用TLS1.2。
    ◆ 不要轻易的尝试设计和实现自己的加密传输算法,几乎都会存在问题。

    具体的错误信息在日志里面是这样的:Fatal error: Uncaught exception ‘WxPayException‘ with message ‘curl出错,错误码:60‘ in ,目前的解决方案如下:

    最原始的3.0demo里面,找到WxPay.JsApiPay.php文件的99行:

    curl_setopt($ch, CURLOP_TIMEOUT, 30); 

    最早的example代码里少了一个“T”,这个问题官方已经解决,正确代码应该是如下的:

    curl_setopt($ch, CURLOPT_TIMEOUT, 30);

    还有就是安全校验的问题,在官方demo WxPay.Api.php 文件中找到如下代码:

    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验

    将上述代码做出如下修改:

    复制代码
    if(stripos($url,"https://")!==FALSE){
            curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
            }    else    {
            curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
            curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
    } 
    复制代码

    这样的话,curl出错,错误码:60这个问题就可以解决了。

    第二部分 微信H5支付

    微信H5支付是微信官方2017年上半年刚刚对外开放的支付模式,它主要应用于在手机网站在移动浏览器(非微信环境)调用微信支付的场景。底层的技术以及支付链接本质上是财付通。

    注意:微信H5支付需要在微信支付商户平台单独申请开通,否则无法使用。

    微信H5支付的流程比较简单,就是拼接请求的xml数据,进行统一下单,获取到支付的mweb_url,然后请求这个url网址就行。请求使用curl函数,使用的时候需要注意设置header参数。

    复制代码
        $headers = array();  
        $headers[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';   
        $headers[] = 'Connection: Keep-Alive';   
        $headers[] = 'Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3';  
        $headers[] = 'Accept-Encoding: gzip, deflate';  
        $headers[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:22.0) Gecko/20100101 Firefox/22.0'; 
    复制代码

    下面直接奉上整个流程源码。

    复制代码
    $userip = $_SERVER["REMOTE_ADDR"]; //获得用户设备IP
    $appid = "wx24d9dbdf00000";//微信
    $mch_id = "888888888";//微信官方的
    $key = "FSDFSD2356DSD00";//自己设置的微信商家key
    
    $nonce_str=MD5($out_trade_no);//随机字符串
    $total_fee = $total_fee*100; //金额
    $spbill_create_ip = $userip; //IP
    $notify_url = "http://www.bojuwang.net/"; //回调地址
    $trade_type = 'MWEB';//交易类型 具体看API 里面有详细介绍
    
     
     $scene_info ='{"h5_info":{"type":"Wap","wap_url":"http://www.hnyjzpw.com","wap_name":"支付"}}';//场景信息 必要参数
     $signA ="appid=$appid&body=$body&mch_id=$mch_id&nonce_str=$nonce_str&notify_url=$notify_url&out_trade_no=$out_trade_no&scene_info=$scene_info&spbill_create_ip=$spbill_create_ip&total_fee=$total_fee&trade_type=$trade_type";
    
     $strSignTmp = $signA."&key=$key"; //拼接字符串  注意顺序微信有个测试网址 顺序按照他的来 直接点下面的校正测试 包括下面XML  是否正确
     $sign = strtoupper(MD5($strSignTmp)); // MD5 后转换成大写
    
     $post_data="<xml><appid>$appid</appid><body>$body</body><mch_id>$mch_id</mch_id><nonce_str>$nonce_str</nonce_str><notify_url>$notify_url</notify_url><out_trade_no>$out_trade_no</out_trade_no><scene_info>$scene_info</scene_info><spbill_create_ip>$spbill_create_ip</spbill_create_ip><total_fee>$total_fee</total_fee><trade_type>$trade_type</trade_type><sign>$sign</sign>
    </xml>";//拼接成XML 格式
    
    
    $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信传参地址
    
    $dataxml = http_post($url,$post_data,$headers); 
    $objectxml = (array)simplexml_load_string($dataxml,'SimpleXMLElement',LIBXML_NOCDATA); //将微信返回的XML 转换成数组
    function http_post($url='',$post_data=array(),$header=array(),$timeout=30) {
        
        $ch = curl_init();  
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查  
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);  // 从证书中检查SSL加密算法是否存在  
        curl_setopt($ch, CURLOPT_URL, $url);  
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);  
        curl_setopt($ch, CURLOPT_POST, true);  
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);  
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);   
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);  
        
        $response = curl_exec($ch);  
    
        curl_close($ch);
    
        return $response;
    }
    
    if($objectxml['return_code'] == 'SUCCESS'){
        $mweb_url= $objectxml['mweb_url'];
        // header("Location:$mweb_url");
    }
    
    $redirect_url = urlencode("http://www.bojuwang.net/");
    复制代码

    H5支付的回调代码如下,注意xml数据的接收。这是一个很大的坑,PHP需要使用 $GLOBALS['HTTP_RAW_POST_DATA']解析微信支付结果返回的xml。

    复制代码
    $xml = $GLOBALS['HTTP_RAW_POST_DATA'];
    $dataxml = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);//转成数组,
    
    if($dataxml['return_code'] == 'SUCCESS'){
       //success
    }
    复制代码

    第三部分 微信扫码支付

    微信扫码支付一般应用的场景是PC端电脑支付。微信扫码支付可分为两种模式,根据支付场景选择相应模式。一般情况下的PC端扫码支付选择的是模式二,需要注意的是模式二无回调函数。

    【模式一】商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。

    【模式二】商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。

    微信扫码支付最友好的解决方案就是支付完成之后通过JS设置监听函数,通过该函数完成跳转。可参考的代码如下:

    复制代码
    <script type="text/javascript">
        $(function () {
            sendPost(); //调用监听事件
        });
        //监听订单支付状态
        function sendPost() {
            //发送AJAX请求
            $.ajax({
                url: "listen.aspx",
                type: "POST",
                timeout: 30000,
                data: { "order_no": "<%=order_no %>" },
                dataType: "json",
                success: function (data, type) {
                    if (data.status == 1) {
                        $("#tipshow").show();
                        setTimeout(function () {
                            location.href = data.url; //支付成功后跳转
                        }, 1000);
                    } else {
                        time();
                    }
                }
            });
        }
        function time() {
            setTimeout(function () {
                sendPost();
            }, 5000);
        }
    </script>
    复制代码

    第四部分 微信小程序支付

    微信小程序支付是在小程序环境中使用的微信支付方式。

    相对于上述几个支付方式,微信小程序支付则显得更简单一些,不涉及到异步通知。在微信小程序中通过官方提供的API wx.requestPayment(OBJECT)发起微信支付,示例代码如下:

    复制代码
    wx.requestPayment({
       'timeStamp': '',
       'nonceStr': '',
       'package': '',
       'signType': 'MD5',
       'paySign': '',
       'success':function(res){
       },
       'fail':function(res){
       }
    })
    复制代码

    通过上面我们可以看到,小程序支付的需要timeStamp、nonceStr、package、signType、paySign这五个参数。然后通过回调函数success或者是fail处理业务逻辑。

    后台处理程序的第一步还是统一下单,通过统一下单拿到prepay_id,然后获取相关参数,通过接口(Ajax)传到小程序端发起微信支付。因为统一下单需要用到用户的OpenId,所以在发起统一下单之前要通过小程序的API  wx.login(OBJECT)调用接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。主要是为了拿到OpenId!

    涉及到是一个.NET项目,大致的.NET后台代码如下:

    复制代码
     1 /// <summary>
     2 /// 获取支付的参数
     3 /// </summary>
     4 private void get_pay_params(HttpContext context)
     5 {
     6     string openId = BJRequest.GetFormString("openid");
     7     int user_id = BJRequest.GetFormIntValue("user_id", 0);
     8 
     9     string appId = MiniPayConfig.APPID;
    10     string timeStamp = MiniPay.GenerateTimeStamp();
    11     string nonceStr = MiniPay.GenerateNonceStr();
    12     string signType = "MD5";
    13     string outTradeNo=MiniPay.GenerateOutTradeNo();
    14 
    15     //获取统一下单结果,主要是为了拿到prepay_id
    16     MiniPay mnpay=new MiniPay();
    17     MiniPayData unifiedOrderResult = mnpay.GetUnifiedOrderResult(openId,outTradeNo);
    18 
    19 
    20     //小程序支付需要的参数
    21     MiniPayData data = new MiniPayData();
    22     data.SetValue("appId", appId);
    23     data.SetValue("timeStamp", timeStamp);
    24     data.SetValue("nonceStr", nonceStr);
    25     data.SetValue("package", "prepay_id=" + unifiedOrderResult.GetValue("prepay_id"));
    26     data.SetValue("signType", "MD5");
    27     data.SetValue("paySign", data.MakeSign());
    28 
    29 
    30     StringBuilder strTxt = new StringBuilder();
    31 
    32     strTxt.Append("{");
    33     strTxt.Append(""timeStamp":"" + timeStamp + """);
    34     strTxt.Append(","nonceStr":"" + nonceStr + """);
    35     strTxt.Append(","package":"" + data.GetValue("package") + """);
    36     strTxt.Append(","signType":"" + signType + """);
    37     strTxt.Append(","paySign":"" + data.GetValue("paySign") + """);
    38     strTxt.Append("}");
    39 
    40     context.Response.Write(strTxt.ToString());
    41 }
    复制代码

    微信小程序端拿到参数之后,发起微信支付请求,小程序端代码如下:

    复制代码
     1  //调起微信支付
     2   wxpay: function(){
     3       var that=this;
     4       wx.request({
     5         url: 'https://888.com/api/order.ashx?action=get_pay_params',
     6         method: 'post', 
     7         data: {
     8           openid: that.data.openId,
     9           user_id: that.data.userId
    10         },
    11         header: {
    12           'Content-Type':  'application/x-www-form-urlencoded'
    13         },
    14         success: function(res){
    15           //if(res.data.status==1){
    16             var order=res.data;
    17             wx.requestPayment({
    18               timeStamp: order.timeStamp,
    19               nonceStr: order.nonceStr,
    20               package: order.package,
    21               signType: 'MD5',
    22               paySign: order.paySign,
    23               success: function(res){
    24                 
    25                 //支付成功,处理相应的订单
    26                 wx.request({
    27                   url: 'https://8888.com/api/order.ashx?action=order_edit_pay',
    28                   method: 'post',
    29                   data: {
    30                     order_id: that.data.returnOrderId
    31                   },
    32                   header: {
    33                     'Content-Type': 'application/x-www-form-urlencoded'
    34                   },
    35                   success: function (res) {
    36                     var data = res.data;
    37                     if (data.status == 1) {
    38                       console.log("支付成功,处理订单:" + that.data.returnOrderId);
    39                       wx.showToast({
    40                         title: "订单支付并处理成功!",
    41                         duration: 1000,
    42                       });
    43                       setTimeout(function () {
    44                         wx.navigateTo({
    45                           url: '../user/dingdan?currentTab=2&otype=deliver',
    46                         });
    47                       }, 1500);
    48                     } else {
    49                       wx.showToast({
    50                         title: "订单处理失败!",
    51                         duration: 2500
    52                       });
    53                     }
    54                   },
    55                   fail: function (e) {
    56                     wx.showToast({
    57                       title: '处理订单网络异常!',
    58                       duration: 2000
    59                     });
    60                   }
    61                 });
    62               },
    63               fail: function(res) {
    64                 wx.showToast({
    65                   title:'支付失败',
    66                   duration:1000
    67                 });
    68                 wx.navigateTo({
    69                   url: '../user/dingdan?currentTab=0&otype=all',
    70                 });
    71               }
    72             })
    73         },
    74         fail: function() {
    75           // fail
    76           wx.showToast({
    77             title: '支付时网络异常!',
    78             duration: 2000
    79           });
    80         }
    81       })
    82   }
    复制代码

    第五部分 微信APP支付

    微信APP支付是在APP应用中使用的微信支付方式。

    最后,总结一下上述几种支付方式需要注意的点。

    1. 所有的支付参数都需要到微信支付商户平台(pay.weixin.qq.com)配置参数。

    2. 微信公众号支付、微信扫码支付需要在微信公众号里面申请开通;APP支付需要在微信开放平台申请开通(open.weixin.qq.com);小程序支付需要在小程序平台申请开通。

    3. 仅有公众号支付和扫码支付需配置支付域名,APP支付、刷卡支付无需配置域名。下图就是在微信支付商户平台配置授权域名的界面。

    4. 所有使用JS API方式发起支付请求的链接地址,都必须在当前页面所配置的支付授权目录之下。下单前需要调用【网页授权获取用户信息】接口获取到用户的Openid。

    5. 当公众平台接到扫码支付请求时,会回调当前页面所配置的支付回调链接传递订单信息。

  • 相关阅读:
    免费的视频、音频转文本
    Errors are values
    Codebase Refactoring (with help from Go)
    Golang中的坑二
    Cleaner, more elegant, and wrong(msdn blog)
    Cleaner, more elegant, and wrong(翻译)
    Cleaner, more elegant, and harder to recognize(翻译)
    vue控制父子组件渲染顺序
    computed 和 watch 组合使用,监听数据全局数据状态
    webstorm破解方法
  • 原文地址:https://www.cnblogs.com/kenshinobiy/p/8878045.html
Copyright © 2011-2022 走看看