标签 PHP 下的文章

支付宝手机网站支付notify异步返回验签失败

搞了一个星期后搞定。

过程如下:

首先上传企业的信息,通过审核之后,开始接入。

下载了最新的支付宝手机网址支付SDK PHP版。

http://aopsdkdownload.cn-hangzhou.alipay-pub.aliyun-inc.com/demo/alipay.trade.wap.pay-PHP-UTF-8.zip

配置填写参数:APPID、openssl生成的私钥,公钥要上传到支付宝商家后台

支付宝网关目前有两个,一个是以前的商家号1.0接口:https://mapi.alipay.com/gateway.do

目前最新2.0采用应用的形式,接口:https://openapi.alipay.com/gateway.do

支付宝手机网站支付notify异步返回验签失败

采用AlipayTradeServcie类请求支付:

$timeout_express="1m"; 
$payRequestBuilder = new AlipayTradeWapPayContentBuilder(); 
$payRequestBuilder->setBody($body); 
$payRequestBuilder->setSubject($subject); 
$payRequestBuilder->setOutTradeNo($out_trade_no); 
$payRequestBuilder->setTotalAmount($total_amount); 
$payRequestBuilder->setTimeExpress($timeout_express); 
$payResponse = new AlipayTradeService($config); 
$result=$payResponse->wapPay($payRequestBuilder,$config['return_url'],$config['notify_url']); 

异步回调接收$_POST数据:notify_url.php

