ThinkPHP接入支付寶支付功能

最近做系統(tǒng),需要實現(xiàn)在線支付功能,毫不猶豫,選擇的是支付寶的接口支付功能。這里我用的是即時到帳的接口,具體實現(xiàn)的步驟如下:

一、下載支付寶接口包

下載地址:

https://b.alipay.com/order/productDetail.htm?productId=2012111200373124&tabId=4#ps-tabinfo-hash

具體如何下載,我就不在羅嗦了~~

很多人反映,用支付寶的接口到最后面會出現(xiàn)驗證錯誤。其實,這里需要對接口程序進行一下改造。需要添加幾個自定義函數(shù)。為了讓大家以后避免出現(xiàn)同樣的問題,我把我改造好的支付寶接口程序上傳了(Alipay)。大家可以下載下來,解壓后放到框架的Vendor目錄中即可~

二、重新整理接口包文件,這一步應該算是比較關鍵的(個人認為)

下載下來的接口包文件有很多語言的源碼,

我們選擇 create_direct_pay_by_user-PHP-UTF-8 這個名稱的接口文件。里面包括如下文件:

images文件里是支付寶相關的一些標志的圖片,我們暫不管他,lib文件很重要,是整個接口的核心類文件;

alipay.config.php是相關參數(shù)的配置文件

alipayapi.php 是支付寶接口入口文件

notify_url.php 是服務器異步通知頁面文件;

return_url.php 是頁面跳轉(zhuǎn)同步通知文件;

在ThinkPHP的框架文件下,找到Extend 進入,再進入Vendor,在Vendor文件夾下,新建文件夾Alipay,把支付寶作為第三方類庫引入。然后,復制支付寶接口文件包中l(wèi)ib文件里的所有文件。一共4個文件,如下:

現(xiàn)在對以上文件進行重命名,

alipay_core.function.php重命名為:Corefunction.php;

alipay_md5.function.php重命名為:Md5function.php;

alipay_notify.class.php重命名為:Notify.php;

alipay_submit.class.php重命名為:Submit.php;

然后,打開Submit.php文件,把以下代碼去掉;

require_once("alipay_core.function.php");

require_once("alipay_md5.function.php");

同樣,打開Notify.php文件,把以下兩段代碼去掉

require_once("alipay_core.function.php");

require_once("alipay_md5.function.php");

為什么要去掉以上兩個文件中的這兩段代碼,因為在項目中調(diào)用接口文件的時候,我把所有4個核心文件都通過vendor來進行引入。所以,這不再需要導入。

到此,支付寶接口包相關核心類庫的整理基本完成。現(xiàn)在開始在項目中調(diào)用;

三、在項目中調(diào)用支付寶接口

調(diào)用分兩步:

1、在配置文件中Conf/Config.php文件中對支付寶相關參數(shù)進行配置:

//支付寶配置參數(shù)

'alipay_config'=>array(

'partner'=>'20********50',//這里是你在成功申請支付寶接口后獲取到的PID;

'key'=>'9t***********ie',//這里是你在成功申請支付寶接口后獲取到的Key

'sign_type'=>strtoupper('MD5'),

'input_charset'=>strtolower('utf-8'),

'cacert'=>getcwd().'\\cacert.pem',

'transport'=>'http',

),

//以上配置項,是從接口包中alipay.config.php 文件中復制過來,進行配置;

'alipay'=>array(

//這里是賣家的支付寶賬號,也就是你申請接口時注冊的支付寶賬號

'seller_email'=>'pay@xxx.com',

//這里是異步通知頁面url,提交到項目的Pay控制器的notifyurl方法;

'notify_url'=>'http://www.xxx.com/Pay/notifyurl',

//這里是頁面跳轉(zhuǎn)通知url,提交到項目的Pay控制器的returnurl方法;

'return_url'=>'http://www.xxx.com/Pay/returnurl',

//支付成功跳轉(zhuǎn)到的頁面,我這里跳轉(zhuǎn)到項目的User控制器,myorder方法,并傳參payed(已支付列表)

'successpage'=>'User/myorder?ordtype=payed',

//支付失敗跳轉(zhuǎn)到的頁面,我這里跳轉(zhuǎn)到項目的User控制器,myorder方法,并傳參unpay(未支付列表)

'errorpage'=>'User/myorder?ordtype=unpay',

),

