PHP微信扫码支付DEMO,thinkphp5+微信支付

PS:微信支付基础条件

1.一个认证的服务号

2.在服务号后台申请微信支付,成功后获得一个商户号

3.在商户号里面选择native支付,这个就是扫码支付,按照提示开通这项。

PHP微信扫码支付DEMO,thinkphp5+微信支付

 

4.按照微信支付文档的说法,扫码支付分两种模式

模式一:生成的二维码没有失效性,但是操作比较复杂,中间步骤比较多,容易出现很多问题。

模式二:生成的二维码有两小时时间限制,但是我感觉也十分够用了,这个步骤比较简单,很容易实现。

按照时序图,大概也就分两步

PHP微信扫码支付DEMO,thinkphp5+微信支付

 

代码结构:

PHP微信扫码支付DEMO,thinkphp5+微信支付

 

 

 

配置文件:

PHP微信扫码支付DEMO,thinkphp5+微信支付

 

 微信支付类:

<?php

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

namespace tools;
use think\Config;
/**
 * Description of WxPay
 *
 * @author admin
 */
class WxPay {
    
    /**
     * 获取签名
     * @param type $arr
     * @return type
     */
    public function getSign($arr)
    { 
        //去除数组的空值
        array_filter($arr);
        if(isset($arr[sign])){
            unset($arr[sign]);
        }
        //排序
        ksort($arr);
        //组装字符
        $str = $this->arrToUrl($arr) . &key= . Config::get(wx_pay)[key];
        //使用md5 加密 转换成大写 
       return strtoupper(md5($str));
    }
    
    /**
     * 校验签名
     * @param type $arr
     * @return boolean
     */
    public function checkSign($arr){        
        //生成新签名
        $sign = $this->getSign($arr);
        //和数组中原始签名比较
        if($sign == $arr[sign]){
            return true;
        }else{
            return false;
        }
    }
    
    /**
     * 获取带签名的数组
     * @param array $arr
     * @return type
     */
    public function setSign($arr)
    {
        $arr[sign] = $this->getSign($arr);
        return $arr;
    }
    
    /**
     * 数组转URL字符串 不带key
     * @param type $arr
     * @return type
     */
    public function arrToUrl($arr)
    {
        return urldecode(http_build_query($arr));
    }
    
    /**
     * 记录到文件
     * @param type $file
     * @param type $data
     */
    public function logs($file,$data)
    {
        $data = is_array($data) ? print_r($data,true) : $data;
        file_put_contents(./public/paylogs/ .$file, $data);
    }
    /**
     * 接收POST推送
     * @return type
     */
    public function getPost()
    {
        return file_get_contents(php://input);
    }
    
    /**
     * Xml 文件转数组
     * @param type $xml
     * @return string
     */
    public function XmlToArr($xml)
    {    
        if($xml == ‘‘) return ‘‘;
        libxml_disable_entity_loader(true);
        $arr = json_decode(json_encode(simplexml_load_string($xml, SimpleXMLElement, LIBXML_NOCDATA)), true);        
        return $arr;
    }
    
    /**
     * 数组转XML
     * @param type $arr
     * @return string
     */
    public function ArrToXml($arr)
    {
        if(!is_array($arr) || count($arr) == 0) return ‘‘;

        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
                if (is_numeric($val)){
                        $xml.="<".$key.">".$val."</".$key.">";
                }else{
                        $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
                }
        }
        $xml.="</xml>";
        return $xml; 
    }
    
    /**
     * 发送POST请求
     * @param type $url
     * @param type $postfields
     * @return type
     */
    public function postStr($url,$postfields)
    {
        $ch = curl_init();
        $params[CURLOPT_URL] = $url;    //请求url地址
        $params[CURLOPT_HEADER] = false; //是否返回响应头信息
        $params[CURLOPT_RETURNTRANSFER] = true; //是否将结果返回
        $params[CURLOPT_FOLLOWLOCATION] = true; //是否重定向
        $params[CURLOPT_POST] = true;
        $params[CURLOPT_SSL_VERIFYPEER] = false;//禁用证书校验
        $params[CURLOPT_SSL_VERIFYHOST] = false;
        $params[CURLOPT_POSTFIELDS] = $postfields;
        curl_setopt_array($ch, $params); //传入curl参数
        $content = curl_exec($ch); //执行
        curl_close($ch); //关闭连接
        return $content;
    }
    
    /**
     * 统一下单
     * @param type $params
     * @return boolean
     */
    public function unifiedorder($params)
    {
        //获取到带签名的数组
        $params = $this->setSign($params);
        //数组转xml
        $xml = $this->ArrToXml($params);
        //发送数据到统一下单API地址
        $data = $this->postStr(Config::get(wx_pay)[uourl], $xml);
        $arr = $this->XmlToArr($data);
        if($arr[result_code] == SUCCESS && $arr[return_code] == SUCCESS){
            return $arr;
        }else{
            $this->logs(error.txt, $data);
            return false;
        }
    }
}

 

index控制器演示代码

 

<?php

namespace app\index\controller;

