客戶的網站須要支付功能,咱們選擇了業界用的最多的支付寶即時到帳支付。申請了兩次將近兩週的時間終於下來了,因而我開始着手測試SDK整合支付流程。php
SDK中的代碼並不複雜,就是構造請求發送,接收並驗證簽名而已。SDK根目錄中的文件基本是示例,開發的時候用於參照, lib
目錄中是核心庫文件,在CodeIgniter中須要把這個目錄放到 application/third_party
目錄下,並將目錄名改成 alipay
方便標識,證書文件 cacert.pem
也放進去。其實更好的方式是把類文件放到 application/libraries
目錄並使用Loader加載類庫,可是其中有兩個公共函數文件引用,省的加載麻煩就直接 require_once()
了。html
配置文件也須要單獨增長一個 alipay.php
在 application/config
目錄中,主要能夠照搬示例中的 alipay.config.php
的內容,我是這麼寫的:git
$config['partner'] = '商戶id'; $config['key'] = '商戶API key'; $config['seller_email'] = '商戶支付寶郵箱帳號'; $config['payment_type'] = 1; $config['transport'] = 'http'; $config['input_charset'] = 'utf-8'; $config['sign_type'] = 'MD5'; $config['notify_url'] = 'http://'.$_SERVER['HTTP_HOST'].'/order/callback/notify'; $config['return_url'] = 'http://'.$_SERVER['HTTP_HOST'].'/order/callback/return'; $config['cacert'] = APPPATH.'third_party/alipay/cacert.pem';
以後就是在訂單控制器中處理支付了,當建立了一個商品訂單後,給用戶一個連接按鈕,跳轉到 /order/pay
控制器進行支付寶請求中轉,主要能夠參照示例 alipayapi.php
文件內容,調用SDK函數生成一個提交表單,渲染在模板中由JS控制自動提交。程序員
// 訂單控制器類 class Order extends CI_Controller{ // ... public function pay($id) { // 獲取訂單數據示例 $order = $this->model->get($id); // 加載支付寶配置 $this->config->load('alipay', TRUE); // 加載支付寶支付請求類庫 require_once(APPPATH."third_party/alipay/alipay_submit.class.php"); $submit = new AlipaySubmit($this->config->item('alipay')); $body = $submit->buildRequestForm(array( 'service' => 'create_direct_pay_by_user', 'partner' => $this->config->item('partner', 'alipay'), 'payment_type' => $this->config->item('payment_type', 'alipay'), 'notify_url' => $this->config->item('notify_url', 'alipay'), 'return_url' => $this->config->item('return_url', 'alipay'), 'seller_email' => $this->config->item('seller_email', 'alipay'), 'out_trade_no' => $id, 'subject' => $order['product']['name'], 'total_fee' => $order['price'], 'body' => $order['product']['name'], 'show_url' => 'http://'.$_SERVER['HTTP_HOST'].'/product/view/'.$order['productId'], 'anti_phishing_key' => '', 'exter_invoke_ip' => '', '_input_charset' => $this->config->item('input_charset', 'alipay') )); // 渲染模板,原生的這麼寫,我本身另外用smarty3 $this->load->view('pay', array('body' => $body)); } // ... }
正常的話用戶瀏覽器會跳轉到支付寶完成支付,以後再跳轉回以前配置了的 return_url
上,這個控制器方法我命名爲 /order/callback/<method>
,經過參數指定是同步返回仍是異步通知,但裏面的業務邏輯處理差很少。github
// 仍是訂單控制器類 class Order extends CI_Controller{ // $method參數只能是'return'或'notify',對應URL public function callback ($method) { // 加載支付寶配置 $this->config->load('alipay', TRUE); // 加載支付寶返回通知類庫 require_once(APPPATH."third_party/alipay/alipay_notify.class.php"); // 初始化支付寶返回通知類 $alipayNotify = new AlipayNotify($this->config->item('alipay')); $input = array(); $is_ajax = FALSE; $notify_status = 'success'; // 這裏作同步仍是異步的判斷並獲取返回數據驗證請求 switch ($method) { case 'notify': $result = $alipayNotify->verifyNotify(); $input = $this->input->post(); $is_ajax = TRUE; break; case 'return': $result = $alipayNotify->verifyReturn(); $input = $this->input->get(); break; default: return $this->out_not_found(); break; } // 支付寶返回支付成功和交易結束標誌 if ($result && ($input['trade_status'] == 'TRADE_FINISHED' || $input['trade_status'] == 'TRADE_SUCCESS')) { $id = $input['out_trade_no']; // 驗證成功則更新訂單信息(略) // ... } else { // 不然置狀態爲失敗 $notify_status = 'fail'; } if ($is_ajax) { // 異步方式調用模板輸出狀態 $this->view->load('alipay', array('status' => $notify_status)); } else { // 同步方式跳轉到訂單詳情控制器,redirect方法要你本身寫 return $this->redirect("order/view/$id#status:$notify_status"); } } }
到這正常的話數據庫已經更新訂單信息,後臺管理也就能夠根據支付狀況去後面的配送流程。但問題是,在CI中正常狀況根本到不了這裏!因此我要開始吐槽支付寶的SDK!ajax
VPS用的ubuntu系統,默認PHP的cURL模塊沒有,須要命令行安裝:sql
$ sudo apt-get install curl php5-curl
好吧,這是我沒有看 readme.txt
文檔,而後 var_dump()
花了三分錢才找到這個問題。數據庫
這纔是真正坑爹的問題!以前測試一直是支付成功但返回調用驗證失敗,直到我一步一步跟到SDK的源碼裏去對比要驗證的簽名串,才發現這根本就是SDK的一個BUG!請看 alipay_core.function.php
文件的 paraFilter
函數,這個函數的做用是過濾掉簽名參數和空值參數,以便生成簽名串。原來的SDK是這麼寫的:ubuntu
function paraFilter($para) { $para_filter = array(); // 問題就在這 while (list ($key, $val) = each ($para)) { if($key == "sign" || $key == "sign_type" || $val == "")continue; else $para_filter[$key] = $para[$key]; } return $para_filter; }
問題就出在 while
循環的條件裏,每次過濾參數,這裏直接就把第一個返回參數body=xxx
給過濾掉了,一旦我加上 body=xxx
變成完整的簽名參數,生成的MD5簽名就能對上。我百思不得其解的時候去查了PHP的文檔,根據 each
方法說明,每調用一次遊標就會發生改變,而首次調用以前沒有調用 reset()
的話,就極可能被以前調用過這個數組的 each()
給弄錯遊標。而每次代碼中每次都是從第二個參數開始,說明數組可能已經被其餘程序調用過了(這個數組其實是系統變量 $_GET
,但我實在沒在CI框架代碼中找到哪裏調用的)。因此我想說的是: 好!好!寫!個! foreach
!循環會死麼! 修改後的代碼以下:api
function paraFilter($para) { // 增長這一行 reset($para); $para_filter = array(); while (list ($key, $val) = each ($para)) { if($key == "sign" || $key == "sign_type" || $val == "")continue; else $para_filter[$key] = $para[$key]; } return $para_filter; }
這纔算解決了簽名不正確的問題。
另外,看了SDK源碼才發現,支付寶的PHP程序員英文真是爛,各類拼寫錯誤,好比:verify寫成veryfy,sign寫成sgin……實在是無力吐槽。
好了,我把修改過的SDK包放在GitHub上了: https://github.com/mytharcher/alipay-php-sdk ,有須要請自取。
-EOF-
http://yanjunyi.com/blog/posts/alipay-integration-in-codeigniter.html?utm_source=tuicool