大部分写过接口的程序员都知道,不同项目之间的接口对接往往会踩到各种坑,尤其是许久不曾维护的框架比较老的项目。

  我之前写过的一个关于订单接口,原理很简单,将一个商城嵌入到一个积分管理项目中,在商城中购买物品,在另外一个积分项目中去支付,中间共有5次数据交互,以接口的形式相互接受发送数据。最常用的就是curl了,大致的交互原理是这样的:

  我就不画图了,大致写一下,商户是商城一方,平台是积分支付一方

  第一步: 商户向平台下订单(后台接口) 商户的交易数据以json格式将接口数据提交到平台,平台实时处理并返回商户系统,平台方则将接收数据存贮到数据库中,作为订单信息。

  第二步:商户向平台发起支付请求。以curl形式将订单信息(订单号,交易流水等)发送至平台方,平台方根据发来的订单号获取交易信息(第一步已存储到数据库中),然后再平台上扣积分或是调用支付接口。记录到数据库中以作校验。

  第三步:交易结果通知(平台->商户  后台)平台方将交易结果返回,扣款成功或余额不足失败,将结果发送到商户平台中

  第四步:交易结果通知(平台->商户 前台) 同上返回结果通知,显示在前台,后台结果通知则保存在数据库中,防止前台结果通知由于服务器并发或是网络问题未能返回正确的结果。此时就通过后台交易结果验证。

  第五步:退款。同理。

  以上过程基本接触过接口这一块的都理解,接口的调用其实非常简单,最麻烦的都是在商户和平台的对接上,这次的对接就遇到了几个问题,虽说不难解决,但是非常麻烦,影响思路。

  1. curl访问失败的问题,代码如下:

private static function get_interface_result($data, $url){
$sign = ChengE::get_cashier_data_sign($data);
$data["sign"] = $sign;
$json_data = json_encode($data);
Log::write("下单url:" . $url);
Log::write("下单json_data:" . $json_data);
ChengE::insert_interface_res_log($url, $json_data, $log_id);
$ch = curl_init();
$timeout = 30;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
curl_setopt($ch, CURLOPT_POSTFIELDS, $json_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, "jb51.net's CURL Example beta");
curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);

    Log::write("接口【" . $url ."】");
$file_contents = curl_exec($ch);
$file_contents = json_decode($file_contents,true);
$http_code = 0;
if(!curl_errno($ch)){
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($http_code == 200)
{
$result = json_decode($file_contents,true);
$resultCode = $result['code'];
$resultMessage = $result['msg'];
}
}
Log::write("接口【" . $url ."】http_code:" . $http_code);
curl_close($ch);
ChengE::update_interface_res_log($log_id, $file_contents, $http_code, $resultCode, $resultMessage);
return $file_contents;
}

  失败原因有多个,一般的curl访问基本是一套模板,不会出现什么大的问题,难发现的基本是环境上的问题。

  首先是服务器环境问题,我用的是nginx5.6版本,curl接收到的响应数据是空!!! 打开测试一下,报错是这样的 Automatically populating $HTTP_RAW_POST_DATA is deprecated and will be removed in a future version.  解决这个问题不难,主要是版本5.6不支持。将配置文件php.ini中将'always_populate_raw_post_data' 这个属性配置成-1.

  配置好之后,虽然没有报错,但是却请求超时。无法的到响应结果,测试时页面一直无法刷新出来。其实这也是nginx环境问题

  因为我的平台和商户的代码都部署在同一台服务器下,而且要死不死的都是nginx环境。而nginx环境不支持curl访问本地域名的。要解决这个问题,可以配置到2台不同的服务器上(如果有多余的服务器的话),其次是可以在同一环境下用apache环境,apache是支持curl访问本地的。最简单的是修改nginx的配置,也可以安装一个依赖包。修改配置如下,

nginx.conf文件中

