ThinkPHP接入支付寶支付功能

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

一、下載支付寶接口包

下載地址:

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

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

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

二、重新整理接口包文件,這一步應(yīng)該算是比較關(guān)鍵的(個(gè)人認(rèn)為)

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

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

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

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

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

notify_url.php 是服務(wù)器異步通知頁(yè)面文件;

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

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

現(xiàn)在對(duì)以上文件進(jì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");

為什么要去掉以上兩個(gè)文件中的這兩段代碼,因?yàn)樵陧?xiàng)目中調(diào)用接口文件的時(shí)候,我把所有4個(gè)核心文件都通過vendor來進(jìn)行引入。所以,這不再需要導(dǎo)入。

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

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

調(diào)用分兩步:

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

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

'alipay_config'=>array(

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

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

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

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

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

'transport'=>'http',

),

//以上配置項(xiàng),是從接口包中alipay.config.php 文件中復(fù)制過來,進(jìn)行配置;

'alipay'=>array(

//這里是賣家的支付寶賬號(hào),也就是你申請(qǐng)接口時(shí)注冊(cè)的支付寶賬號(hào)

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

//這里是異步通知頁(yè)面url,提交到項(xiàng)目的Pay控制器的notifyurl方法;

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

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

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

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

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

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

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

),

2、新建一個(gè)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']))

);

//建立請(qǐng)求

$alipaySubmit=newAlipaySubmit($alipay_config);

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

echo $html_text;

}

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

服務(wù)器異步通知頁(yè)面方法

其實(shí)這里就是將notify_url.php文件中的代碼復(fù)制過來進(jìn)行處理

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

functionnotifyurl(){

/*

同理去掉以下兩句代碼;

*/

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

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

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

$alipay_config=C('alipay_config');

//計(jì)算得出通知驗(yàn)證結(jié)果

$alipayNotify=newAlipayNotify($alipay_config);

$verify_result=$alipayNotify->verifyNotify();

if($verify_result){

//驗(yàn)證成功

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

$out_trade_no=$_POST['out_trade_no'];//商戶訂單號(hào)

$trade_no=$_POST['trade_no'];//支付寶交易號(hào)

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

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

$notify_id=$_POST['notify_id'];//通知校驗(yàn)ID。

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

$buyer_email=$_POST['buyer_email'];//買家支付寶帳號(hào);

$parameter=array(

"out_trade_no"=>$out_trade_no,//商戶訂單編號(hào);

"trade_no"=>$trade_no,//支付寶交易號(hào);

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

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

"notify_id"=>$notify_id,//通知校驗(yàn)ID。

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

"buyer_email"=>$buyer_email,//買家支付寶帳號(hào);

);

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

//

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

orderhandle($parameter);

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

}

}

echo"success";//請(qǐng)不要修改或刪除

}else{

//驗(yàn)證失敗

echo"fail";

}

}

/*

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

這里其實(shí)就是將return_url.php這個(gè)文件中的代碼復(fù)制過來,進(jìn)行處理;

*/

functionreturnurl(){

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

$alipay_config=C('alipay_config');

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

$verify_result=$alipayNotify->verifyReturn();

if($verify_result){

//驗(yàn)證成功

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

$out_trade_no=$_GET['out_trade_no'];//商戶訂單號(hào)

$trade_no=$_GET['trade_no'];//支付寶交易號(hào)

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

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

$notify_id=$_GET['notify_id'];//通知校驗(yàn)ID。

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

$buyer_email=$_GET['buyer_email'];//買家支付寶帳號(hào);

$parameter=array(

"out_trade_no"=>$out_trade_no,//商戶訂單編號(hào);

"trade_no"=>$trade_no,//支付寶交易號(hào);

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

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

"notify_id"=>$notify_id,//通知校驗(yàn)ID。

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

"buyer_email"=>$buyer_email,//買家支付寶帳號(hào)

);

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

if(!checkorderstatus($out_trade_no)){

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

}

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

}else{

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

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

}

}else{

//驗(yàn)證失敗

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

echo"支付失??!";

}

}

}

?>

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

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

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

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

CREATE TABLE `tb_orderlist` (

`id` int(11) NOT NULL AUTO_INCREMENT,

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

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

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

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

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

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

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

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

`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,支付接口交易號(hào)

`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, //這個(gè)字段不需要的,大家看我西面的修正補(bǔ)充部分的說明!

`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更正

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

具體看我下面的修正補(bǔ)充部分的說明

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

//獲取一個(gè)隨機(jī)且唯一的訂單號(hào);

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é)幾點(diǎn)

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

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

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

4、支付返回后的處理因?yàn)橐诋惒胶吞D(zhuǎn)兩個(gè)方法中都要進(jìn)行相應(yīng)的判斷和處理,所以,把這些判斷和處理寫到一個(gè)自定義函數(shù)中,這樣只要調(diào)用函數(shù)即可,使得代碼更加清晰明了。

5、notify_url和return_url兩種模式的返回url必須使用http://xxxxxxx這樣的絕對(duì)路徑,因?yàn)槔锸菑闹Ц秾毱脚_(tái)返回到你的項(xiàng)目頁(yè)面。不能使用相對(duì)路徑。

以上代碼在ThinkPHP3.0中正常使用?。?/p>

————————修正補(bǔ)充??!2013.08.13——————————

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

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

在此修正!

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

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

解決辦法:

方法1:

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

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