之前就看到一些博客用的收款码都是三合一小磁怪,于是我也上网了解了一下支付宝,微信,qq收款码合成原理,以及对相关源码的进行了一番改造

收款码源码

在网上看到一些做合成码的网站,但类似的开源项目还是比较少的,找了一番看到一位阿珏博主,在github上写的一份关于合成码的源码。
https://github.com/iAjue/qr
而我的合成码就是基于这份开源项目进行的改造。


合成码原理

二维码

首先我们需要先了解一下二维码,手机、电脑或者说电子设备识别二维码,主要就是将二维码转换为二进制,然后再通过二进制转换为相应的数字、字母、符号。关于二维码、一维码详细的说明我可能会另开一篇。

厂商识别

说到厂商识别,我们就需要思考一个问题,我们都知道,无论是微信、qq、支付宝扫码,都会访问一段网址,跳转到另一个页面。那么我们的微信,qq,支付宝是不是可以暂时当做一个浏览器,如果是浏览器的话,访问服务器就肯定有User-Agent,而由于各个厂商的浏览器肯定都有自己独特的标识,于是,我们就可以识别出各个厂商。那么它们独特的标识是什么呢,我列在下面:
QQ:QQ/
微信:MicroMessenger/
支付宝:AlipayClient/


改造

好了,源码有了,原理也清楚了,接下来就是怎么塞进自己的网站中了。

构建二级网站

由于没有多余的域名,并且我的资金也不允许有多余的域名。因此,我的想法是这个域名下添加一个二级目录,用来专门存放我的工具,然后将这个项目放到我的tools目录中去了。

首先,看了一下wordpress官网好像有个二级网站,和我的想要的功能有点像,但是它是二级博客。我就想着也许可以改改呢?结果,并不是我想象的那么美好,它真的只是第二个博客。

度娘搜了搜,没找到什么想要的,想了想还是用最原始的办法,把tools放到放到项目的根目录中,不就行了。然后,通过宝塔的默认文档,将public目录隐藏了,按照github的安装步骤完成了项目的搭建。

寻找问题原因

项目虽然搭好了,但是功能并没有实现(咋就不能直接结束呢,麻烦。。)

没办法,只能看看人家的源码,观察一下和自己的博客哪里不大契合。
先格式化了他的js,然后就感觉自己跟看天书一样了。。。,全是a,b,c,d...算了,我还是去chrome的开发者模式,看看元素的class和id,然后通过eclipse的全局查找它们和js的关系,大致的理一下关系吧。然后,我吭哧吭哧看了半天,感觉好像前台没什么问题。
难道php写的有问题,等等,thinkphp是啥,我好像没接触过。我记得,好像还加了伪静态,说这些伪静态是为了正常的运行thinkphp而添加的,那是不是我的伪静态有问题。

解决问题

我仔细的搜索了一些伪静态的相关资料,发现所谓伪静态其实就是使用简单的正则对url进行改造或者重写。
首先,看一下thinkphp的伪静态

location / {
    if (!-e $request_filename){
        rewrite  ^(.*)$  /index.php?s=$1  last;   break;
    }
}

根据我对正则和伪静态的理解,如果找不到url对应的文件就将文件名作为参数交给index.php来处理。猜测thinkphp原理应该是会根据参数名称,运行index.php中的相应的方法进行处理。

然后改造了一下,进行测试。

location /tools/qr-code/ {
    if (!-e $request_filename){
        rewrite  ^/tools/qr-code/public/(.*)$  /tools/qr-code/?s=$1  last;   break;
    }
}

ok,好像换了一个错误了,很好!!有效果!!但是,我们还有一个wp2的伪静态,两者叠加起来似乎有点冲突,于是,我进行了合成。

rewrite ^.*/files/(.*)$ /wp-includes/ms-files.php?file=$1 last;
if (!-e $request_filename){

    rewrite  ^/tools/qr-code/public/(.*)$  /tools/qr-code/?s=$1  last;
    rewrite ^.+?(/wp-.*) $1 last;
    rewrite ^.+?(/.*\.php)$ $1 last;
    rewrite ^ /index.php last;
}

那么我们迈进下一步,

关于这个问题,首先去github源码的提问区看看,有没有类似的问题,经过简单的查找,我们很快就发现问题所在。二维码的API挂了。。。
至于如何更改直接可以在config.php中更改。不过,新版用的好像是Zxing包,也就是说这个法子行不通了。
观察了一下这个Zxing包,发现它和github上php-qrcode-detector-decoder项目差不多。然后,去度娘看了一下这个包,发现它的识别率普遍都不是很高,必须是纯正的二维码。这可不行,看它github上的介绍,发现它是基于Zxing这个Java项目改造的。于是,我去看了一眼这个项目,发现了另一个基于它的项目php-zxing
然后,看了一下介绍,它的核心就是这个zxing项目,只是加了PHP包装器,不是像php-qrcode-detector-decoder这个项目是移植的,感觉识别率应该会高一些。
给服务器装了个Java,设置了一下JAVA的环境变量。然后把项目扔到了网站上,运行了一下它的例子,结果爆炸。。。。