server {
listen       80;
        server_name  127.0.0.1;
 
location ~ \.php($|/) {
root               项目地址
fastcgi_pass   127.0.0.1:9000;
 
}
 
php.ini文件中:
 
set PHP_FCGI_MAX_REQUESTS=1000
echo Starting PHP FastCGI...
RunHiddenConsole D:/wnmp/php-5.6.27/php-cgi.exe -b 127.0.0.1:9000 -c D:/wnmp/php-5.6.279/php.ini
RunHiddenConsole D:/wnmp/php-5.6.27/php-cgi.exe -b 127.0.0.1:9090 -c D:/wnmp/php-5.6.27/php.ini
echo Starting nginx...
RunHiddenConsole D:/wnmp/nginx-1.7.7/nginx.exe -p D:/wnmp/nginx-1.7.7
 
  2.  接口传值时的验签问题
  接口中为了防止他人而已篡改数据,往往会用非对称型加密将请求数据作签名认证。代码如下:
  public static function login_validate_sign($queryStr){
parse_str($queryStr, $get_param_array);
$ori_array = array(
exToken => $get_param_array['exToken'],
mobile => $get_param_array['mobile'],
reqParty => ChengE::REQPARTY,
timestamp => $get_param_array['timestamp'],
);
ksort($ori_array);
$ori_str = http_build_query($ori_array)."&key=" .ChengE::LOGIN_KEY;
$sign_str = md5($ori_str);
$ori_sign = $get_param_array['sign'];
//验签通过
if($sign_str == $ori_sign){
return true;
}
else{
return false;
}
}
在不同的nginx版本环境中,有些加密结果都是不一样,这个用debug可以很容易的查出原因,要解决的话,要不重写一个加密算法
或是将2个版本算法统一,最直接的是直接写个依赖处理类:
DES算法:
class DES { var $key; function DES($key) { $this->key =$key; } function encrypt($input) { $size = mcrypt_get_block_size('des', 'ecb'); $input = $this->pkcs5_pad($input, $size); $key = 'x8thTs9j1dpcw10vba4opm4c'; $td = mcrypt_module_open('des', '', 'ecb', ''); $iv = @mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND); @mcrypt_generic_init($td, $key, $iv); $data = mcrypt_generic($td, $input); mcrypt_generic_deinit($td); mcrypt_module_close($td); $data = base64_encode($data); return $data; } function decrypt($encrypted) { $encrypted = base64_decode($encrypted); $key = 'x8thTs9j1dpcw10vba4opm4c'; $td = mcrypt_module_open('des','','ecb',''); //使用MCRYPT_DES算法,cbc模式 $iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); $ks = mcrypt_enc_get_key_size($td); @mcrypt_generic_init($td, $key, $iv); //初始处理 $decrypted = mdecrypt_generic($td, $encrypted); //解密 mcrypt_generic_deinit($td); //结束 mcrypt_module_close($td); $y=$this->pkcs5_unpad($decrypted); return $y; } function pkcs5_pad ($text, $blocksize) { $pad = $blocksize - (strlen($text) % $blocksize); return $text . str_repeat(chr($pad), $pad); } function pkcs5_unpad($text) { $pad = ord($text{strlen($text)-1}); if ($pad > strlen($text)) return false; if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false; return substr($text, 0, -1 * $pad); } }

3. 路由问题,curl访问的路径如果是路由重写过后的地址可能会导致访问失败,所以路由最好配置一下。比如vhost文件中:
server { listen 80; server_name www.xfl.com phpStudy.net; root "E:/phpstudy/WWW/xfl"; location / { index index.html index.htm index.php; #autoindex on; } location ~ \.php(.*)$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_split_path_info ^((?U).+\.php)(/?.+)$; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; include fastcgi_params; } }

以上就是本人遇到的一些坑,更多的坑下次在分享吧 尤其是新手写接口经常会采坑,最好多问问你们的项目经理,避免入坑!
 
posted on 2018-03-13 16:35  河畔  阅读(963)  评论(2编辑  收藏