use app\common\controller\HomeBase;
use tools\WxPay;
use think\Config;
use phpqrcode\ApiQrcode;
use think\Request;
use think\Cache;
use tools\RetJosn;

class Index extends HomeBase {

    /**
     * 首页
     * @param WxPay $wxpay
     * @return mixed
     */
    public function index(WxPay $wxpay) {
        //$wxpay->logs(‘log.txt‘,[‘213123‘,‘123123123‘]);
        echo index;
    }

    /**
     * 扫码支付-模式二
     * @param WxPay $wxpay
     */
    public function index2(Request $request, WxPay $wxpay) {
        if ($request->isGet()) {
            return $this->fetch();
        }
    }

    /**
     * 统一下单,生成二维码
     */
    public function getQrUrl(Request $request) {
        $pid = $request->get(id);
        //调用统一下单API
        $params = [
            appid => Config::get(wx_pay)[appid],
            mch_id => Config::get(wx_pay)[mchid],
            nonce_str => md5(time()),
            body => 订单号:.$pid,
            out_trade_no => $pid,
            total_fee => 2,
            spbill_create_ip => $_SERVER[SERVER_ADDR],
            notify_url => Config::get(wx_pay)[notify],
            trade_type => NATIVE,
            product_id => $pid
        ];
        $wxpay = new WxPay();
        $arr = $wxpay->unifiedorder($params);
        if (!empty($arr[code_url])) {
            Cache::set(send . $params[out_trade_no], $params[total_fee], 3600);
            ApiQrcode::png($arr[code_url]);
        } else {
            return 下单失败!;
        }
    }

    /**
     * 接收腾讯推送支付通知
     * @param WxPay $wxpay
     */
    public function backOrder(WxPay $wxpay) {
        try {
            // 获取腾讯传回来的通知数据
            $xml = $wxpay->getPost();
            // 将XML格式的数据转换为数组
            $arr = $wxpay->XmlToArr($xml);
            $wxpay->logs(logs.txt, 1);
            // 验证签名
            if ($wxpay->checkSign($arr)) {
                Cache::set(back.$arr[out_trade_no],$arr[total_fee],3600);
            }
            $wxpay->logs(logs.txt, 2);
            $params = [
                return_code => SUCCESS,
                return_msg => OK
            ];
            echo $wxpay->ArrToXml($params);
        } catch (\Exception $e) {
            $wxpay->logs(logs.txt, $e->getMessage());
            exit();
        }
    }

    /**
     * 查询支付状态
     * @param Request $request
     * @return type
     */
    public function checkSuccess(Request $request){
        $pid = $request->get(id);
        if (Cache::get(send.$pid) == Cache::get(back.$pid)) {
            return RetJosn::successJson(支付成功);
        } else {
            return RetJosn::errorJson(Cache::get(send.$pid).|.Cache::get(back.$pid));
        }
    }
}

 二维码页面代码:

<!DOCTYPE html>
<!--
To change this license header, choose License Headers in Project Properties.
To change this template file, choose Tools | Templates
and open the template in the editor.
-->
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <div class="btn-box">
            <button onclick="getQrcode()">刷新二维码</button>
        </div>
        <div class="qrcode-box">
            <img id="qrimg" src="">
        </div>
        <div class="status-box">
            <h2 id="status"></h2>
        </div>
    </body>
    <script src="__STATIC__/default/js/jquery-1.9.1.min.js"></script>
    <script>
        (function () {
            getQrcode();//二维码生成
        })();

        /*验证码生成*/
        function getQrcode() {
            var id = getId(10);
            var url = "{:url(‘Index/getQrUrl‘)}" + "?id=" + id;
            $("#qrimg").attr("src", url);
            sessionStorage.setItem(id,id);
            checkSuccess();//轮询支付状态
        }
        
        /*生成唯一Id*/
        function getId(length){
            var tmp = Date.parse( new Date() ).toString();
            tmp = tmp.substr(0,length);
            return tmp;
        }
        
        /*轮询支付状态*/
        function checkSuccess(){
            var interval = window.setInterval(function(){
                var id = sessionStorage.getItem(id);
                $.ajax({
                   url:"{:url(‘Index/checkSuccess‘)}?id=" + id,
                   dataType:json,
                   success:function(res) {
                       if (res.code == 200) {
                           $(#status).html(订单号:+id+,+res.msg);
                clearInterval(interval); } } }) },
2000) } </script> </html>

 效果演示:

生成二维码,并开始启动轮询

PHP微信扫码支付DEMO,thinkphp5+微信支付

手机上支付订单:

PHP微信扫码支付DEMO,thinkphp5+微信支付

 

轮询到成功支付的状态:

 

 

PHP微信扫码支付DEMO,thinkphp5+微信支付

 

PHP微信扫码支付DEMO,thinkphp5+微信支付

上一篇:Hive分区、分桶操作及其比较(转自:http://blog.csdn.net/epitomizelu/article/details/41911657)


下一篇:Educational Codeforces Round 116 (Rated for Div. 2) E. Arena