zoukankan      html  css  js  c++  java
  • PHP微信扫码支付DEMO,thinkphp5+微信支付

    PS:微信支付基础条件

    1.一个认证的服务号

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

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

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

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

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

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

    代码结构:

    配置文件:

     微信支付类:

    <?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 thinkConfig;
    /**
     * 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 appindexcontroller;
    
    use appcommoncontrollerHomeBase;
    use toolsWxPay;
    use thinkConfig;
    use phpqrcodeApiQrcode;
    use thinkRequest;
    use thinkCache;
    use toolsRetJosn;
    
    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>

     效果演示:

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

    手机上支付订单:

    轮询到成功支付的状态:

  • 相关阅读:
    SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
    简单的使用SLF4J门面和log4j
    详细的Log4j使用教程
    java的Mybatis动态代理方式(二)
    java的一个基础的Mybatis例子(一)
    java的注解学习
    ArrayList输出的几种方法
    自己写的一个java链接数据库的类
    ipv4的TCP的几个状态 (SYN, FIN, ACK, PSH, RST, URG)
    tomcat的CATALINA_HOME环境变量可以不用设置
  • 原文地址:https://www.cnblogs.com/wordblog/p/12540590.html
Copyright © 2011-2022 走看看