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 ?>
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>
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 }
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 }
页面测试出现的问题:
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 }
把H5支付iOS H5拉起微信支付