$arr = $_POST; 
$alipaySevice = new AlipayTradeService($config); 
$result = $alipaySevice->check($arr); 
jqlog("notify data:"); 
jqlog(print_r($arr, true)); 
if ($result) { //验证成功

$result一直返回false,进入AlipayTradeService.php里面调用的openssl_verify,开日志,看不出来问题, 没有阿里官方的错误码。仅仅是openssl_verify函数返回失败。

大致就是使用了支付宝官网的php sdk集成到我们的第三方php商城平台。同步返回的验签通过了。但是异步验签失败。

同步返回的数据:

app_id=2016090101835493&auth_app_id=2016090101835493&charset=UTF-8&method=alipay.trade.wap.pay.return&out_trade_no=2016129153945712&seller_id=2088412610646251&timestamp=2016-12-09 15:39:54&total_amount=0.01&trade_no=2016120921001004240294983745&version=1.0

验签通过

异步返回的数据:app_id=2016090101835493&auth_app_id=2016090101835493&buyer_id=2088002228430249&buyer_logon_id=tun***@gmail.com&buyer_pay_amount=0.01&charset=UTF-8&fund_bill_list=[{"amount":"0.01","fundChannel":"ALIPAYACCOUNT"}]&gmt_create=2016-12-09 15:34:25&gmt_payment=2016-12-09 15:34:25&invoice_amount=0.01&notify_id=62fec6aebe29efc9de91810311654b1huq&notify_time=2016-12-09 15:40:53&notify_type=trade_status_sync&out_trade_no=2016120979410&point_amount=0.00&receipt_amount=0.01&seller_email=****&seller_id=2088412610646251&subject=order2016120979410&total_amount=0.01&trade_no=2016120921001004240295013034&trade_status=TRADE_SUCCESS&version=1.0

最后别着没办法,咨询支付宝网上客服,发现是机器人,没用!打支付宝售后电话 0571-88158090 ,告诉我技术问题上支付宝网站咨询。在支付宝开放平台论坛提问,没有人回答。论坛也没啥人气。我的帖子还是第462贴。其实最有效和支付宝工作人员沟通的方式是在支持中心页面点击:提问。类似于tickets的方式一对一解答。

最后问题找到,支付宝返回数据有问题,转义字符多了一个反斜杠。

'fund_bill_list' => '[{\\"amount\\":\\"0.01\\",\\"fundChannel\\":\\"ALIPAYACCOUNT\\"}]'

正确的应该是这样的

'fund_bill_list' => '[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]'

很奇怪的是,支付宝直接给我返回的就是两个反斜杠,所以PHP的解决办法就是返回的变量stripslashes一次就可以了。

$arr['fund_bill_list'] = stripslashes($arr['fund_bill_list']);

修改Typecho后台日期显示方式

修改前:

Typecho日期默认显示方式.png

最近的7天内,显示的字符是:“N天前”,其实我更关注的是博文的具体年月日。所以定位到\var\Typecho\I18n.php文件Typecho_I18n类的dateWord方法:

public static function dateWord($from, $now)
{
    $between = $now - $from;

    /** 如果是一天 */
    if ($between >= 0 && $between < 86400 && date('d', $from) == date('d', $now)) {
        /** 如果是一小时 */
        if ($between < 3600) {
            /** 如果是一分钟 */
            if ($between < 60) {
                if (0 == $between) {
                    return _t('刚刚');
                } else {
                    return str_replace('%d', $between, _n('一秒前', '%d秒前', $between));
                }
            }

            $min = floor($between / 60);
            return str_replace('%d', $min, _n('一分钟前', '%d分钟前', $min));
        }

        $hour = floor($between / 3600);
        return str_replace('%d', $hour, _n('一小时前', '%d小时前', $hour));
    }

    /** 如果是昨天 */
    if ($between > 0 && $between < 172800 
    && (date('z', $from) + 1 == date('z', $now)                             // 在同一年的情况 
        || date('z', $from) + 1 == date('L') + 365 + date('z', $now))) {    // 跨年的情况
        return _t('昨天 %s', date('H:i', $from));
    }

    /** 如果是一个星期 */
    if ($between > 0 && $between < 604800) {
        $day = floor($between / 86400);
        return str_replace('%d', $day, _n('一天前', '%d天前', $day));
    }

    /** 如果是 */
    if (date('Y', $from) == date('Y', $now)) {
        return date(_t('n月j日'), $from);
    }

    return date(_t('Y年m月d日'), $from);
}

仅仅保留最后一行,简单粗暴:

public static function dateWord($from, $now)
{
    return date(_t('Y年m月d日'), $from);
}

修改后效果:

Typecho日期修改后显示方式.png.png

将外链图片保存到Typecho本地

<?php 
//connect to typecho database
$curl = curl_init();
$res = mysql_connect('localhost', 'root', '1111');
$ret = mysql_select_db('typecho', $res);
$sql = "select * from jq_contents where type = 'post' order by cid asc";
$query = mysql_query($sql, $res);
while($row = mysql_fetch_assoc($query))
{
    
    preg_match_all('/src="(http:\/\/ww[1234].*?\/.*?\/(.*?))".*?"/', $row['text'], $matches);
    //url : $matches[1][x]  filename : $matches[2][x] , alt: $matches[3][x]
    foreach($matches[1] as $k=>$pic)
    {
        //print_r($matches);
        //文章日期->local date
        $local_date =  date('Y-m-d', $row['created']);
        //print_r($local_date);
        //建立图片所在日期规格的目录
        $arr_date = explode('-', $local_date);
        //@mkdir(dirname(__FILE__)."\\usr\\uploads\\".$arr_date[0]); 
        //@mkdir(dirname(__FILE__)."\\usr\\uploads\\".$arr_date[0]."\\".$arr_date[1]);
        //print_r(dirname(__FILE__)."\\usr\\uploads\\".$arr_date[0]."\\".$arr_date[1].'<br />');
        //下载图片
        /*
            curl_setopt($curl, CURLOPT_URL, $matches[1][$k]);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($curl, CURLOPT_CONNECTTIMEOUT,0);
            $data = curl_exec($curl);
            $ret = file_put_contents(dirname(__FILE__)."\\usr\\uploads\\".$arr_date[0]."\\".$arr_date[1]."\\".$matches[2][$k], $data);
            print_r(dirname(__FILE__)."\\usr\\uploads\\".$arr_date[0]."\\".$arr_date[1]."\\".$matches[2][$k]." ret:$ret<br />");
        
        */
        //插入typecho数据库
        /*
        $type = explode('.',$matches[2][$k]);
        $text = array(
            'name'=>$matches[2][$k],
            'path'=>"/usr/uploads/".$arr_date[0]."/".$arr_date[1]."/".$matches[2][$k],
            'size'=>filesize(dirname(__FILE__)."\\usr\\uploads\\".$arr_date[0]."\\".$arr_date[1]."\\".$matches[2][$k]),
            'type'=> $type[1],
            'mime'=>mime_content_type(dirname(__FILE__)."\\usr\\uploads\\".$arr_date[0]."\\".$arr_date[1]."\\".$matches[2][$k])
        );
        $sql2 = sprintf("INSERT INTO `jq_contents` (  `title`, `slug`, `created`, `modified`, `text`, `order`, `authorId`, `template`, `type`, `status`, `password`, `commentsNum`, `allowComment`, `allowPing`, `allowFeed`, `parent`) VALUES('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')", $matches[2][$k],str_replace('.','-', $matches[2][$k]), $row['created'],$row['created'],serialize($text), $k, 1, '', 'attachment', 'publish', '', 0, 1,1,1,$row['cid']);
        $query2 = mysql_query($sql2, $res);
        print_r($sql2.'<br />');
        
        */
        //更新文章内的图片路径
        
        $old_img_path = $matches[1][$k];
        $new_img_path = "http://tunps.com/usr/uploads/".$arr_date[0]."/".$arr_date[1]."/".$matches[2][$k];
        $newtext = str_replace($old_img_path, $new_img_path, $row['text']);
        $newtext = mysql_escape_string($newtext);
        $sql3 = "UPDATE `jq_contents` SET  `text` =  '$newtext' where cid=$row[cid] ";
        $query3 = mysql_query($sql3);
        var_dump($query3);
         
    }
}
curl_close($curl);

Gist: https://gist.github.com/fr33m44/118d9f3873dbf959228f06bfe5a0253a

以上PHP脚本的作用是将外链的图片通过cURL下载到Typecho本地博客目录。然后在数据库中替换文章内容中的图片链接为本地图片链接。同时将图片信息关联到文章上。

通过contents.parent字段与contents.cid字段关联。

之所有没有做成Typecho插件形式,那是因为。。。我不懂Typecho插件机制。。。= =#

升级WampServer中Apache、PHP、MySQL版本

初到jjsj公司工作又近2个月。了解到公司从16年4月开始做EC的B2B商城。由于公司领导不懂技术。找到外包公司做了一个Java版本的系统短期快速上线后发现问题多多也找不到售后。功能修改的需求无法快速满足。又下线后重新采用PHP+ECShop重新开发了一套。前期Java版本在阿里云ECS CentOS上跑。后来ECShop上马又转到Windows Server 2008 R2 服务器环境用的是奇葩的IIS+PHP组合。确实是够折腾的。

三个月我把服务器上面的IIS卸载掉,转到WAMP环境(采用WampServer),我用的是WampServer2.0i.exe安装包,2009年发布的版本,包含:Apache2.2.11,PHP5.3.0,MySQL5.1.36。为什么要用这么老的版本?难道不怕security bug吗。非也非也。最新的ECShop源码2.7.3最高只能支持到PHP5.3。ECShop里面又很多引用新建对象代码:&new Object,这玩意儿在新版本PHP5.5及其以上的已经版本已经deprecated。

WampServer2.0i.exe里面的二进制都是采用VC6编译。而最新的一般都是VC9。这点需要注意。

虽然说AMP套件是跨平台开源软件,但实际上Windows平台一直都是被歧视的。所以找到对应版本的Win32 VC9 编译版确实是门学问。

这里:http://mirrors.cnnic.cn/apache//httpd/binaries/win32/ 有可以下载编译好的Win32版本。

Apache 2.2.11升级到2.2.31

到apachehaus下载 httpd-2.2.31-x86-r6.zip解压到D:\tunps.com\bin\apache\Apache2.2.31,将老版本(2.2.11)手动卸载。在cmd下运行:D:\tunps.com\bin\apache\Apache2.2.11\bin\httpd.exe -k uninstall,然后安装新版本:D:\tunps.com\bin\apache\Apache2.2.31\bin\httpd.exe -k install -n apache22,将老版本的conf目录覆盖到新版本的conf目录。修改PHP LoadModule的路径和ServerRoot:

ServerRoot "D:/tunps.com/bin/apache/apache2.2.31"

LoadModule php5_module "D:/tunps.com/bin/php/php5.3.29/php5apache2_2.dll"

然后重启Apahce让配置生效:D:\tunps.com\bin\apache\Apache2.2.31\bin\httpd.exe -k restart
注意:因为2.2.31用的是VC9编译,为了让httpd跑起来,必须确保服务器环境已经安装VC++ Redist 2008 SP1。

PHP 5.3.0升级到5.3.29

PHP也是同样的道理,官方php.net不提供win32而精致版,所以在apachelounge搜索到人家的编译版:

https://phpdev2.toolsforresearch.com/php-5.3.29-nts-Win32-VC9-x86.zip 
https://phpdev2.toolsforresearch.com/php-5.3.29-Win32-VC9-x86.zip 
https://phpdev2.toolsforresearch.com/php-5.3.29-nts-Win32-VC9-x64.zip 
https://phpdev2.toolsforresearch.com/php-5.3.29-Win32-VC9-x64.zip 

nts表示非线程安全,我们不需要这个。下载好之后解压到D:\tunps.com\bin\php\php5.3.29,将老版本PHP5.3.0的php.ini覆盖到5.3.29,并且覆盖一份到apache2.2.31\bin目录下。这样不用修改任何配置,可以直接使用。但是php.ini有一个地方还是要修改:

extension_dir = "D:/tunps.com/bin/php/php5.3.29/ext/"

MySQL 5.1.36升级到最新的5.7.16

因为PHP一般都是采用tcp socket和MySQL连接,所以兼容性要求并不高,哪怕PHP的MySQL扩展(php_mysqli.dll)还是5.0.8 2010年的版本,但还是可用。

到MySQL官网下载最新的MySQL win32压缩包(mysql-5.7.16-win32.zip),现在的MySQL安装包做得很大,有400MB左右,里面不仅仅包含二进制还是各种文档、各种语言的客户端连接library还有Visual Studio扩展等等。安装程序也不是十年那样的简陋,可以安装的过程中实现各种高级配置(端口、用户名、安全性设置 etc.)。

解压MySQL压缩包到D:\tunps.com\bin\mysql\mysql-5.7.16-win32,管理员cmd下安装MySQL:

mysqld --install mysql57

初始化数据:

mysqld --initialize

或者:

mysqld --initialize-insecure

没有加insecure的自动生成一个随机的root密码, root密码在:D:\tunps.com\bin\mysql\mysql-5.7.16-win32\data\<host name>.err 日志文件里面。加了insecure的,默认root没有密码。mysql -uroot -p登录后用

set password = password('tunps.com');

设置为新密码。

然后重新导入SQL文件即可。

如果不初始化mysql直接启动mysql服务会启动不起来,事件查看器eventvwr.msc报错:

failed to set datadir to D:\tunps.com\bin\mysql\mysql-5.7.16-win32\data\

phpMyAdmin 3.2.0.1升级到4.6.5.1

更炫的ajax操作,效率大为提升,下载后解压缩到D:\tunps.com\apps\phpMyAdmin-4.6.5.1-all-languages,修改D:\tunps.com\alias\phpmyadmin.conf里面Alias路径。

注意:以上所有的软件都是x86的。

phpMyAdmin 无法加载 mysqli 扩展,请检查您的 PHP 配置

昨天打算给公司的WAMP套件更新一下版本,更新完了Apahce、PHP、MySQL版本之后,结果浏览器打开phpMyAdmin报错:

phpMyAdmin 无法加载 mysqli 扩展,请检查您的 PHP 配置

首先想到的是打开php.ini配置查看php_mysql.dllphp_mysqli.dll扩展是否已经加载起来。

打开D:\wamp\bin\apache\Apache2.2.31\bin\php.ini,发现

extension=php_mysql.dll
extension=php_mysqli.dll

前面没有; 然后随便写一个phpinfo()http://tunps.com/info.php,查看mysqlmysqli扩展也是加载起来了的。

phpinfo

在困扰了我一个小时之后,偶然将用IE浏览器可以正常打开phpMyAdmin,并且可以正常登录。然后返回到Chrome开发浏览器还是一样的红色标题报错。那么到此真相大白。是Chrome缓存的问题,清空缓存了一些正常。这里不得不吐槽一下phpMyAdmin的报错设计。又错误直接给你跳转到http://tunps.com/error.php,浏览器缓存后就一直跳转到这里页面,就算问题解决了也是这样。坑啊!!!