THINHPHPjavascript
THINHPHP結合支付寶接口總結:php
1.採用THINKPHP3.1.3版本html
2.從支付寶官網下載php版本的demo,把utf-8(我用的是utf-8的編碼)解壓到THINKPHP框架的項目的Lib目錄下的Extend目錄下,刪除沒必要要的文件,而且把證書文件拷貝到網站的根目錄下(不要考慮到安全問題,由於這個證書能夠在官網直接下載,也不用修改任何代碼,沒有必要去下載你根目錄下面的證書文件,在官網能夠隨意下載)。準備工做已經所有完成,直接看個人募界結構:java
3在ACTION下面創建AlipayAction.class.phpthinkphp
源碼:api
<?php數組
/**瀏覽器
* 支付寶接口安全
* @author Administrator服務器
*
*/
class AlipayapiAction extends CommonAction{
private $_alipay_config=array();//支付寶配置
private $_userid;//登陸的userid
private $_username;//登陸的username
private $_mycount='XXXX@xxx.cn';//支付寶簽約賬號
publicfunction __construct(){
parent::__construct();
/**start******加載支付寶接口類***********/
require_once './Home/Lib/Extend/Alipayapi/lib/alipay_submit.class.php';
require_once './Home/Lib/Extend/Alipayapi/lib/alipay_core.function.php';
require_once './Home/Lib/Extend/Alipayapi/lib/alipay_notify.class.php';
require_once './Home/Lib/Extend/Alipayapi/lib/alipay_md5.function.php';
/**end********加載支付寶接口類***********/
self::setalipay_config();//設置支付寶基本配置
}
/**
*設置支付寶
*/
privatefunction setalipay_config(){
/* *
* 配置文件
* 版本:3.3
* 日期:2012-07-19
* 說明:
* 如下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶能夠根據本身網站的須要,按照技術文檔編寫,並不是必定要使用該代碼。
* 該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。
* 提示:如何獲取安全校驗碼和合做身份者id
* 1.用您的簽約支付寶帳號登陸支付寶網站(www.alipay.com)
* 2.點擊「商家服務」(https://b.alipay.com/order/myorder.htm)
* 3.點擊「查詢合做者身份(pid)」、「查詢安全校驗碼(key)」
* 安全校驗碼查看時,輸入支付密碼後,頁面呈灰色的現象,怎麼辦?
* 解決方法:
* 1、檢查瀏覽器配置,不讓瀏覽器作彈框屏蔽設置
* 2、更換瀏覽器或電腦,從新登陸查詢。
*/
//↓↓↓↓↓↓↓↓↓↓請在這裏配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
$alipay_config['partner']= '20880XXXXX071';//合做身份者id,以2088開頭的16位純數字
$alipay_config['key']= '8XXXXXXs8ywrwxxxxxxck65xxxxhct';//安全檢驗碼,以數字和字母組成的32位字符
//↑↑↑↑↑↑↑↑↑↑請在這裏配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
$alipay_config['sign_type']= strtoupper('MD5');//簽名方式不需修改
$alipay_config['input_charset']=strtolower('utf-8');//字符編碼格式目前支持 gbk 或 utf-8
//ca證書路徑地址,用於curl中ssl校驗
//請保證cacert.pem文件在當前文件夾目錄中
$alipay_config['cacert']= getcwd().'\\cacert.pem';
//訪問模式,根據本身的服務器是否支持ssl訪問,若支持請選擇https;若不支持請選擇http
$alipay_config['transport']= 'http';
$this->_alipay_config=$alipay_config;//設置支付寶基本配置
}
/**
* 獲得支付寶配置
*/
private function getalipay_config(){
return $this->_alipay_config;
}
public function index(){
A('Public')->checklogin();
$this->_userid=$_SESSION['userid'];
$this->_username=$_SESSION['username'];
$alipay_config=self::getalipay_config();//獲得支付寶基本配置
/**************************請求參數**************************/
$payment_type = "1";//支付類型,必填,不能修改,(官方文檔這麼說的)
//服務器異步通知頁面路徑
//$notify_url ="http://www.xxx.com/create_direct_pay_by_user-PHP-UTF-8/notify_url.php";
$notify_url = "http://www.xxxxx.cn/alipayapi/notifyurl";
//需http://格式的完整路徑,不能加?id=123這類自定義參數
//頁面跳轉同步通知頁面路徑
//$return_url = "http://www.xxx.com/create_direct_pay_by_user-PHP-UTF-8/return_url.php";
$return_url = "http://www.xxxx.cn/alipayapi/returnurl";
//需http://格式的完整路徑,不能加?id=123這類自定義參數,不能寫成http://localhost/
//賣家支付寶賬戶
//$seller_email =$_POST['WIDseller_email'];
$seller_email = $this->_mycount;
//必填
//商戶訂單號
//$out_trade_no =$_POST['WIDout_trade_no'];
$order_no=date('YmdHis').rand(1000,9999);
$out_trade_no = $order_no;
//商戶網站訂單系統中惟一訂單號,必填
/********將訂單號與用戶ID寫入訂單表以便支付寶返回中查找*********/
$ulast_id=M('Usercheckout')->add(array('trade_no'=>$out_trade_no,'user_id'=>$this->_userid));
if (false===$ulast_id){
echo'<script>alert("生成訂單失敗!請從新填寫!");history.back();</script>';
exit();
}
/********將訂單號與用戶ID寫入訂單表*********/
//$subject = $_POST['WIDsubject'];
$subject = '充值';//訂單名稱,必填(官方文檔這麼說的)
$total_fee = $_POST['WIDtotal_fee'];//付款金額,必填(官方文檔這麼說的)
//訂單描述
//$body = $_POST['WIDbody'];
$remark='會員 '.getUserName($_SESSION['userid']).'在'.date('Y-m-dH:i:s').'充值'.$_POST['WIDtotal_fee'].'元';
$body =$remark;
//$show_url = $_POST['WIDshow_url'];
$show_url='http://www.xxxxx.cn/';//商品展現地址(個人設置了也不會跳轉到這個頁面不知道爲何)
//需以http://開頭的完整路徑,例如:http://www.xxx.com/myorder.html
//防釣魚時間戳
$anti_phishing_key = "";
//若要使用請調用類文件submit中的query_timestamp函數
//客戶端的IP地址
//$exter_invoke_ip = "";
$exter_invoke_ip = get_client_ip();
//非局域網的外網IP地址,如:221.0.0.1
/************************************************************/
//構造要請求的參數數組,無需改動
$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,"get", "確認");//會返回javascript模擬表單提交html頁面
//$this->assign('html_text',$html_text);
$this->display();
echo $html_text;//必須輸出不然不會跳轉
}
/**
* 支付寶異步請求
*/
/* *官方說明
* 功能:支付寶服務器異步通知頁面
* 版本:3.3
* 日期:2012-07-23
* 說明:
* 如下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶能夠根據本身網站的須要,按照技術文檔編寫,並不是必定要使用該代碼。
* 該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。
*************************頁面功能說明*************************
* 建立該頁面文件時,請留心該頁面文件中無任何HTML代碼及空格。
* 該頁面不能在本機電腦測試,請到服務器上作測試。請確保外部能夠訪問該頁面。
* 該頁面調試工具請使用寫文本函數logResult,該函數已被默認關閉,見alipay_notify_class.php中的函數verifyNotify
* 若是沒有收到該頁面返回的 success 信息,支付寶會在24小時內按必定的時間策略重發通知
*/
public function notifyurl(){
$alipay_config=self::getalipay_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'];
//請在這裏加上商戶的業務邏輯程序代
$alipaylog=M("Alipaylog");//詳細的支付寶支付記錄表
$checkoutlog=M("Checkoutlog");//簡單的支付記錄表
$user=M('User');
$usercheckout=M('Usercheckout');//訂單號與user_id的中間表,用於查找user_id
$alipaylog->startTrans();
if ($_POST['out_trade_no']){
$is_out_trade_no=$alipaylog->where('out_trade_no='.$_POST['out_trade_no'])->count();
$ucinfo=$usercheckout->where('trade_no='.$_POST['out_trade_no'])->find();
}
$data3=array(
'user_id'=>$ucinfo['user_id'],
'gmt_create'=>strtotime($_POST['gmt_create']),
'gmt_payment'=>strtotime($_POST['gmt_payment']),
'gmt_close'=>strtotime($_POST['gmt_close']),
'gmt_refund'=>strtotime($_POST['gmt_refund'])
);
unset($_POST['gmt_create']);
unset($_POST['gmt_payment']);
unset($_POST['gmt_close']);
unset($_POST['gmt_refund']);
$data2=array_merge($_POST,$data3);
if (!$is_out_trade_no){
$alist_id=$alipaylog->add($data2);
}
$clist_id=$checkoutlog->add($data2);
if ($ucinfo){
$userid=$ucinfo['user_id'];
$price=$user->where('id='.$userid)->getField('price');
$ulist_id=$user->where('id='.$userid)->setField('price',(float)$price+(float)$_POST['total_fee']);
}
//——判斷業務邏輯——//
if ((false!==$alist_id)&&(false!==$clist_id)&&(false!==$ulist_id)){
if($_POST['trade_status']== 'TRADE_FINISHED') {
//判斷該筆訂單是否在商戶網站中已經作過處理
//若是沒有作過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序
//若是有作過處理,不執行商戶的業務程序
$alipaylog->commit();
//注意:
//該種交易狀態只在兩種狀況下出現
//1、開通了普通即時到帳,買家付款成功後。
//2、開通了高級即時到帳,從該筆交易成功時間算起,過了簽約時的可退款時限(如:三個月之內可退款、一年之內可退款等)後。
//調試用,寫文本函數記錄程序運行狀況是否正常
//logResult("這裏寫入想要調試的代碼變量值,或其餘運行的結果記錄");
}else if ($_POST['trade_status']== 'TRADE_SUCCESS') {
//判斷該筆訂單是否在商戶網站中已經作過處理
//若是沒有作過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序
//若是有作過處理,不執行商戶的業務程序
$alipaylog->commit();
//注意:
//該種交易狀態只在一種狀況下出現——開通了高級即時到帳,買家付款成功後。
//調試用,寫文本函數記錄程序運行狀況是否正常
//logResult("這裏寫入想要調試的代碼變量值,或其餘運行的結果記錄");
}
echo "success";//請不要修改或刪除
}else{
$alipaylog->rollback();
echo "fail";
}
}else {
//驗證失敗
echo "fail";
//調試用,寫文本函數記錄程序運行狀況是否正常
//logResult("這裏寫入想要調試的代碼變量值,或其餘運行的結果記錄");
}
}
/**
* 支付寶同步請求
*/
public function returnurl(){
$alipay_config=self::getalipay_config();
//計算得出通知驗證結果
$alipayNotify = new AlipayNotify($alipay_config);
//$verify_result =$alipayNotify->verifyReturn();
//if($verify_result) {//驗證成功
//請在這裏加上商戶的業務邏輯程序代碼
//——請根據您的業務邏輯來編寫程序(如下代碼僅做參考)——
//獲取支付寶的通知返回參數,可參考技術文檔中頁面跳轉同步通知參數列表
//thinkphp的配置url模式爲兼容模式能夠用$_GET來獲取?的值,實在不行能夠獲取到url完成路徑,用?拆分。
//商戶訂單號
$out_trade_no= $_GET['out_trade_no'];
//支付寶交易號
$trade_no = $_GET['trade_no'];
//交易狀態
$trade_status = $_GET['trade_status'];
if($_GET['trade_status']== 'TRADE_FINISHED' || $_GET['trade_status'] == 'TRADE_SUCCESS') {
//判斷該筆訂單是否在商戶網站中已經作過處理
echo '<script>alert("支付成功!!!");location.href="'.__APP__.'/member/myaccount/";</script>';
//若是沒有作過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序
//若是有作過處理,不執行商戶的業務程序
}
else {
echo "trade_status=".$_GET['trade_status'];
}
//echo "驗證成功<br/>";
//——請根據您的業務邏輯來編寫程序(以上代碼僅做參考)——
//}else {
//驗證失敗
//如要調試,請看alipay_notify.php頁面的verifyReturn函數
//echo "驗證失敗";
//}
$this->display();
}
}
3.基本就是把官方的demo中的同步與異步頁面移植到AlipayAction中的方法裏面,連註釋都沒有更改,代碼再也不作詳細解釋,會php的基本都能理解,若是看不懂註釋,我解釋你也仍是不懂。親測成功,並且也不會由於淘寶的重複發送異步而累加,由於我有用到了訂單號的判斷,這個訂單號存在就再也不執行業務邏輯,並且也用到了事務,不會單方面更改單個數據。
我用了2個晚上才最終調試成功,開始的時候老是支付寶錢扣款成功,可是個人業務邏輯沒有成功,因而詳細看了一個demo的源代碼,以及看了看官方的視頻教程(和沒看同樣)和網上查看了一些資料,詳細看了一下支付寶的接口驗證流程,和你們分享一下,大神們不要吐槽,這只是我我的的理解。
支付寶流程:
在支付寶的異步通知中notifyurl()方法中調用了new AlipayNotify($alipay_config);這個類的這個$alipayNotify->verifyNotify();方法他是支付寶接口自身的嚴重呢個方法,驗證是否爲支付寶發送來的信息,發送的驗證ID以及是否成功等。
異步調用流程以及驗證詳細:
4.經驗心得:
a.ca證書的路徑問題,剛開始的時候失敗了幾回,因而一步一步echo發現找不到證書,支付寶接口默認使用getcwd()方法,這個方法是找到物理磁盤的路徑,即項目的根目錄。
b.確保服務器開啓curl擴展,異步通知的時候要用到,而且保證可以經過防火牆許可發送證書。
c.同步通知是頁面的跳轉,不能用session,這個頁面只是顯示給用戶看的,我沒有在這個頁面裏寫業務邏輯(由於值這個頁面可能會出現用戶直接關閉等操做,致使沒法完成業務邏輯,而異步通知只要支付寶服務器沒有接收到success就會在24小時內分時間段不停的發送返回結果),因此也沒有驗證時候爲淘寶服務器發送的,由於我根本就沒有在這個方法裏寫任何業務邏輯代碼,只是根據返回的finished作頁面的跳轉。
d.在異步通知中SESSION和COOKIE不能使用,你能夠想象一下,這個頁面是支付寶服務器模擬post數據向你的服務器發送的請求,能用SESSION纔怪呢。並且你也不用擔憂THINKPHP的url模式和路由規則,支付寶是以POST方式發送給你服務器的。這時候你會遇到一個問題,就是你沒法肯定是你網站的哪一個會用在提交支付信息。個人解決方案是在用戶提交以前創建一箇中間表,字段有user_id和out_trade_no(訂單ID)其中out_trade_no爲惟一索引,根據訂單號去找user_id,另外在處理業務邏輯的時候最好在檢查一次這個訂單號是否已經處理了,防止重複執行業務邏輯。