java path设置有问题?不应该啊,设置完Java环境变量不就不用设置路径了么。
由于没有关注前面两个警告,只关注了这个问题,到网上搜了一圈一没看到解决方案。没办法,看看源码吧,发现,这个提示是在prepareSingleImage方法没有返回值的情况下才会出现,然后进入了方法,看到了exec方法,查了一下是执行一个外部程序,猜测应该是执行Zxing核心包。然后,我注意了一下代码行数和之前报错中警告的行数好像一致,估计是没执行了。
仔细看看了那个警告,说是由于安全问题不可用。然后查了一下exec方法不执行原因,应该是php.ini中禁用exec方法。在宝塔->软件管理->PHP-7.3->设置->禁用函数,删除了这个禁用。

试着运行一下它的例子,ok!!!
好的,写个PHP,简单包装一下!!


namespace Zxing; require dirname(__FILE__) . DIRECTORY_SEPARATOR . "PHPZxing" . DIRECTORY_SEPARATOR . "PHPZxingBase.php"; require dirname(__FILE__) . DIRECTORY_SEPARATOR . "PHPZxing" . DIRECTORY_SEPARATOR . "PHPZxingInterface.php"; require dirname(__FILE__) . DIRECTORY_SEPARATOR . "PHPZxing" . DIRECTORY_SEPARATOR . "PHPZxingDecoder.php"; require dirname(__FILE__) . DIRECTORY_SEPARATOR . "PHPZxing" . DIRECTORY_SEPARATOR . "ZxingImage.php"; require dirname(__FILE__) . DIRECTORY_SEPARATOR . "PHPZxing" . DIRECTORY_SEPARATOR . "ZxingBarNotFound.php"; use PHPZxing\PHPZxingDecoder; class QrReader{ public function decode($image=null){ $config = array( 'try_harder'=> true ); $decoder = new PHPZxingDecoder($config); $data=$decoder->decode($image); return $data->getImageValue(); } }

把文件夹名src改成了Zxing扔到我的qr-code中的extend文件夹中,然后去qr-code/application/index/controller/index.php文件中改一改
之前

            $qrcode = new QrReader($path.$name);
            $url = $qrcode->text();

之后

            $qrcode = new QrReader(); 
            $url = $qrcode->decode($path.$name); 

去浏览器看看,解决了没,ok!!

好的,终于可以再迈向下一步了!!!
咦!!咋生成这么久,老是转圈圈啊!!打开F12发现

额,简单,拿eclipse,找找位置,发现在layui.js脚本中

我改
/tools/qr-code/public/make.html
ok,解决问题,下一步!!
嗯?生成好了咋没图片啊,我的图片呢?看看F12

额,老样子,eclipse全局搜索大法,还是在layui.js中

我改
$('#qr').attr("src ",'/tools/qr-code/public/static/images/qr/'+res.msg);
好的,下一步,生成完毕,扫码,支付宝成功,微信,等等我图片呢,这个扫出来咋没图片。。
额,废了一番功夫,我开个Fiddler,打开手机代理到,电脑的fiddler上,总算发现问题!!

应该还是目录的问题,寻找一下/api.html位置,发现在weixin.html,qq.html中都有

我改
/tools/qr-code/public/api.html?text={$pay_url}
ok,完美运行!!大功告成!!!
后记-----------------------------------------------

简单加工

不是很想要qq支付,要不改一下,改成自定义
说干就干
到layui中,删除下面一段代码
else if($('#ailpay').val()==''||$('#qq').val()==''||$('#wechat').val()==''){layer.msg('请先上传收款二维码在生成!',{icon:5});return false}
到index.php中,更改下面一段代码
之前:

 $this->assign([
                'pay_url' => $res['wechat'],
                'pay_name' => $res['name']
            ]);
            return $this->fetch('wx');
        } elseif (strpos($ua, 'AlipayClient')) {
            header('location: ' . $res['alipay']);
            exit();
        } elseif (strpos($ua, 'QQ/')) {
            $this->assign([
                'pay_url' => urlencode($res['qq']),
                'pay_name' => $res['name']
            ]);
            return $this->fetch('qq');

之后

    if(!empty($res['wechat'])){
            $this->assign([
                'pay_url' => $res['wechat'],
                'pay_name' => $res['name']
            ]);
            return $this->fetch('wx');}
          else
            abort(404,'不支持微信');
        } elseif (strpos($ua, 'AlipayClient')) {
            if(!empty($res['alipay'])){
            header('location: ' . $res['alipay']);
            exit();}else
                abort(404,'不支持支付宝');

        } elseif (strpos($ua, 'QQ/')) {
            if(!empty($res['qq'])){
            $this->assign([
                'pay_url' => urlencode($res['qq']),
                'pay_name' => $res['name']
            ]);
            return $this->fetch('qq');
            }else
                abort(404,'不支持QQ');

你能得到多少,往往取决于你能知道多少