H5呼起微信支付(个人实践总结)

Posted Web_boom

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了H5呼起微信支付(个人实践总结)相关的知识,希望对你有一定的参考价值。

H5呼起微信支付存在两种场景

第一种:其他浏览器呼起微信支付。

第二种:微信内部呼起微信支付。

项目说明:

我这边的项目要求的两种方式均要适用。 产品要求是当支付失败时或者未支付情况是停留在支付订单页面,可再次发起订单请求(新订单),故我在这项目中,把支付成功页面和订单页面在同一页(根据订单状态去展示)。

前期准备:

公众号设置:

内部呼起微信搭桥,需要配置一个网页授权域名,用于前端内部呼起微信生成对应的code给到后端。

需要注意的是,填入域名即可,不用在域名前加【http://】,不然会提示【域名或路径格式不正确,请参考注意事项】。别问我怎么知道的,因为我删掉了我写的前面【http://】就配置通过了。

后端涉及到支付,要配置支付授权。

公众平台配置位置:微信支付-开发设置-支付授权目录

前端代码

判断浏览器情况

初始化时判断浏览器内核,并针对不同情况做不同的事情。

init()
    this.wechatFlag = this.isWeChat();//本人项目提交时需要判断区分,所以就定义了全局
    if(this.wechatFlag)
        //当前在微信内部 do someThing
        // 比方判断是否有授权code
          let code = this.getQueryObject().code;
          if (code == null || code == '') 
            this.redirectPage();//重定向获取code
           else 
            this.code = code;
          
    else
        //外部浏览器 do someThing
        //项目里,我设置的支付订单页和成功页在同一个页面
        //所以外部浏览器付费与否都回到这个页面
        //所以我这要获取成功后的订单参数
        if (this.$route.query.orderNumber) //外部浏览器跳转重定向
            //从微信里重定向回来
            this.orderId = this.$route.query.orderNumber;//订单参数
            //this.getMyOrder();//请求订单数据
          
    
 

判断是否在微信内部浏览器(浏览器内核)isWeChat()

isWeChat()
      //判断是否为微信内部登录
      var ua = window.navigator.userAgent.toLowerCase();
      //console.log(ua);
      if (ua.match(/MicroMessenger/i) == 'micromessenger') 
        return true;//是微信内部
       else 
        return false;
      
    
 

外部浏览器成功回调后url传参获取getQueryObject()【公用函数】

getQueryObject(url)可当成一个公用方法
  url = url == null ? window.location.href : url;
  let search = url.substring(url.lastIndexOf("?") + 1);
  let obj = ;
  let reg = /([^?&=]+)=([^?&=]*)/g;
  search.replace(reg, function (rs, $1, $2) 
    let name = decodeURIComponent($1);
    let val = decodeURIComponent($2);
    val = String(val);
    obj[name] = val;
    return rs;
  );
  return obj;
 

第一种:其他浏览器呼起微信支付。

思路:其他浏览器呼起微信支付,更多的是依靠后台接口返回数据。

请求微信支付的接口 --> 接口返回一个链接 --> 跳转连接

 async submitOrder()
  this.isDisabledSubmitBtn = true //防止用户点击多次
    let params= ;//项目支付相关的参数
    try 
          let 
            retBody,
            retMsg
           = await requestPay (params)//请求接口
          if (retStatus === 'success') //成功判断依据
            if (retBody) //直接跳转链接
              window.location.href = retBody;
            
           else 
            this.$notify(retMsg || '提交订单失败')
            this.isDisabledSubmitBtn = false //防止用户点击多次
          
         catch (error) 
          this.error = true
          this.$notify('提交订单失败' || error)
          this.isDisabledSubmitBtn = false //防止用户点击多次
    
  



跳转回来之后就在会重新create了,获取与后台约定参数重新查询订单数据即可。 

第二种:微信内部浏览器

需要前端搭桥呼起浏览器内置方法

重定向获取用户code