2、新建一個PayAction控制器代碼如下:

"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=newAlipaySubmit($alipay_config);

$html_text=$alipaySubmit->buildRequestForm($parameter,"post","確認");

echo $html_text;

}

/******************************

服務器異步通知頁面方法

其實這里就是將notify_url.php文件中的代碼復制過來進行處理

*******************************/

functionnotifyurl(){

/*

同理去掉以下兩句代碼;

*/

//require_once("alipay.config.php");

//require_once("lib/alipay_notify.class.php");

//這里還是通過C函數(shù)來讀取配置項,賦值給$alipay_config

$alipay_config=C('alipay_config');

//計算得出通知驗證結(jié)果

$alipayNotify=newAlipayNotify($alipay_config);

$verify_result=$alipayNotify->verifyNotify();

if($verify_result){

//驗證成功

//獲取支付寶的通知返回參數(shù),可參考技術文檔中服務器異步通知參數(shù)列表

$out_trade_no=$_POST['out_trade_no'];//商戶訂單號

$trade_no=$_POST['trade_no'];//支付寶交易號

$trade_status=$_POST['trade_status'];//交易狀態(tài)

$total_fee=$_POST['total_fee'];//交易金額

$notify_id=$_POST['notify_id'];//通知校驗ID。

$notify_time=$_POST['notify_time'];//通知的發(fā)送時間。格式為yyyy-MM-dd HH:mm:ss。

$buyer_email=$_POST['buyer_email'];//買家支付寶帳號;

$parameter=array(

"out_trade_no"=>$out_trade_no,//商戶訂單編號;

"trade_no"=>$trade_no,//支付寶交易號;

"total_fee"=>$total_fee,//交易金額;

"trade_status"=>$trade_status,//交易狀態(tài)

"notify_id"=>$notify_id,//通知校驗ID。

"notify_time"=>$notify_time,//通知的發(fā)送時間。

"buyer_email"=>$buyer_email,//買家支付寶帳號;

);

if($_POST['trade_status']=='TRADE_FINISHED'){

//

}elseif($_POST['trade_status']=='TRADE_SUCCESS'){if(!checkorderstatus($out_trade_no)){

orderhandle($parameter);

//進行訂單處理,并傳送從支付寶返回的參數(shù);

}

}

echo"success";//請不要修改或刪除

}else{

//驗證失敗

echo"fail";

}

}

/*

頁面跳轉(zhuǎn)處理方法;

這里其實就是將return_url.php這個文件中的代碼復制過來,進行處理;

*/

functionreturnurl(){

//頭部的處理跟上面兩個方法一樣,這里不羅嗦了!

$alipay_config=C('alipay_config');

$alipayNotify=newAlipayNotify($alipay_config);//計算得出通知驗證結(jié)果

$verify_result=$alipayNotify->verifyReturn();

if($verify_result){

//驗證成功

//獲取支付寶的通知返回參數(shù),可參考技術文檔中頁面跳轉(zhuǎn)同步通知參數(shù)列表

$out_trade_no=$_GET['out_trade_no'];//商戶訂單號

$trade_no=$_GET['trade_no'];//支付寶交易號

$trade_status=$_GET['trade_status'];//交易狀態(tài)

$total_fee=$_GET['total_fee'];//交易金額

$notify_id=$_GET['notify_id'];//通知校驗ID。

$notify_time=$_GET['notify_time'];//通知的發(fā)送時間。

$buyer_email=$_GET['buyer_email'];//買家支付寶帳號;

$parameter=array(

"out_trade_no"=>$out_trade_no,//商戶訂單編號;

"trade_no"=>$trade_no,//支付寶交易號;

"total_fee"=>$total_fee,//交易金額;

"trade_status"=>$trade_status,//交易狀態(tài)

"notify_id"=>$notify_id,//通知校驗ID。

"notify_time"=>$notify_time,//通知的發(fā)送時間。

"buyer_email"=>$buyer_email,//買家支付寶帳號

);

if($_GET['trade_status']=='TRADE_FINISHED'||$_GET['trade_status']=='TRADE_SUCCESS'){

if(!checkorderstatus($out_trade_no)){

orderhandle($parameter);//進行訂單處理,并傳送從支付寶返回的參數(shù);

}

$this->redirect(C('alipay.successpage'));//跳轉(zhuǎn)到配置項中配置的支付成功頁面;

}else{

echo"trade_status=".$_GET['trade_status'];

$this->redirect(C('alipay.errorpage'));//跳轉(zhuǎn)到配置項中配置的支付失敗頁面;

}

}else{

//驗證失敗

//如要調(diào)試,請看alipay_notify.php頁面的verifyReturn函數(shù)

echo"支付失敗!";

}

}

}

