大部分寫過接口的程序員都知道,不同項目之間的接口對接往往會踩到各種坑,尤其是許久不曾維護的框架比較老的項目。
我之前寫過的一個關於訂單接口,原理很簡單,將一個商城嵌入到一個積分管理項目中,在商城中購買物品,在另外一個積分項目中去支付,中間共有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文件中
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; } }
以上就是本人遇到的一些坑,更多的坑下次在分享吧 尤其是新手寫接口經常會采坑,最好多問問你們的項目經理,避免入坑!