signed

QiShunwang

“诚信为本、客户至上”

PHP微信支付之---调用支付JSAPI缺少参数:appId

2020/12/26 16:58:14   来源:

先说下事情发生背景

之前做微信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,所以所以页面元素都是一起加载进来的。

 

关于调起微信支付窗口函数jsApiCall(jsApiParameters)参数 jsApiParameters的说明

1、jsApiParameters的参数需要是一个json对象,本身后端传递到前端是json字符串,在前端使用 JSON.parse(msg)  可轻松转换成json对象

2、jsApiParameters转换成json对象后,里面包含的参数具体如下:

然后在被 jsApiCall() 调用后,微信开始调起支付窗口,同时反馈调用结果如下:

可以看出,反馈的对象中的参数,比处理前jsApiParameters的参数少了一个appId,而不是之前的“isTrusted:false”。这样就说明调用正确了,此时在手机端就可以正常调起支付窗口了。