?>

3、這里有幾個支付處理過程中需要用到的函數(shù),我把這些函數(shù)寫到了項目的Common/common.php中,這樣不用手動調(diào)用,即可直接使用這些函數(shù),代碼如下:

//////////////////////////////////////////////////////

//Orderlist數(shù)據(jù)表,用于保存用戶的購買訂單記錄;

/* Orderlist數(shù)據(jù)表結(jié)構(gòu);

CREATE TABLE `tb_orderlist` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`userid` int(11) DEFAULT NULL,購買者userid

`username` varchar(255) DEFAULT NULL,購買者姓名

`ordid` varchar(255) DEFAULT NULL,訂單號

`ordtime` int(11) DEFAULT NULL,訂單時間

`productid` int(11) DEFAULT NULL,產(chǎn)品ID

`ordtitle` varchar(255) DEFAULT NULL,訂單標題

`ordbuynum` int(11) DEFAULT '0',購買數(shù)量

`ordprice` float(10,2) DEFAULT '0.00',產(chǎn)品單價

`ordfee` float(10,2) DEFAULT '0.00',訂單總金額

`ordstatus` int(11) DEFAULT '0',訂單狀態(tài)

`payment_type` varchar(255) DEFAULT NULL,支付類型

`payment_trade_no` varchar(255) DEFAULT NULL,支付接口交易號

`payment_trade_status` varchar(255) DEFAULT NULL,支付接口返回的交易狀態(tài)

`payment_notify_id` varchar(255) DEFAULT NULL,

`payment_notify_time` varchar(255) DEFAULT NULL,

`payment_buyer_email` varchar(255) DEFAULT NULL,

`ordcode` varchar(255) DEFAULT NULL, //這個字段不需要的,大家看我西面的修正補充部分的說明!

`isused` int(11) DEFAULT '0',

`usetime` int(11) DEFAULT NULL,

`checkuser` int(11) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

*/

//在線交易訂單支付處理函數(shù)

//函數(shù)功能:根據(jù)支付接口傳回的數(shù)據(jù)判斷該訂單是否已經(jīng)支付成功;

//返回值:如果訂單已經(jīng)成功支付,返回true,否則返回false;

functioncheckorderstatus($ordid){

$Ord=M('Orderlist');

$ordstatus=$Ord->where('ordid='.$ordid)->getField('ordstatus');

if($ordstatus==1){

returntrue;

}else{

returnfalse;

}

}

//處理訂單函數(shù)

//更新訂單狀態(tài),寫入訂單支付后返回的數(shù)據(jù)

functionorderhandle($parameter){

$ordid=$parameter['out_trade_no'];

$data['payment_trade_no']=$parameter['trade_no'];

$data['payment_trade_status']=$parameter['trade_status'];

$data['payment_notify_id']=$parameter['notify_id'];

$data['payment_notify_time']=$parameter['notify_time'];

$data['payment_buyer_email']=$parameter['buyer_email'];

$data['ordstatus']=1;

$Ord=M('Orderlist');

$Ord->where('ordid='.$ordid)->save($data);

}

