月度归档: 2017 年 4 月

  • nginx使用 logrotate 切割日志 图文教程

    nginx使用 logrotate 切割日志 图文教程

    大体上有三种方法切割nginx日志。

    第一种是写个定时任务,每天零点把旧的日志重命名,并对nginx进程发送USR1信号使其重新打开日志并写入。

    第二种是能过管道的方式把新产生的日志写到另外一个日志文件里。

    第三种是能过logrotate来切割日志,logrotate是系统自带的服务,可以切割任何日志,不仅仅是nginx,这里推荐使用它。

    下面我们详细讲一下Logrotate切割Nginx日志

    [title]步骤1:[/title]

    vi /etc/logrotate.d/nginx

    文件内容如下

    /usr/local/nginx/logs/access.log {   
    daily 
    rotate 7  
    missingok 
    notifempty 
    dateext 
    sharedscripts 
    postrotate 
        if [ -f /usr/local/nginx/logs/nginx.pid ]; then
            kill -USR1 `cat /usr/local/nginx/logs/nginx.pid` 
        fi
    endscript 
    }

    [title]步骤2:测试是否可用[/title]

    /usr/sbin/logrotate -f /etc/logrotate.d/nginx

    [title]步骤3:配置定时任务[/title]

    59 23 * * * /usr/sbin/logrotate -f /etc/logrotate.d/nginx

    完成!

  • 新站上线

    最近我们弄了一个新的项目,做了一些简单的工具。

    关键词:随机生成密码、MD5加密、SHA1加密、搜索引擎快照检测

    php 生成随机 字符串 密码的方法 图文教程

    搜索引擎快照检测

    discuz快照被劫持的解决办法

    我们正常用浏览器打开网站是正常的,一些高级的挂马只针对搜索引擎,如下图所示:

  • 【高危漏洞预警】Window系统SMB和RDP多个高危漏洞

    【高危漏洞预警】Window系统SMB和RDP多个高危漏洞

    2017年4月14日,国外黑客组织Shadow Brokers发出了NSA方程式组织的机密文档,包含了多个Windows远程漏洞利用工具,该工具包可以可以覆盖全球70%的Windows服务器,可以导致服务器被入侵,请您关注:
    [title]一.影响范围:[/title]

    已知受影响的Windows版本包括但不限于:

    Windows NT、Windows 2000、Windows XP、Windows 2003、Windows Vista、Windows 7、Windows 8,Windows 2008、Windows 2008?R2、Windows Server 2012 SP0;

     

    [title]二.排查方式[/title]

    本次曝出的工具利用SMB服务和RDP服务远程入侵,需要确认服务器是否对外开启了137、139、445、3389端口。

    排查方式如下:

    外网计算机上telnet 目标地址445,例如:telnet 【IP】 445

     

    [title]三.缓解措施[/title]

    微软已经发出通告?,强烈建议您更新最新补丁;

    ? ? ?工具名称? ? ? 解决措施
    “EternalBlue”Addressed by?MS17-010
    “EmeraldThread”Addressed by?MS10-061
    “EternalChampion”Addressed by?CVE-2017-0146?&?CVE-2017-0147
    “ErraticGopher”Addressed prior to the release of Windows Vista
    “EsikmoRoll”Addressed by?MS14-068
    “EternalRomance”Addressed by?MS17-010
    “EducatedScholar”Addressed by?MS09-050
    “EternalSynergy”Addressed by?MS17-010
    “EclipsedWing”Addressed by?MS08-067
  • YII2 安装教程 数据库配置前后台 初始化 图文实例

    YII2 安装教程 数据库配置前后台 初始化 图文实例

    [title]1.首先下载yii[/title]

    composer create-project yiisoft/yii2-app-advanced advanced 2.0.11

    我本地服务用的是Apache

     

    [title]2.解压到E:\wamp\www\yii2目录下面将目录advanced下所有文件剪切到 E:\wamp\www\yii2[/title]

     

     

    [title]3.打开cmd ,切换到当前目录,运行init.bat 文件[/title]

     

     

    [title]4.现在可以打开浏览器:输入http://localhost:8080/yii2/[/title]

     

    [title]5.点击frontend》web》,查看表示安装成功[/title]

     
    [title]6.配置数据库[/title]

    首先在当前目录下找到common>config>main-local.php

    打开main-local.php 进行修改,将yii2advanced更改为你自己的数据库,我的是新建的yii2;

     

     

    [title]7.打开cmd 切换到根目录E:\wamp\www\yii2,输入:yii.bat migrate(意思是创建一个数据表为migrate),其中默认会创建一张user表[/title]

     

     

    [title]8.我们可以切换到后台界面看看输入Url:http://localhost/yii2/backend/web[/title]

     

     

    [title]9.怎么登录或注册呢?好吧,先打开前台点击Signup,进行注册,如demo[/title]

     

    [title]10.打开数据库user表进行查看[/title]

    到此安装完成了,大家可以登录前台后台了

  • php curl函数采集网页出现gzip压缩、编码不同导致的乱码 图文解决方法

    php curl函数采集网页出现gzip压缩、编码不同导致的乱码 图文解决方法

    curl乱码有两个方面的原因一个是因为我们采集页面的php页面编码与远程文档编码不致导致的,另一种可能是页面进入了gzip压缩传输导致的,那么我们要如何解决这些问题呢?

     

    [title]gzip压缩传输导致乱码[/title]

    今天在采集京东的时候发现返回的数据是乱码,网上说可能和压缩有关,看了一下京东的头信息的确进行gzip加密,好吧,那就解压吧

    $return = gzdecode($return); //将return的字符进行解码
    

    另一种解决办法

    curl_setopt($ch, CURLOPT_ENCODING, 'gzip');

    [title]页面编码问题[/title]

    mb_convert_encoding($str, 'utf-8', 'GBK,UTF-8,ASCII');
    

    [title]完整代码示例[/title]

    // curl 伪造agent抓取页面
    function http_get($URL,$key='baidu') { 
        $agent = Flight::get('flight.spider_agent')[$key];
        if( !$agent ){
            return false;
        }
        $c = curl_init(); 
        curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($c, CURLOPT_USERAGENT, $agent);
        curl_setopt($c, CURLOPT_HTTPHEADER,array('Accept-Encoding: gzip, deflate'));
        curl_setopt($c, CURLOPT_ENCODING, 'gzip,deflate');//这个是解释gzip内容.................
        curl_setopt($c, CURLOPT_URL, $URL); 
        curl_setopt($c, CURLOPT_TIMEOUT,2);
        $contents = curl_exec($c); 
        $contents = mb_convert_encoding($contents, 'utf-8', 'GBK,UTF-8,ASCII');
        $httpCode = curl_getinfo($c,CURLINFO_HTTP_CODE); 
        curl_close($c);
        return ['data'=>$contents,'http_code'=>$httpCode];
    }

     

  • discuz如何发现快照被劫持?该如何解决图文教程

    discuz如何发现快照被劫持?该如何解决图文教程

    前2天遇到两例discuz网站百度快照被劫持的例子,一般快照被劫持很难被发现。

    被劫持的表现

    site:域名

    在搜索引擎中输入自己的网址,然后选择时间:【一月内】。出现被收录的帖子链接,点击后就跳转到博彩网站等。

    解决方案

    进discuz后台找到工具-文件校验,看下最近有哪些php文件被修改了。

    1、排查网站源文件的php 文件,查看是否有出现IP地址、或者异常网站(上面跳转的网站),如果有删除就可以了。

    2、排查网站源文件的php 文件,查看是否有如下代码:

    @include($_SERVER['DOCUMENT_ROOT']

    如果有就删除此段代码。并把该段代码进行解密。

    举例:

    @include($_SERVER['DOCUMENT_ROOT'].PACK('H*','2F646174612F6176617461722F30'));

    需要解密的代码为:2F646174612F6176617461722F30

    解密后为:/data/avatar/0

      意思就是在上述路径下存在被非法上传的文件,用于做快照劫持。

    解决办法:删除上面的代码和文件即可。

    挂马问题解决了后,记得更改服务器相关的各种密码,尽量设置的复杂一点。

    一般这种情况说明网站代码已被修改,下面提供一个思路,查找关键词pack

    find /目录 -name '*.php' | xargs grep -i 'PACK('
    find /目录 -name '*.html' | xargs grep -i 'PACK('
    find /目录 -name '*.js' | xargs grep -i 'PACK('

    以上仅是思路,具体还要根据代码分析。

  • ThinkPHP 整合微信支付 扫码支付 模式二 图文教程

    ThinkPHP 整合微信支付 扫码支付 模式二 图文教程

    前面我们介绍了支付的在线支付实例 ThinkPHP实现支付宝接口功能 代码实例

    这篇文章主要介绍扫码支付场景二。

    目前有两种模式,模式一比模式二稍微复杂点,至于模式一与模式二的具体内容,流程,微信开发文档都有详细介绍,这里就不多说废话,接下来赶紧上教程!

    下载SDK类文件

    Wxpay Library

    并SDK文件/lib下的几个文件放置到/ThinkPHP/Library/Vendor/

    修改WxPay.pub.config.php中的配置选项

    	//=======【基本信息设置】=====================================
    	//微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看
    	const APPID = '公众号表示ID';
    	//受理商ID,身份标识
    	const MCHID = '商户ID';
    	//商户支付密钥Key。审核通过后,在微信发送的邮件中查看
    	const KEY = 'KEY密匙';
    	//JSAPI接口中获取openid,审核后在公众平台开启开发模式后可查看
    	const APPSECRET = 'SECRET密匙';
    	
    	//=======【JSAPI路径设置】===================================
    	//获取access_token过程中的跳转uri,通过跳转将code传入jsapi支付页面
    	const JS_API_CALL_URL = 'http://www.laoji.org';
    	
    	//=======【证书路径设置】=====================================
    	//证书路径,注意应该填写绝对路径
    	const SSLCERT_PATH =  "__ROOT__\paypem\\apiclient_cert.pem";
    	const SSLKEY_PATH =   "__ROOT__\paypem\\apiclient_key.pem";
    	
    	//=======【异步通知url设置】===================================
    	//异步通知url,商户根据实际开发过程设定
    	const NOTIFY_URL = 'http://www.laoji.org/wxpay/notify';
    	//=======【curl超时设置】===================================
    	//本例程通过curl使用HTTP POST方法,此处可修改其超时时间,默认为30秒
    	const CURL_TIMEOUT = 30;
    

    新建微信的Controller方法,目录位置为:\Application\Home\Controller\WxpayController.class.php

    <?php
    /**
     * WxpayController.class.php
     * 微信支付控制器
     * 网站地址:   https://www.jiloc.com/43212.html
     * Author: 老季 ( admin@laoji.org ) 
     * Date: 2017年4月18日
    */
    namespace Home\Controller;
    class WxpayController extends HomeController {
        //初始化
        public function _initialize()
        {
            //引入类文件
            vendor('Wxpay.WxPayPubHelper');
        }
        
        private function makeOrder($ary){
            $data = array();
            $data['userid'] = session('user_auth')['uid'];
            if ( !$data['userid'] ){
                $this->error('用户ID错误! -> '.$data['uid'],'/my/publishlist');
            } 
            // 检查船舶信息
            $data['shipinfoid'] = $ary['id'];
            $ShipInfo = M('ShipInfo')->where(array('id'=>$ary['id']))->find();
            if( !$ShipInfo ){
                $this->error('信息ID错误! -> '.$ary['id'],'/my/publishlist');
            }
            // 检查支付价格
            $data['ordercatid'] = $ary['catid'];
            $OrderCat = M('OrderCat')->where(array('id'=>$ary['catid']))->find();
            if( !$OrderCat ){
                $this->error('产品价格错误! -> '.$ary['catid'],'/my/publishlist');
            }
            
            $data['ordid'] = "LJ".$ary['TYPE'].date('YmdHis',time()).rand(11,99);
            $data['ordtime'] = time();
            
            $data['ordtitle'] = $ShipInfo['title'];
            $data['ordbuydays'] = $OrderCat['days'];
            $data['ordfee'] = $data['ordprice'] = $OrderCat['price'];
            $data['status'] = 0;
            
            M('OrderList')->add($data);
            
            return $data['ordid'];
            
        }
        
        //生成二维码
        public function qrcode()
        {
            // 生成订单
            $_POST['TYPE'] = 'W';
            $out_trade_no = $this->makeOrder($_POST);
            $ordtotal_fee = str_replace(".","",$_POST['ordtotal_fee']*100);
            //使用统一支付接口
            $unifiedOrder = new \UnifiedOrder_pub();
            //设置统一支付接口参数
            //设置必填参数
            $unifiedOrder->setParameter("body",$_POST['ordsubject'] );//商品描述
            //自定义订单号,此处仅作举例
            $timeStamp = time();
            $unifiedOrder->setParameter("out_trade_no",$out_trade_no);//商户订单号 
            $unifiedOrder->setParameter("total_fee",$ordtotal_fee);//总金额
            $unifiedOrder->setParameter("notify_url", \WxPayConf_pub::NOTIFY_URL);//通知地址 
            $unifiedOrder->setParameter("trade_type","NATIVE");//交易类型
            //非必填参数,商户可根据实际情况选填
            //$unifiedOrder->setParameter("sub_mch_id","XXXX");//子商户号  
            //$unifiedOrder->setParameter("device_info","XXXX");//设备号 
            //$unifiedOrder->setParameter("attach","XXXX");//附加数据 
            //$unifiedOrder->setParameter("time_start","XXXX");//交易起始时间
            //$unifiedOrder->setParameter("time_expire","XXXX");//交易结束时间 
            //$unifiedOrder->setParameter("goods_tag","");//商品标记 
            //$unifiedOrder->setParameter("openid","19405");//用户标识
            //$unifiedOrder->setParameter("product_id","XXXX");//商品ID
            //获取统一支付接口结果
            $unifiedOrderResult = $unifiedOrder->getResult();
            //var_dump($unifiedOrder);
            //商户根据实际情况设置相应的处理流程
            if ($unifiedOrderResult["return_code"] == "FAIL") 
            {
                //商户自行增加处理流程
                echo "通信出错:".$unifiedOrderResult['return_msg']."<br>";
            }
            elseif($unifiedOrderResult["result_code"] == "FAIL")
            {
                //商户自行增加处理流程
                echo "错误代码:".$unifiedOrderResult['err_code']."<br>";
                echo "错误代码描述:".$unifiedOrderResult['err_code_des']."<br>";
            }
            elseif($unifiedOrderResult["code_url"] != NULL)
            {
                //从统一支付接口获取到code_url
                $code_url = $unifiedOrderResult["code_url"];
                //商户自行增加处理流程
                //......
            }
            $this->assign('title',$_POST['ordsubject']);
            $this->assign('total_fee',$_POST['ordtotal_fee']);
            $this->assign('out_trade_no',$out_trade_no);
            $this->assign('code_url',urlencode($code_url));
            $this->assign('unifiedOrderResult',$unifiedOrderResult);
            
            $this->display();
        }
        
        public function notify()
        {
            //使用通用通知接口
            $notify = new \Notify_pub();
            //存储微信的回调
            $xml = $GLOBALS['HTTP_RAW_POST_DATA'];
            $notify->saveData($xml);
            //var_dump($xml);
            //验证签名,并回应微信。
            //对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,
            //微信会通过一定的策略(如30分钟共8次)定期重新发起通知,
            //尽可能提高通知的成功率,但微信不保证通知最终能成功。
            if($notify->checkSign() == FALSE){
                $notify->setReturnParameter("return_code","FAIL");//返回状态码
                $notify->setReturnParameter("return_msg","签名失败");//返回信息
            }else{
                $notify->setReturnParameter("return_code","SUCCESS");//设置返回码
            }
            $returnXml = $notify->returnXml();
            echo $returnXml;
             
            //==商户根据实际情况设置相应的处理流程,此处仅作举例=======
             
            //以log文件形式记录回调信息
            //         $log_ = new Log_();
            //$log_name= __ROOT__."/Public/notify_url.log";//log文件路径
             
            //$this->log_result($log_name,"【接收到的notify通知】:\n".$xml."\n");
             
            if($notify->checkSign() == TRUE)
            {
                if ($notify->data["return_code"] == "FAIL") {
                    //此处应该更新一下订单状态,商户自行增删操作
                    log_result($log_name,"【通信出错】:\n".$xml."\n");
                    $this->error("通信出错");
                }
                elseif($notify->data["result_code"] == "FAIL"){
                    //此处应该更新一下订单状态,商户自行增删操作
                    //log_result($log_name,"【业务出错】:\n".$xml."\n");
                    $this->error("业务出错");
                }
                elseif( $notify->data['return_code'] == "SUCCESS" && $notify->data['result_code'] == "SUCCESS"){
                    $out_trade_no   = $notify->data['out_trade_no'];
                    $parameter = array(
                        "out_trade_no"     => $notify->data['out_trade_no'],    //商户订单编号;
                        "transaction_id"         => $notify->data['transaction_id'],  //微信支付订单号	
                        "total_fee"        => $notify->data['total_fee'],       //交易金额;
                        "result_code"     => "SUCCESS",                        //交易状态
                        "time_end"    => $notify->data['time_end'],          //支付完成时间
                        "openid"    => $notify->data['openid'],            //用户标识	
                    );
                    if (!$this->checkorderstatus($out_trade_no)){
                        $this->orderhandle($parameter);
                    }
                    return true;
                    //$this->success("支付成功!");
                }
                else{
                    //此处应该更新一下订单状态,商户自行增删操作
                    //log_result($log_name,"【支付成功】:\n".$xml."\n");
                    $this->error("UNKNOW ERROR");
                    
                }
                 
                //商户自行增加处理流程,
                //例如:更新订单状态
                //例如:数据库操作
                //例如:推送支付完成信息
            }
        }
        
        //查询订单
        public function orderQuery()
        {  
            if($this->checkorderstatus( I('post.out_trade_no') )){
                $this->ajaxReturn("SUCCESS");
            };
            $this->ajaxReturn("FAILD");
        }
        
        //在线交易订单支付处理函数
        //函数功能:根据支付接口传回的数据判断该订单是否已经支付成功;
        //返回值:如果订单已经成功支付,返回true,否则返回false;
        private function checkorderstatus($ordid){
            $ordstatus=M('OrderList')->where(array('ordid'=>$ordid))->getField('ordstatus');
            if($ordstatus==1){
                return true;
            }else{
                return false;    
            }
        }
        //处理订单函数
        //更新订单状态,写入订单支付后返回的数据
        private function orderhandle($parameter){
            $ordid=$parameter['out_trade_no'];
            $data['payment_trade_no']      =$parameter['transaction_id'];
            $data['payment_trade_status']  =$parameter['result_code'];
            $data['payment_notify_time']   =$parameter['time_end'];
            $data['payment_buyer_email']   =$parameter['openid'];
            $data['ordstatus']             =1;
            M('OrderList')->where(array('ordid'=>$ordid))->save($data);
            
            $Order = M('OrderList')->where(array('ordid'=>$ordid))->find();
            
            $ship_due_date = M("ShipInfo")->where(array('id'=>$Order['shipinfoid']))->find();
            if( $ship_due_date['due_date'] > time() ){
                $due_date = $ship_due_date['due_date']+($Order['ordbuydays']*86400);
            }else{
                $due_date = strtotime("+".$Order['ordbuydays']." day");
            }
            
            M('ShipInfo')->where(array('id'=>$Order['shipinfoid']))->save(array('due_date'=>$due_date,'status'=>1,'top'=>1));
            return ;
        } 
        
        
        
    }

    二维码生成页面的html,目录位置\Application\Home\View\default\Wxpay\qrcode.html

    <extend name="Base/common"/>
    
    <block name="body">
    <div class="center clearfix">
        <div class="bread">
        <a class="link" href="/">首页></a><span class="on">支付页面</span>
    	</div>
        <div class="pay_con_zl">
           
            <div class="p_tit_zl">支付页面</div>
          <div class="p_info_zl">
               <p>标题:{$title}</p>
               <p>支付金额:<span style="font-size:24px; font-weight:bold; color:#F00;">¥{$total_fee}元</span>,请使用微信扫描下方二维码完成支付<span style="padding-left:20px; color:#F00;">2小时之内,请尽快扫描支付</span></p>          
               <P><img alt="扫码支付" src="https://www.jiloc.com/wp-content/uploads/20170409040501NP4.jpg" style="width:150px;height:150px;"/></P>
            </div>
            
        </div>
    </div>
    <script language="JavaScript">
    function Check(){
        var out_trade_no = "{$out_trade_no}";
        $.post("{:U('wxpay/orderQuery')}", { out_trade_no: out_trade_no},
            function(data){
                if(data == "SUCCESS"){
                    window.location = '{:U("my/paystatus",array("ordtype"=>"payed"))}';
                }
        });
    }
    window.setInterval("Check()",3000); 
    </script>
    </block>

    表单提交以后则跳转到二维码生成页面,提交地址:/wxpay/qrcode ,效果如下图:

    微信客户端支付成功以后,该页面会自动跳转到支付成功页面。

    回调数据库表建立

    我们这里使用的是ThinkPHP实现支付宝接口功能 代码实例 中的order_list 表。

    微信平台配置

    微信公众平台上的配置了,这里我遇到过问题,如果有在这里遇到问题的同学请留言,比如出现了access_deined或者access_notallowed等问题,这都可能是因为这里配置不对。
    请看配置过程截图:

    支付授权目录设置为:http://laoji.org/wxpay/


    注意

    微信支付订单中的totla_fee的单位为分,比如1元,传值为100(分)

  • OneinStack PHP多版本共存图文教程

    OneinStack PHP多版本共存图文教程

    教程基于《OneinStack》一键PHP/JAVA安装工具中lnmp模式,很用户希望一个网站运行在php5.4,另一个网站运行在php7下,一台服务器上同时支持多个PHP版本,步骤如下:

    安装OneinStack

    选择lnmp模式,默认安装php5.4,安装步骤参考:http://oneinstack.com/install/

    选项如下:

    Install Nginx
    Do not install Apache
    Do not install Tomcat
    Install MySQL-5.6
    Install php-5.4
    ….

    安装php7

    service php-fpm stop #后面需要再安装php,需要停止php
    mv /etc/init.d/php-fpm{,_bk} #后面需要再安装php会覆盖,备份启动脚本

    默认php5.4安装路径是/usr/local/php,如果再次安装会提示php已经安装,因此必须修改options.conf的php安装目录,将php7安装路径设置为/usr/local/php7,修改/root/oneinstack/options.conf:

    php_install_dir=/usr/local/php7

    再次执行./install.sh,选择Install php-7,其余均选择n,等待ing

    修改php配置文件

    service php-fpm stop #停止php7启动脚本
    mv /etc/init.d/php-fpm /etc/init.d/php7-fpm  #重命名php7启动脚本
    mv /etc/init.d/php-fpm_bk /etc/init.d/php-fpm  #恢复php5.4启动脚本

    设置php5.4、php7开机自启动:

    # CentOS:

    chkconfig –add php7-fpm
    chkconfig –add php-fpm
    chkconfig php7-fpm on
    chkconfig php-fpm on

    # Ubuntu/Debian:

    update-rc.d php7-fpm defaults
    update-rc.d php-fpm defaults

    防止php5.4、php7监听sock冲突,修改php7的listen,更改配置文件/usr/local/php7/etc/php-fpm.conf:

    listen = /dev/shm/php-cgi.sock

    #改成

    listen = /dev/shm/php7-cgi.sock

    手工启动php5.4、php7:

    service php-fpm start  #启动php5.4
    service php7-fpm start #启动php7

    如下图:

    修改nginx虚拟主机配置文件

    ./vhost.sh绑定域名,默认是运行在php5.4,如需要将网站运行在php7下,需要修改/usr/local/nginx/conf/vhost/www.oneinstack.com.conf(www.oneinstack.com改成自己绑定域名):

    fastcgi_pass unix:/dev/shm/php-cgi.sock;

    #改成

    fastcgi_pass unix:/dev/shm/php7-cgi.sock;

    重新加载nginx,使配置生效:

    service nginx reload

  • ThinkPHP实现支付宝接口功能 代码实例

    ThinkPHP实现支付宝接口功能 代码实例

    ThinkPHP 整合微信支付 扫码支付 模式二 图文教程

    我们这里用的是即时到帐的接口,具体实现的步骤如下:

    下载支付宝接口包

    下载地址:
    https://doc.open.alipay.com/doc2/detail?treeId=62&articleId=103566&docType=1
    具体如何下载,我就不在罗嗦了~~

    解压后放到框架的Vendor目录中即可~

    重新整理接口包文件,这一步应该算是比较关键的(个人认为)

    下载下来的接口包文件有很多语言的源码,

    我们选择 create_direct_pay_by_user-PHP-UTF-8 这个名称的接口文件。里面包括如下文件:

    images文件里是支付宝相关的一些标志的图片,我们暂不管他,lib文件很重要,是整个接口的核心类文件;
    alipay.config.php是相关参数的配置文件
    alipayapi.php 是支付宝接口入口文件
    notify_url.php 是服务器异步通知页面文件;
    return_url.php 是页面跳转同步通知文件;

    在ThinkPHP的框架文件下,找到Extend 进入,再进入Vendor,在Vendor文件夹下,新建文件夹Alipay,把支付宝作为第三方类库引入。然后,复制支付宝接口文件包中lib文件里的所有文件。一共4个文件,如下:

    现在对以上文件进行重命名,
    alipay_core.function.php重命名为:Corefunction.php;
    alipay_md5.function.php重命名为:Md5function.php;
    alipay_notify.class.php重命名为:Notify.php;
    alipay_submit.class.php重命名为:Submit.php;

    然后,打开Submit.php文件,把以下代码去掉;

    require_once("alipay_core.function.php");
    require_once("alipay_md5.function.php");

    同样,打开Notify.php文件,把以下两段代码去掉

    require_once("alipay_core.function.php");
    require_once("alipay_md5.function.php");

    为什么要去掉以上两个文件中的这两段代码,因为在项目中调用接口文件的时候,我把所有4个核心文件都通过vendor来进行引入。所以,这不再需要导入。

    到此,支付宝接口包相关核心类库的整理基本完成。现在开始在项目中调用;

    在项目中调用支付宝接口

    调用分两步:

    在配置文件中Conf/Config.php文件中对支付宝相关参数进行配置:

    //支付宝配置参数
    'alipay_config'=>array(
           'partner' =>'20********50',   //这里是你在成功申请支付宝接口后获取到的PID;
        'key'=>'9t***********ie',//这里是你在成功申请支付宝接口后获取到的Key
        'sign_type'=>strtoupper('MD5'),
        'input_charset'=> strtolower('utf-8'),
        'cacert'=> getcwd().'\\cacert.pem',
        'transport'=> 'http',
          ),
    //以上配置项,是从接口包中alipay.config.php 文件中复制过来,进行配置;
    'alipay'   =>array(
     //这里是卖家的支付宝账号,也就是你申请接口时注册的支付宝账号
    'seller_email'=>'pay@xxx.com',
    //这里是异步通知页面url,提交到项目的Pay控制器的notifyurl方法;
    'notify_url'=>'http://www.xxx.com/Pay/notifyurl', 
    //这里是页面跳转通知url,提交到项目的Pay控制器的returnurl方法;
    'return_url'=>'http://www.xxx.com/Pay/returnurl',
    //支付成功跳转到的页面,我这里跳转到项目的User控制器,myorder方法,并传参payed(已支付列表)
    'successpage'=>'User/myorder?ordtype=payed',   
    //支付失败跳转到的页面,我这里跳转到项目的User控制器,myorder方法,并传参unpay(未支付列表)
    'errorpage'=>'User/myorder?ordtype=unpay', 
    ),

    新建一个PayAction控制器,代码如下

    <?php
    class PayAction extends Action{
           //在类初始化方法中,引入相关类库    
           public function _initialize() {
            vendor('Alipay.Corefunction');
            vendor('Alipay.Md5function');
            vendor('Alipay.Notify');
            vendor('Alipay.Submit');    
        }
        
        //doalipay方法
            /*该方法其实就是将接口文件包下alipayapi.php的内容复制过来
              然后进行相关处理
            */
        public function doalipay(){
                /*********************************************************
                把alipayapi.php中复制过来的如下两段代码去掉,
                第一段是引入配置项,
                第二段是引入submit.class.php这个类。
               为什么要去掉??
                第一,配置项的内容已经在项目的Config.php文件中进行了配置,我们只需用C函数进行调用即可;
                第二,这里调用的submit.class.php类库我们已经在PayAction的_initialize()中已经引入;所以这里不再需要;
                *****************************************************/
           // require_once("alipay.config.php");
           // require_once("lib/alipay_submit.class.php");
           
           //这里我们通过TP的C函数把配置项参数读出,赋给$alipay_config;
           $alipay_config=C('alipay_config');  
     
            /**************************请求参数**************************/
            $payment_type = "1"; //支付类型 //必填,不能修改
            $notify_url = C('alipay.notify_url'); //服务器异步通知页面路径
            $return_url = C('alipay.return_url'); //页面跳转同步通知页面路径
            $seller_email = C('alipay.seller_email');//卖家支付宝帐户必填
            $out_trade_no = $_POST['trade_no'];//商户订单号 通过支付页面的表单进行传递,注意要唯一!
            $subject = $_POST['ordsubject'];  //订单名称 //必填 通过支付页面的表单进行传递
            $total_fee = $_POST['ordtotal_fee'];   //付款金额  //必填 通过支付页面的表单进行传递
            $body = $_POST['ordbody'];  //订单描述 通过支付页面的表单进行传递
            $show_url = $_POST['ordshow_url'];  //商品展示地址 通过支付页面的表单进行传递
            $anti_phishing_key = "";//防钓鱼时间戳 //若要使用请调用类文件submit中的query_timestamp函数
            $exter_invoke_ip = get_client_ip(); //客户端的IP地址 
            /************************************************************/
        
            //构造要请求的参数数组,无需改动
        $parameter = array(
            "service" => "create_direct_pay_by_user",
            "partner" => trim($alipay_config['partner']),
            "payment_type"    => $payment_type,
            "notify_url"    => $notify_url,
            "return_url"    => $return_url,
            "seller_email"    => $seller_email,
            "out_trade_no"    => $out_trade_no,
            "subject"    => $subject,
            "total_fee"    => $total_fee,
            "body"            => $body,
            "show_url"    => $show_url,
            "anti_phishing_key"    => $anti_phishing_key,
            "exter_invoke_ip"    => $exter_invoke_ip,
            "_input_charset"    => trim(strtolower($alipay_config['input_charset']))
            );
            //建立请求
            $alipaySubmit = new AlipaySubmit($alipay_config);
            $html_text = $alipaySubmit->buildRequestForm($parameter,"post", "确认");
            echo $html_text;
        }
        
            /******************************
            服务器异步通知页面方法
            其实这里就是将notify_url.php文件中的代码复制过来进行处理
            
            *******************************/
        function notifyurl(){
                    /*
                    同理去掉以下两句代码;
                    */ 
                    //require_once("alipay.config.php");
                    //require_once("lib/alipay_notify.class.php");
                    
                    //这里还是通过C函数来读取配置项,赋值给$alipay_config
            $alipay_config=C('alipay_config');
            //计算得出通知验证结果
            $alipayNotify = new AlipayNotify($alipay_config);
            $verify_result = $alipayNotify->verifyNotify();
            if($verify_result) {
                   //验证成功
                       //获取支付宝的通知返回参数,可参考技术文档中服务器异步通知参数列表
               $out_trade_no   = $_POST['out_trade_no'];      //商户订单号
               $trade_no       = $_POST['trade_no'];          //支付宝交易号
               $trade_status   = $_POST['trade_status'];      //交易状态
               $total_fee      = $_POST['total_fee'];         //交易金额
               $notify_id      = $_POST['notify_id'];         //通知校验ID。
               $notify_time    = $_POST['notify_time'];       //通知的发送时间。格式为yyyy-MM-dd HH:mm:ss。
               $buyer_email    = $_POST['buyer_email'];       //买家支付宝帐号;
                       $parameter = array(
                 "out_trade_no"     => $out_trade_no, //商户订单编号;
                 "trade_no"     => $trade_no,     //支付宝交易号;
                 "total_fee"     => $total_fee,    //交易金额;
                 "trade_status"     => $trade_status, //交易状态
                 "notify_id"     => $notify_id,    //通知校验ID。
                 "notify_time"   => $notify_time,  //通知的发送时间。
                 "buyer_email"   => $buyer_email,  //买家支付宝帐号;
               );
               if($_POST['trade_status'] == 'TRADE_FINISHED') {
                           //
               }else if ($_POST['trade_status'] == 'TRADE_SUCCESS') {                           if(!checkorderstatus($out_trade_no)){
                   orderhandle($parameter); 
                               //进行订单处理,并传送从支付宝返回的参数;
                   }
                }
                    echo "success";        //请不要修改或删除
             }else {
                    //验证失败
                    echo "fail";
            }    
        }
        
        /*
            页面跳转处理方法;
            这里其实就是将return_url.php这个文件中的代码复制过来,进行处理; 
            */
        function returnurl(){
                    //头部的处理跟上面两个方法一样,这里不罗嗦了!
            $alipay_config=C('alipay_config');
            $alipayNotify = new AlipayNotify($alipay_config);//计算得出通知验证结果
            $verify_result = $alipayNotify->verifyReturn();
            if($verify_result) {
                //验证成功
                //获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表
            $out_trade_no   = $_GET['out_trade_no'];      //商户订单号
            $trade_no       = $_GET['trade_no'];          //支付宝交易号
            $trade_status   = $_GET['trade_status'];      //交易状态
            $total_fee      = $_GET['total_fee'];         //交易金额
            $notify_id      = $_GET['notify_id'];         //通知校验ID。
            $notify_time    = $_GET['notify_time'];       //通知的发送时间。
            $buyer_email    = $_GET['buyer_email'];       //买家支付宝帐号;
                
            $parameter = array(
                "out_trade_no"     => $out_trade_no,      //商户订单编号;
                "trade_no"     => $trade_no,          //支付宝交易号;
                "total_fee"      => $total_fee,         //交易金额;
                "trade_status"     => $trade_status,      //交易状态
                "notify_id"      => $notify_id,         //通知校验ID。
                "notify_time"    => $notify_time,       //通知的发送时间。
                "buyer_email"    => $buyer_email,       //买家支付宝帐号
            );
            
    if($_GET['trade_status'] == 'TRADE_FINISHED' || $_GET['trade_status'] == 'TRADE_SUCCESS') {
            if(!checkorderstatus($out_trade_no)){
                 orderhandle($parameter);  //进行订单处理,并传送从支付宝返回的参数;
        }
            $this->redirect(C('alipay.successpage'));//跳转到配置项中配置的支付成功页面;
        }else {
            echo "trade_status=".$_GET['trade_status'];
            $this->redirect(C('alipay.errorpage'));//跳转到配置项中配置的支付失败页面;
        }
    }else {
        //验证失败
        //如要调试,请看alipay_notify.php页面的verifyReturn函数
        echo "支付失败!";
        }
    }
    }
    ?>

    这里有几个支付处理过程中需要用到的函数,我把这些函数写到了项目的Common/common.php中,这样不用手动调用,即可直接使用这些函数,代码如下:

    //////////////////////////////////////////////////////
    //Orderlist数据表,用于保存用户的购买订单记录;
    /* Orderlist数据表结构;
    CREATE TABLE `tb_orderlist` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `userid` int(11) DEFAULT NULL,购买者userid
      `username` varchar(255) DEFAULT NULL,购买者姓名
      `ordid` varchar(255) DEFAULT NULL,订单号
      `ordtime` int(11) DEFAULT NULL,订单时间
      `productid` int(11) DEFAULT NULL,产品ID
      `ordtitle` varchar(255) DEFAULT NULL,订单标题
      `ordbuynum` int(11) DEFAULT '0',购买数量
      `ordprice` float(10,2) DEFAULT '0.00',产品单价
      `ordfee` float(10,2) DEFAULT '0.00',订单总金额
      `ordstatus` int(11) DEFAULT '0',订单状态
      `payment_type` varchar(255) DEFAULT NULL,支付类型
      `payment_trade_no` varchar(255) DEFAULT NULL,支付接口交易号
      `payment_trade_status` varchar(255) DEFAULT NULL,支付接口返回的交易状态
      `payment_notify_id` varchar(255) DEFAULT NULL,
      `payment_notify_time` varchar(255) DEFAULT NULL,
      `payment_buyer_email` varchar(255) DEFAULT NULL,
      `ordcode` varchar(255) DEFAULT NULL,       //这个字段不需要的,大家看我西面的修正补充部分的说明!
      `isused` int(11) DEFAULT '0',
      `usetime` int(11) DEFAULT NULL,
      `checkuser` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
    */
    //在线交易订单支付处理函数
    //函数功能:根据支付接口传回的数据判断该订单是否已经支付成功;
    //返回值:如果订单已经成功支付,返回true,否则返回false;
    function checkorderstatus($ordid){
        $Ord=M('Orderlist');
        $ordstatus=$Ord->where('ordid='.$ordid)->getField('ordstatus');
        if($ordstatus==1){
            return true;
        }else{
            return false;    
        }
    }
    //处理订单函数
    //更新订单状态,写入订单支付后返回的数据
    function orderhandle($parameter){
        $ordid=$parameter['out_trade_no'];
        $data['payment_trade_no']      =$parameter['trade_no'];
        $data['payment_trade_status']  =$parameter['trade_status'];
        $data['payment_notify_id']     =$parameter['notify_id'];
        $data['payment_notify_time']   =$parameter['notify_time'];
        $data['payment_buyer_email']   =$parameter['buyer_email'];
        $data['ordstatus']             =1;
        $Ord=M('Orderlist');
        $Ord->where('ordid='.$ordid)->save($data);
    } 
    
    /*-----------------------------------
    2013.8.13更正
    下面这个函数,其实不需要,大家可以把他删掉,
    具体看我下面的修正补充部分的说明
    ------------------------------------*/
    //获取一个随机且唯一的订单号;
    function getordcode(){
        $Ord=M('Orderlist');
        $numbers = range (10,99);
        shuffle ($numbers); 
        $code=array_slice($numbers,0,4); 
        $ordcode=$code[0].$code[1].$code[2].$code[3];
        $oldcode=$Ord->where("ordcode='".$ordcode."'")->getField('ordcode');
        if($oldcode){
            getordcode();
        }else{
            return $ordcode;
        }
    }

    总结

    • 接口包中lib文件中的文件复制到Vendor后,重命名为TP规范的命名规则,为的是调用方便,当然你要改成其他名称也可以;
    • 把执行支付操作(doalipay),处理异步返回结果(notifyurl),处理跳转返回结果(returnurl)三个支付接口的核心页面写到一个PayAction控制器中。
    • 提交支付的页面中,可以在提交之前先把一些参数要传递的内容先通过隐藏域的方法组合好,比如金额先计算好,订单名称,订单描述等先用字符串组合好。然后提交表单,这样,在doalipay方法中只要直接构造传递参数,直接进行提交就行过了。
    • 支付返回后的处理因为要在异步和跳转两个方法中都要进行相应的判断和处理,所以,把这些判断和处理写到一个自定义函数中,这样只要调用函数即可,使得代码更加清晰明了。
    • notify_url和return_url两种模式的返回url必须使用http://xxxxxxx这样的绝对路径,因为里是从支付宝平台返回到你的项目页面。不能使用相对路径。

    以上代码在ThinkPHP3.0中正常使用!!

    ————————修正补充!!2013.08.13——————————
    在第三部分中Orderlist数据表结构中,我有一个字段是OrdCode,这个字段是我系统中用来发送短信给客户的消费密码,也就是客户凭手机短信来消费时就要验证这个字段。
    其实,大家在做系统的时候,可以把这个字段忽略,可以不用他。代码最后部分中,有一个获取一个随机且唯一的订单号的函数 getordcode(),这里我其实写错了,不是获取订单号,是ordcode,也就是消费密码,这个函数也不需要。系统中的订单号(ordid字段),我用的是时间戳。
    在此修正!

    ——————–解决签名错误问题 修正 13-08-16————————
    有人说在在调试时,签名出现无法通过的问题,产生问题的原因是在返回的URL地址中返回的参数中,可能存在__URL__这样的字符串。导致无法正确过滤参数。

    解决办法:
    方法1:
    在向支付宝提交需要的参数时,不要使用__URL__,__PUBLIC__等TP中的模版替换变量,如果TP对这些变量解析不成功,会直接传递过去,所以,在这些地方直接使用原始的URL地址。

    方法2:
    在接口的Core文件中,加入改造后的过滤函数,如下:

    /**
     * 除去数组中的空值和签名参数
     * @param $para 签名参数组
     * return 去掉空值与签名参数后的新签名参数组
     */
    function paraFilter($para) {
        $para_filter = array();
        while (list ($key, $val) = each ($para)) {
            if($key == "sign" || $key == "sign_type" || $key == '_URL_' || $val == "")continue;    //添加了$key == '_URL_' 
            else    $para_filter[$key] = $para[$key];
        }
        return $para_filter;
    }
    function myparaFilter($para) {
        $para_filter = array();
        while (list ($key, $val) = each ($para)) {
            if($key == '_URL_')continue;
            else $para_filter[$key] = $para[$key];
        }
        return $para_filter;
    }
WeChat