先说下事情发生背景
之前做微信jsapi支付接口时,将官方的demo进行了简化整理,整合到自己的laravel框架控制器方法中,经历一番折腾后,终于可以正常支付了。
这次准备做个新的支付页面,框架还是用的原来的,控制器方法都是一样的,最后在测试的时候,毫无疑问,依然遇到各种问题。
问题一:
从后端传到js里的参数,双引号被自动转义了,获取代码是:
var msg = "{{$getmsg}}";
console.log(msg);
// 打印结果是:{"appId":"wx43a64e664e1ff289","nonceStr":"h2uwblf6l4pugm99m9og3oe6mq59ucpk","package":"prepay_id=wx26114518675771d11d88b6e988a0530000","signType":"JSAPI","timeStamp":1608954318,"paySign":"4B54E3BF828A9942930BB8B24C9B9D08"}
如想正常使用,需要将转义后的符号恢复,这个可以使用正则替换。或者直接先将这个变量显示到一个div中,这样就不会被自动转义,然后通过js再获取这个div内容,也可以达到目的。
问题二:
解决了转义的问题后,还存在无法调起微信支付窗口的问题。
在微信开发工具中错误信息一直是:
在手机端测试,弹出“调用支付JSAPI缺少参数:appId”的提示。
然后开始搜索这个错误提示的解决办法,各种说法烟花缭乱,后来搜索“isTrusted:false”,看到一个帖子做了说明,具体没看太明白,大概意思就是由于微信的js应该要先加载完成,才能使用自己的js来进行调用,解决办法就是让自己的js代码在其他代码加载完成后再执行,也就是说在自己的代码外包裹上 $(function(){ }); 即可。 要注意的是, $(function(){ })是jquery的方法,要先加载jquery.js文件<script src="/style/../../jquery.js"></script> 才可以使用。经测试,确实可用。
<script type="text/javascript">
$(function(){
//alert(typeof WeixinJSBridge);
var msg= $("#msg").text();
callpay(JSON.parse(msg));
});
</script>
<script type="text/javascript">
//微信支付demo里微信支付窗口调起方法
function jsApiCall(jsApiParameters)
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',jsApiParameters,
function(res){
WeixinJSBridge.log(res.err_msg);
}
);
}
function callpay(jsApiParameters)
{
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}else{
jsApiCall(jsApiParameters);
}
}
</script>
问题三:
在解决上面几个问题后,出现了最诡异的一个现象:同一个链接,在同一微信中,尝试多次访问,有时能够正常调出微信支付窗口,有时又报缺少appid的错误提示
也尝试清楚微信缓存了,还是会一定概率性地出现缺少appid的错误提示
但是可以确定的是,后端传过来的参数是没有问题,问题应该还是处在前端的js这里。
于是再次分析了一下处理问题二时看到的那个帖子,里面说了:“调起微信支付的时候,页面会加载微信的js,会在所有的script标签后面添加一行script用来加载微信的js”;按这个理解意思就是,在微信中载入这个页面时微信客户端都会在页面最下面加上一句js用来加载微信js或是微信对象,加载完成后,才能通过WeixinJSBridge来获取到,此时才能正常调起微信支付;
于是在自己的js最前面加了一句 alert(typeof WeixinJSBridge); 用来查看是否获取到了微信的WeixinJSBridge对象。
果然,只要是弹出undefined的,都会报缺少appid错误; 凡是弹出object的,都能够正常调起。
那么,至此大概了解了微信调用的逻辑了,可能某些方面理解的不完全正确,但是整体逻辑应该是没错了,所以,接下来要做的就是要保证:在微信自动加载其js完成之后,才能继续加载其他的js。
再看问题二中的代码,还有 jsApiCall() callpay() 这两个方法没有加到 $(function(){ }) 里面。所以按照上面分析的逻辑,应该把这两个js方法也加进去。所以最终将问题二中的代码又做了调整:
<script type="text/javascript">
$(function(){
alert(typeof WeixinJSBridge);
var msg= $("#msg").text();
callpay(JSON.parse(msg));
//微信支付demo里微信支付窗口调起方法
function jsApiCall(jsApiParameters)
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',jsApiParameters,
function(res){
WeixinJSBridge.log(res.err_msg);
}
);
}
function callpay(jsApiParameters)
{
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}else{
jsApiCall(jsApiParameters);
}
}
});
</script>
原以为至此问题已解决,然而没有,多数情况下WeixinJSBridge都能获取到对象object,然后正常弹出支付窗口,但是换了个手机,还是存在无法获取到ojbect的情况,一般是第一访问的时候都会报错。
根本原因还是js的加载顺序导致的,如果非常熟悉js的可以再研究更好的解决办法。
不熟悉js的也可以尝试采用其他办法,例如可以先加载页面,然后通过ajax来获取到jsApiParameters参数,然后再调用callpay()方法,这样无论如何页面面都是先加载好了的。 上面说的那些都为了方便,而直接使用laravel框架的直接加载的view,所以所以页面元素都是一起加载进来的。
function wxpayaction(){
$.ajax({
type: "POST",
url: "/test",
data: "_token={{csrf_token()}}&input="+$("#recform").serialize(),
success: function(msg){
if (msg=='exist') {
alert("订单重复");return false;
}
console.log(msg);
callpay(JSON.parse(msg));
}
});
}
关于调起微信支付窗口函数jsApiCall(jsApiParameters)参数 jsApiParameters的说明
1、jsApiParameters的参数需要是一个json对象,本身后端传递到前端是json字符串,在前端使用 JSON.parse(msg) 可轻松转换成json对象
2、jsApiParameters转换成json对象后,里面包含的参数具体如下:
然后在被 jsApiCall() 调用后,微信开始调起支付窗口,同时反馈调用结果如下:
可以看出,反馈的对象中的参数,比处理前jsApiParameters的参数少了一个appId,而不是之前的“isTrusted:false”。这样就说明调用正确了,此时在手机端就可以正常调起支付窗口了。