zoukankan      html  css  js  c++  java
  • PHP微信公众号支付,JSAPI支付方法,ThinkPHP5+微信支付

    JSAPI支付:是指在微信内置浏览器内调用微信支付模块支付,比如可用于微信公众号内的微信商城之类的。

    首先得在微信公众号的公众号设置里,把微信支付的授权目录填上

     然后你还得在商户号里,开通微信JSAPI支付的功能

    然后这里的授权目录也得填上

    然后按照微信文档的时序图,大概分3步

    项目结构:

     

    配置文件:

     Jsapi.php代码:

    <?php
    
    namespace appindexcontroller;
    
    use appcommoncontrollerHomeBase;
    use toolsWxPay;
    use thinkConfig;
    use phpqrcodeApiQrcode;
    use thinkRequest;
    use thinkCache;
    use toolsRetJosn;
    
    /**
     * JSAPI支付DEMO
     * Class Index
     * @package appindexcontroller
     */
    class Jsapi extends HomeBase {
    
        /**
         * 首页
         */
        public function index() {
            $redirect_uri = urlencode('http://wxpay.ngrok2.xiaomiqiu.cn'.url('Jsapi/index2'));
            $url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid='.Config::get('wx_pay')['appid'].'&redirect_uri='.$redirect_uri.'&response_type=code&scope=snsapi_base&state=123#wechat_redirect';
            $this->redirect($url);
        }
    
    
        /**
         * 授权后跳转到此
         */
        public function index2(Request $request){
            $arr = $request->get();
            $code = $arr['code'];
            $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid='.Config::get('wx_pay')['appid'].'&secret='.Config::get('wx_pay')['secret'].'&code='.$code.'&grant_type=authorization_code';
            $ret_json = file_get_contents("$url");
            $ret_arr = json_decode($ret_json,true);
            $openid = $ret_arr['openid'];
            $this->assign('openid',$openid);
            return $this->fetch();
        }
    
    
        /**
         * 下单获取支付参数
         * @param Request $request
         * @param WxPay $wxpay
         * @return 	hink
    esponseJson
         */
        public function getUrl(Request $request,WxPay $wxpay) {
            $pid = $request->get('id');
            $openid = $request->get('openid');
            //调用统一下单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' => 'JSAPI',
                'product_id' => $pid,
                'openid' => $openid
            ];
            $arr = $wxpay->unifiedorder($params);
            $wxpay->logs('logs.txt',$arr);
    
            if (isset($arr['prepay_id'])) {
                //重新签名
                $data = [
                    'appId' => $arr['appid'],
                    'timeStamp' => time(),
                    'nonceStr' => md5(time()),
                    'package' => 'prepay_id='.$arr['prepay_id'],
                    'signType' => 'MD5'
                ];
                $data = $wxpay->setSign($data);
                $data['paySign'] = $data['sign'];
                unset($data['sign']);
                Cache::set('send' . $params['out_trade_no'], $params['total_fee'], 3600);
                return RetJosn::successJson('OK',$data);
            } else {
                return RetJosn::errorJson('err');
            }
        }
    
        /**
         * 接收腾讯推送支付通知
         * @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));
            }
        }
    }

    微信支付类:

    <?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;
            }
        }
    }

    前端页面:

    <!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" />
        </head>
        <body>
            <h1 id="openid"></h1>
            <div class="btn-box">
                <button onclick="getUrl()">下单</button>
            </div>
            <div id="info">
    
            </div>
        </body>
        <script src="__STATIC__/default/js/jquery-1.9.1.min.js"></script>
        <script>
            var openid = '';
            $(function () {
                localStorage.getItem('openid');
                if (localStorage.getItem('openid')) {
                    openid = localStorage.getItem('openid');
                } else {
                    openid = '{$openid}';
                    localStorage.setItem('opendi',openid);
                }
                $('#openid').html(openid);
            });
    
            /*验证码生成*/
            function getUrl() {
                var id = getId(10);
                $.ajax({
                    url:"{:url('Jsapi/getUrl')}?id=" + id + '&openid=' + openid,
                    dataType:'json',
                    success:function(res) {
                        if (res.code == 200) {
                            alert('1'+JSON.stringify(res));
                            var data = res.data;
                            wakeup(res.data);
                            checkSuccess(id);
                        }
                    }
                });
            }
    
            function wakeup(data){
                WeixinJSBridge.invoke(
                    'getBrandWCPayRequest', data,
                    function(res){
                        if(res.err_msg == "get_brand_wcpay_request:ok" ){
                            // 使用以上方式判断前端返回,微信团队郑重提示:
                            //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                            alert('支付成功');
                        }
                    });
            }
    
            
            /*生成唯一Id*/
            function getId(length){
                var tmp = Date.parse( new Date() ).toString();
                tmp = tmp.substr(0,length);
                return tmp;
            }
            
            /*轮询支付状态*/
            function checkSuccess(id){
                var interval = window.setInterval(function(){
                    $.ajax({
                       url:"{:url('Jsapi/checkSuccess')}?id=" + id,
                       dataType:'json',
                       success:function(res) {
                           if (res.code == 200) {
                               $('#info').html('订单号:'+id+','+res.msg);
                               clearInterval(interval);
                           }
                       }
                    })
                },2000)
            }
        </script>
    </html>

    效果演示:

     

     

  • 相关阅读:
    c# out ref parames的用法
    c#测试执行时间的方法
    c#文件的操作
    c#md5加密的简单用法
    notepad++加到右键
    mysql自动安装脚本
    Arrays.sort实现原理
    选择排序
    自带排序 Array.sort()
    vi中使用鼠标右键插入时进入(insert)visual模式
  • 原文地址:https://www.cnblogs.com/wordblog/p/12550359.html
Copyright © 2011-2022 走看看