redirectPage()
  //微信内部则重定向页面 
  let local = window.location.href;//当前地址
  let appId = 'thisistheappid';//填写公众号APPID
  window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=$appId&redirect_uri=$encodeURIComponent(local)&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect`  //跳转授权链接
, 

提交函数

submitOrderInside()
    let params = 
    code:this.code,
    ;//微信内部添加的订单,后台需要我传多一个用户code
    //因为code有时效性,所以失效了或者使用过之后需要重新获取个新的code
    try 
          let 
            retStatus,
            retBody,
            retMsg
           = await requestPayInside(params)//支付接口
          if (retStatus === 'success') 
            this.orderId = retBody.orderNumber;
            this.weChatParameter = //微信搭桥需要的数据
              appid: retBody.appid,
              timeStamp: retBody.timeStamp,
              nonceStr: retBody.nonceStr,
              packageValue: retBody.packageValue,
              signType: retBody.signType,
              paySign: retBody.paySign,
            ;
            this.wechatPay();//微信内置对象判断
           else 
            this.$notify(retMsg || '提交订单失败')
            this.isDisabledSubmitBtn = false //防止用户点击多次
            this.redirectPage();//重定向获取新的code
          
         catch (error) 
          this.error = true
          this.$notify('提交订单失败' || error)
          this.isDisabledSubmitBtn = false //防止用户点击多次
           this.redirectPage();//重定向获取新的code
        

     

wechatPay()解决微信内置对象报错

//搭桥前先解决微信内置对象报错
weixinPay (params) 
      var that = this;
      if (typeof WeixinJSBridge == "undefined") 
        if (document.addEventListener) 
          document.addEventListener('WeixinJSBridgeReady', that.onBridgeReady(params), false);
         else if (document.attachEvent) 
          document.attachEvent('WeixinJSBridgeReady', that.onBridgeReady(params));
          document.attachEvent('onWeixinJSBridgeReady', that.onBridgeReady(params));
        
       else 
        that.onBridgeReady();
      
    , 

搭桥onBridgeReady()

 //微信内置浏览器类
onBridgeReady () 
  var that = this;
  var timestamp = Math.round(that.weChatParameter.timeStamp).toString();
  window.WeixinJSBridge.invoke(
    'getBrandWCPayRequest', 
    debug: false,
    "appId": that.weChatParameter.appid,     //公众号名称,由商户传入
    "timeStamp": timestamp, //时间戳,自1970年以来的秒数
    "nonceStr": that.weChatParameter.nonceStr, //随机串
    "package": that.weChatParameter.packageValue,
    "signType": that.weChatParameter.signType, //微信签名方式:
    "paySign": that.weChatParameter.paySign, //微信签名
    jsApiList: [
      'chooseWXPay'
    ]
  ,
    function (res) 
      // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
      if (res.err_msg == "get_brand_wcpay_request:ok")  //支付成功后的操作
        that.isDisabledSubmitBtn = true;
        //支付成功
        that.getMyOrder();//请求成功后重新请求订单数据
       else if (res.err_msg == 'get_brand_wcpay_request:cancel')  //取消支付的操作
        that.isDisabledSubmitBtn = false //按钮恢复高亮
        that.redirectPage();//重定向获取新的code
        //取消支付
       else 
        //支付失败
        that.isDisabledSubmitBtn = false //按钮恢复高亮
        that.redirectPage();//重定向获取新的code

      
    
  );
 

以上就是个人微信支付的思路以及部分代码。仅供参考。

有关微信公众号和H5支付的一些记录

最近项目里面需要做公众号和H5支付的功能,根据自己的体验,整理了一下,做个记录。

首先我解释一下,为什么有公众号支付还要做H5支付?因为不确定每个用户在公众号上打开网站,所以另外做了H5支付。

以下是官方的解释:

H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付。

主要用于触屏版的手机浏览器请求微信支付的场景。可以方便的从外部浏览器唤起微信支付。

提醒:H5支付不建议在APP端使用,如需要在APP中使用微信支付,请接APP支付,文档详见微信支付开发文档

既然是公众号支付,准备工作要做好(下载的SDK是V3版本):

  必须是认证的服务号才能支付,详细接口权限见官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433401084。

  1.登陆公众号:

  

  2.

  

  3.登陆商户平台(pay.weixin.qq.com)

    在开发配中设置如下:

    H5支付直接填写服务器的域名即可。

    JSAPI授权目录这里需要注意,如果支付授权目录没有设置正确,在请求JSAPI时,会提示“-1当前页面的url未注册”的错误。” 

      首先要看你支付的当前页面URL,

      比如是:http://www.xxx.com/wxpay/jsapi.php

        你就必须填写:http://www.xxx.com/wxpay/

      假如是:http://www.xxx.com/wxpay/order/id/56.html

        你就必须写: http://www.xxx.com/wxpay/order/id/    

      假如是:http://www.xxx.com/wxpay/order?id=56

        你就必须写:http://www.xxx.com/wxpay/order/       

  

下面是代码部分(没有自己封装代码,直接用官方的示例代码进行修改)

  下载好官方的sdk,我是放到网站的根目录,看网上有把SDK进行集成封装好的,大家可自行搜索。

  1.在lib----->下的Wxpay.Config.php文件中,填好APPID、MCHID、KEY(操作密码)、APPSECRET(公众帐号secert)

  设置证书

  

  在配置文件的最后最好加上回调地址(当时我没有设置报错了,想不起来是什么错误了,没及时记录……)

    

  2.写一个验证token的方法(代码是网上找的,顺便找了一下有关token的登陆介绍:http://blog.csdn.net/resilient/article/details/72673222)

 1 <?php
 2     /**
 3      * wechat php test
 4      */
 5     //define your token
 6     define("TOKEN", "shendai");
 7     $wechatObj = new wechatCallbackapiTest();
 8     $wechatObj->valid();
 9 
10     class wechatCallbackapiTest
11     {
12         public function valid()
13         {
14             $echoStr = $_GET["echostr"];
15             //valid signature , option
16             if ($this->checkSignature())
17             {
18                 echo $echoStr;
19                 exit;
20             }
21         }
22         public function responseMsg()
23         {
24             //get post data, May be due to the different environments
25             $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
26             //extract post data
27             if (!empty($postStr))
28             {
29                 $postObj = simplexml_load_string($postStr, \'SimpleXMLElement\', LIBXML_NOCDATA);
30                 $fromUsername = $postObj->FromUserName;
31                 $toUsername = $postObj->ToUserName;
32                 $keyword = trim($postObj->Content);
33                 $time = time();
34                 $textTpl = "<xml>
35                             <ToUserName><![CDATA[%s]]></ToUserName>
36                             <FromUserName><![CDATA[%s]]></FromUserName>
37                             <CreateTime>%s</CreateTime>
38                             <MsgType><![CDATA[%s]]></MsgType>
39                             <Content><![CDATA[%s]]></Content>
40                             <FuncFlag>0</FuncFlag>
41                             </xml>";
42                 if (!empty($keyword))
43                 {
44                     $msgType = "text";
45                     $contentStr = "Welcome to wechat world!";
46                     $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
47                     echo $resultStr;
48                 }
49                 else
50                 {
51                     echo "Input something...";
52                 }
53             }
54             else
55             {
56                 echo "";
57                 exit;
58             }
59         }
60         private function checkSignature()
61         {
62             $signature = $_GET["signature"];
63             $timestamp = $_GET["timestamp"];
64             $nonce = $_GET["nonce"];
65             $token = TOKEN;
66             $tmpArr = array($token, $timestamp, $nonce);
67             sort($tmpArr);
68             $tmpStr = implode($tmpArr);
69             $tmpStr = sha1($tmpStr);
70             if ($tmpStr == $signature)
71             {
72                 return true;
73             }
74             else
75             {
76                 return false;
77             }
78         }
79 
80 
81     }
82 
83 ?>
View Code

  3.公众号支付文件jsapi.php,只用示例代码进行了修改

  1 <?php
  2     ini_set(\'date.timezone\',\'Asia/Shanghai\');
  3     //error_reporting(E_ERROR);
  4     require_once "$wx_root/lib/WxPay.Api.php";
  5     require_once "WxPay.JsApiPay.php";
  6     require_once \'log.php\';
  7 
  8     //初始化日志
  9     $logHandler= new CLogFileHandler("$wx_root/logs/".date(\'Y-m-d\').\'.log\');
 10     $log = Log::Init($logHandler, 15);
 11 
 12     //打印输出数组信息
 13     function printf_info($data)
 14     {
 15         foreach($data as $key=>$value){
 16             echo "<font color=\'#00ff55;\'>$key</font> : $value <br/>";
 17         }
 18     }
 19 
 20     //①、获取用户openid
 21     $tools = new JsApiPay();
 22     $openId = $tools->GetOpenid();
 23 
 24     //②、统一下单
 25     $input = new WxPayUnifiedOrder();
 26     $input->SetBody("xxx充值");
 27     $input->SetAttach("xxx");
 28     $input->SetOut_trade_no($orderNo); //订单号
 29     $input->SetTotal_fee($trade[\'fee\']); //充值的金额
 30     $input->SetTime_start(date("YmdHis"));
 31     $input->SetTime_expire(date("YmdHis", time() + 600));
 32     $input->SetGoods_tag("xxx充值");
 33     $input->SetNotify_url("http://xxx.xxx.com/xxx/notify");//支付成功的回调地址
 34     $input->SetTrade_type("JSAPI"); //支付类型
 35     $input->SetOpenid($openId);
 36     $order = WxPayApi::unifiedOrder($input);
 37 //    echo \'<font color="#f00"><b>xxx支付页面</b></font><br/>\';
 38 //    printf_info($order);
 39     $jsApiParameters = $tools->GetJsApiParameters($order);
 40 
 41     //获取共享收货地址js函数参数
 42     $editAddress = $tools->GetEditAddressParameters();
 43 
 44     //③、在支持成功回调通知中处理成功之后的事宜,见 notify.php
 45     /**
 46      * 注意:
 47      * 1、当你的回调地址不可访问的时候,回调通知会失败,可以通过查询订单来确认支付是否成功
 48      * 2、jsapi支付时需要填入用户openid,WxPay.JsApiPay.php中有获取openid流程 (文档可以参考微信公众平台“网页授权接口”,
 49      * 参考http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html)
 50      */
 51 ?>
 52 
 53 <html>
 54 <head>
 55     <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
 56     <meta name="viewport" content="width=device-width, initial-scale=1"/>
 57     <meta http-equiv="X-UA-Compatible" content="ie=edge">
 58     <title>xxx-支付</title>
 59     <style>
 60         html, body {
 61             margin: 0;
 62             width: 100%;
 63             height: 100%;
 64         }
 65         .modal {
 66             position: relative;
 67             width: 100%;
 68             height: 100%;
 69             background-color: rgba(0, 0, 0, .6);
 70         }
 71         .dialog {
 72             position: absolute;
 73             top: 50%;
 74             left: 50%;
 75             width: 210px;
 76             transform: translate(-50%, -50%);
 77             background-color: #fff;
 78             border-radius: 4px;
 79         }
 80         .dialog .head {
 81             height: 36px;
 82             line-height: 36px;
 83             border-bottom: 1px solid #1aad19;
 84             text-align: center;
 85             font-size: 14px;
 86         }
 87 
 88         .dialog .paymoney {
 89             padding: 14px;
 90 
 91         }
 92         .dialog .paymoney span {
 93             display: block;
 94             font-size: 14px;
 95             text-align: center;
 96         }
 97         .dialog .paymoney b {
 98             display: block;
 99             font-size: 28px;
100             text-align: center;
101         }
102         .dialog .paymoney a {
103             background: #1aad19;
104             padding: 8px 0;
105             margin-top:5px;
106             display: block;
107             border-radius: 4px;
108             text-decoration: none;
109             text-align: center;
110             font-size: 18px;
111             color: #fff;
112         }
113         .dialog .btn-pay {
114 
115         }
116     </style>
117     <script type="text/javascript">
118         //调用微信JS api 支付
119         function jsApiCall()
120         {
121             WeixinJSBridge.invoke(
122                 \'getBrandWCPayRequest\',
123                 <?php echo $jsApiParameters; ?>,
124                 function(res){
125                     if(res.err_msg == \'get_brand_wcpay_request:ok\')
126                     {
127                         window.location.href = \'支付成功后跳转地址\';
128                     }
129                 }
130             );
131         }
132 
133         function callpay()
134         {
135             if (typeof WeixinJSBridge == "undefined"){
136                 if( document.addEventListener ){
137                     document.addEventListener(\'WeixinJSBridgeReady\', jsApiCall, false);
138                 }else if (document.attachEvent){
139                     document.attachEvent(\'WeixinJSBridgeReady\', jsApiCall);
140                     document.attachEvent(\'onWeixinJSBridgeReady\', jsApiCall);
141                 }
142             }else{
143                 jsApiCall();
144             }
145         }
146     </script>
147     <script type="text/javascript">
148         //获取共享地址
149         //    function editAddress()
150         //    {
151         //        WeixinJSBridge.invoke(
152         //            \'editAddress\',
153         //            <?php //echo $editAddress; ?>//,
154         //            function(res){
155         //                var value1 = res.proviceFirstStageName;
156         //                var value2 = res.addressCitySecondStageName;
157         //                var value3 = res.addressCountiesThirdStageName;
158         //                var value4 = res.addressDetailInfo;
159         //                var tel = res.telNumber;
160         //
161         //                alert(value1 + value2 + value3 + value4 + ":" + tel);
162         //            }
163         //        );
164         //    }
165         //
166         //    window.onload = function(){
167         //        if (typeof WeixinJSBridge == "undefined"){
168         //            if( document.addEventListener ){
169         //                document.addEventListener(\'WeixinJSBridgeReady\', editAddress, false);
170         //            }else if (document.attachEvent){
171         //                document.attachEvent(\'WeixinJSBridgeReady\', editAddress);
172         //                document.attachEvent(\'onWeixinJSBridgeReady\', editAddress);
173         //            }
174         //        }else{
175         //            editAddress();
176         //        }
177         //    };
178 
179     </script>
180 </head>
181 <body>
182 <div class="modal">
183     <div class="dialog">
184         <div class="head">
185             支付
186         </div>
187         <div class="paymoney">
188             <span>xxx有限公司</span>
189             <b>¥<?php echo $trade[\'fee\']; ?></b>
190 
191             <a type="button" onclick="callpay()" >立即支付</a>
192         </div>
193 
194     </div>
195 </div>
196  <br/>
197 </body>
198 </html>
View Code

  4.在notify.php文件中写成功支付后的业务代码

 1 //重写回调处理函数
 2 public function NotifyProcess($data, &$msg)
 3 {
 4     Log::DEBUG("call back:" . json_encode($data));
 5     $notfiyOutput = array();
 6         
 7     if(!array_key_exists("transaction_id", $data)){
 8         $msg = "输入参数不正确";
 9         return false;
10     }
11     //查询订单,判断订单真实性
12     if(!$this->Queryorder($data["transaction_id"])){
13         $msg = "订单查询失败";
14         return false;
15     }
16 
17     $out_trade_no = $data["out_trade_no"]; //订单号
18 
19     //判断该笔订单是否在商户网站中已经做过处理
20     $trade = db(\'订单表\')->where(\'orderNo\',$out_trade_no)->find();
21     //请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
22     if ($data[\'mch_id\'] == \'商户号\' && $data[\'total_fee\'] == $trade[\'fee\'])
23     {
24         db(\'订单表\')->where(\'id\',$trade[\'id\'])->update([\'feeStatus\'=>1]);
25 
26     }
27     else
28     {
29        return false;
30     }
31 
32     return true;
33 }        
View Code

  5.编写支付接口回调接口(有重复代码,懒得去改了)

 1  /*微信客户端支付*/
 2     public function pay()
 3     {
 4         if(input(\'param.total\') > 0)
 5         {
 6             //添加一个订单
 7            当用户点击支付按钮时添加一条订单记录
 8            ...
 9 
10             $wx_root = \'./Wxpay\';
11             $file = \'./Wxpay/example/jsapi.php\';
12             require_once $file;
13         }
14     }
15     /*H5支付*/
16     public function h5Pay()
17     {
18         if(input(\'param.total\') > 0)
19         {
20             //添加一个订单
21            当用户点击支付按钮时添加一条订单记录
22            ...
23 
24             $wx_root = \'./Wxpay\';
25             $file = \'./Wxpay/example/mweb.php\';
26             require_once $file;
27         }
28 
29     }
30     /*微信支付回调*/
31     public function notify()
32     {
33         $wx_root = \'./Wxpay\';
34         $file = \'./Wxpay/example/notify.php\';
35         require_once $file;
36     }
View Code

页面测试出现的问题:

  1.文件加载错误,所以支付接口里面已写成了绝对路径。(在控制器里面,include文件是相对于网站的根目录的);

  2.出现’-1页面未注册‘的报错,查各种资料,说是支付授权目录的问题,填写规则上面已有说明;

  3.Notice: Use of undefined constant CURLOP_TIMEOUT - assumed \'CURLOP_TIMEOUT 报错 ,解决如下:

  (在example的JsApi.php的大概99行,修改如下:)

//        curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);

 

  4.H5支付出现curl出错,错误码60,解决如下:

    在lib--->wxpay.api.php大概538行处,修改如下代码

 1 //        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
 2 //        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
 3         if(stripos($url,"https://")!==FALSE){
 4             curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
 5             curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
 6             curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
 7         }
 8         else
 9         {
10             curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
11             curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
12         }
View Code

 

 

H5支付iOS H5拉起微信支付

有关微信公众号和H5支付的一些记录

iOS开发-处理H5拉起微信支付返回到Safari情况

Android 微信H5支付,无法拉起微信支付页面

Android 微信H5支付,无法拉起微信支付页面

Android 微信H5支付,无法拉起微信支付页面