/*-----------------------------------

2013.8.13更正

下面這個函數(shù),其實不需要,大家可以把他刪掉,

具體看我下面的修正補充部分的說明

------------------------------------*/

//獲取一個隨機且唯一的訂單號;

functiongetordcode(){

$Ord=M('Orderlist');

$numbers=range(10,99);

shuffle($numbers);

$code=array_slice($numbers,0,4);

$ordcode=$code[0].$code[1].$code[2].$code[3];

$oldcode=$Ord->where("ordcode='".$ordcode."'")->getField('ordcode');

if($oldcode){

getordcode();

}else{

return$ordcode;

}

}

四、總結(jié)幾點

1、接口包中l(wèi)ib文件中的文件復制到Vendor后,重命名為TP規(guī)范的命名規(guī)則,為的是調(diào)用方便,當然你要改成其他名稱也可以;

2、把執(zhí)行支付操作(doalipay),處理異步返回結(jié)果(notifyurl),處理跳轉(zhuǎn)返回結(jié)果(returnurl)三個支付接口的核心頁面寫到一個PayAction控制器中。

3、提交支付的頁面中,可以在提交之前先把一些參數(shù)要傳遞的內(nèi)容先通過隱藏域的方法組合好,比如金額先計算好,訂單名稱,訂單描述等先用字符串組合好。然后提交表單,這樣,在doalipay方法中只要直接構(gòu)造傳遞參數(shù),直接進行提交就行過了。

4、支付返回后的處理因為要在異步和跳轉(zhuǎn)兩個方法中都要進行相應的判斷和處理,所以,把這些判斷和處理寫到一個自定義函數(shù)中,這樣只要調(diào)用函數(shù)即可,使得代碼更加清晰明了。

5、notify_url和return_url兩種模式的返回url必須使用http://xxxxxxx這樣的絕對路徑,因為里是從支付寶平臺返回到你的項目頁面。不能使用相對路徑。

以上代碼在ThinkPHP3.0中正常使用!!

————————修正補充!!2013.08.13——————————

在第三部分中Orderlist數(shù)據(jù)表結(jié)構(gòu)中,我有一個字段是OrdCode,這個字段是我系統(tǒng)中用來發(fā)送短信給客戶的消費密碼,也就是客戶憑手機短信來消費時就要驗證這個字段。

其實,大家在做系統(tǒng)的時候,可以把這個字段忽略,可以不用他。代碼最后部分中,有一個獲取一個隨機且唯一的訂單號的函數(shù) getordcode(),這里我其實寫錯了,不是獲取訂單號,是ordcode,也就是消費密碼,這個函數(shù)也不需要。系統(tǒng)中的訂單號(ordid字段),我用的是時間戳。

在此修正!

——————–解決簽名錯誤問題 修正 13-08-16————————

有人說在在調(diào)試時,簽名出現(xiàn)無法通過的問題,產(chǎn)生問題的原因是在返回的URL地址中返回的參數(shù)中,可能存在__URL__這樣的字符串。導致無法正確過濾參數(shù)。

解決辦法:

方法1:

在向支付寶提交需要的參數(shù)時,不要使用__URL__,__PUBLIC__等TP中的模版替換變量,如果TP對這些變量解析不成功,會直接傳遞過去,所以,在這些地方直接使用原始的URL地址。

方法2:

在接口的Core文件中,加入改造后的過濾函數(shù),如下:

/**

* 除去數(shù)組中的空值和簽名參數(shù)

* @param $para 簽名參數(shù)組

* return 去掉空值與簽名參數(shù)后的新簽名參數(shù)組

*/

functionparaFilter($para){

$para_filter=array();

while(list($key,$val)=each($para)){

if($key=="sign"||$key=="sign_type"||$key=='_URL_'||$val=="")continue;//添加了$key == '_URL_'

else$para_filter[$key]=$para[$key];

}

return$para_filter;

}

functionmyparaFilter($para){

$para_filter=array();

while(list($key,$val)=each($para)){

if($key=='_URL_')continue;

else$para_filter[$key]=$para[$key];

}

return$para_filter;

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容