ThinkPHP实现支付宝接口功能
最近做系统,需要实现在线支付功能,毫不犹豫,选择的是支付宝的接口支付功能。这里我用的是即时到帐的接口,具体实现的步骤如下:
一、下载支付宝接口包
下载地址:
https://b.alipay.com/order/productDetail.htm?productId=2012111200373124&tabId=4#ps-tabinfo-hash
具体如何下载,我就不在罗嗦了~~
很多人反映,用支付宝的接口到最后面会出现验证错误。其实,这里需要对接口程序进行一下改造。需要添加几个自定义函数。为了让大家以后避免出现同样的问题,我把我改造好的支付宝接口程序上传了(--> 猛戳这里下载附件)。大家可以下载下来,解压后放到框架的Vendor目录中即可~
二、重新整理接口包文件,这一步应该算是比较关键的(个人认为)
下载下来的接口包文件有很多语言的源码,
我们选择 create_direct_pay_by_user-PHP-UTF-8 这个名称的接口文件。里面包括如下文件:
images文件里是支付宝相关的一些标志的图片,我们暂不管他,lib文件很重要,是整个接口的核心类文件;
alipay.config.php是相关参数的配置文件
alipayapi.php 是支付宝接口入口文件
notify_url.php 是服务器异步通知页面文件;
return_url.php 是页面跳转同步通知文件;
在ThinkPHP的框架文件下,找到Extend 进入,再进入Vendor,在Vendor文件夹下,新建文件夹Alipay,把支付宝作为第三方类库引入。然后,复制支付宝接口文件包中lib文件里的所有文件。一共4个文件,如下:
现在对以上文件进行重命名,
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");<br>
require_once("alipay_md5.function.php");
同样,打开Notify.php文件,把以下两段代码去掉require_once("alipay_core.function.php");<br>
require_once("alipay_md5.function.php");
为什么要去掉以上两个文件中的这两段代码,因为在项目中调用接口文件的时候,我把所有4个核心文件都通过vendor来进行引入。所以,这不再需要导入。
到此,支付宝接口包相关核心类库的整理基本完成。现在开始在项目中调用;
三、在项目中调用支付宝接口
调用分两步:
1、在配置文件中Conf/Config.php文件中对支付宝相关参数进行配置://支付宝配置参数<br>
'alipay_config'=>array(<br>
'partner' =>'20********50', //这里是你在成功申请支付宝接口后获取到的PID;<br>
'key'=>'9t***********ie',//这里是你在成功申请支付宝接口后获取到的Key<br>
'sign_type'=>strtoupper('MD5'),<br>
'input_charset'=> strtolower('utf-8'),<br>
'cacert'=> getcwd().'\\cacert.pem',<br>
'transport'=> 'http',<br>
),<br>
//以上配置项,是从接口包中alipay.config.php 文件中复制过来,进行配置;<br>
<br>
'alipay' =>array(<br>
//这里是卖家的支付宝账号,也就是你申请接口时注册的支付宝账号<br>
'seller_email'=>'pay@xxx.com',<br>
<br>
//这里是异步通知页面url,提交到项目的Pay控制器的notifyurl方法;<br>
'notify_url'=>'http://www.xxx.com/Pay/notifyurl', <br>
<br>
//这里是页面跳转通知url,提交到项目的Pay控制器的returnurl方法;<br>
'return_url'=>'http://www.xxx.com/Pay/returnurl',<br>
<br>
//支付成功跳转到的页面,我这里跳转到项目的User控制器,myorder方法,并传参payed(已支付列表)<br>
'successpage'=>'User/myorder?ordtype=payed', <br>
<br>
//支付失败跳转到的页面,我这里跳转到项目的User控制器,myorder方法,并传参unpay(未支付列表)<br>
'errorpage'=>'User/myorder?ordtype=unpay', <br>
),
2、新建一个PayAction控制器代码如下:<?php <br />
class PayAction extends Action{<br>
//在类初始化方法中,引入相关类库 <br>
public function _initialize() {<br>
vendor('Alipay.Corefunction');<br>
vendor('Alipay.Md5function');<br>
vendor('Alipay.Notify');<br>
vendor('Alipay.Submit'); <br>
}<br>
<br>
//doalipay方法<br>
/*该方法其实就是将接口文件包下alipayapi.php的内容复制过来<br>
然后进行相关处理<br>
*/<br>
public function doalipay(){<br>
/*********************************************************<br>
把alipayapi.php中复制过来的如下两段代码去掉,<br>
第一段是引入配置项,<br>
第二段是引入submit.class.php这个类。<br>
<b>为什么要去掉??</b><br>
第一,配置项的内容已经在项目的Config.php文件中进行了配置,我们只需用C函数进行调用即可;<br>
第二,这里调用的submit.class.php类库我们已经在PayAction的_initialize()中已经引入;所以这里不再需要;<br>
*****************************************************/<br>
// require_once("alipay.config.php");<br>
// require_once("lib/alipay_submit.class.php");<br>
<br>
//这里我们通过TP的C函数把配置项参数读出,赋给$alipay_config;<br>
$alipay_config=C('alipay_config'); <br>
<br>
/**************************请求参数**************************/<br>
<br>
$payment_type = "1"; //支付类型 //必填,不能修改<br>
$notify_url = C('alipay.notify_url'); //服务器异步通知页面路径<br>
$return_url = C('alipay.return_url'); //页面跳转同步通知页面路径<br>
$seller_email = C('alipay.seller_email');//卖家支付宝帐户必填<br>
$out_trade_no = $_POST['trade_no'];//商户订单号 通过支付页面的表单进行传递,注意要唯一!<br>
$subject = $_POST['ordsubject']; //订单名称 //必填 通过支付页面的表单进行传递<br>
$total_fee = $_POST['ordtotal_fee']; //付款金额 //必填 通过支付页面的表单进行传递<br>
$body = $_POST['ordbody']; //订单描述 通过支付页面的表单进行传递<br>
$show_url = $_POST['ordshow_url']; //商品展示地址 通过支付页面的表单进行传递<br>
$anti_phishing_key = "";//防钓鱼时间戳 //若要使用请调用类文件submit中的query_timestamp函数<br>
$exter_invoke_ip = get_client_ip(); //客户端的IP地址 <br>
/************************************************************/<br>
<br>
//构造要请求的参数数组,无需改动<br>
$parameter = array(<br>
"service" => "create_direct_pay_by_user",<br>
"partner" => trim($alipay_config['partner']),<br>
"payment_type" => $payment_type,<br>
"notify_url" => $notify_url,<br>
"return_url" => $return_url,<br>
"seller_email" => $seller_email,<br>
"out_trade_no" => $out_trade_no,<br>
"subject" => $subject,<br>
"total_fee" => $total_fee,<br>
"body" => $body,<br>
"show_url" => $show_url,<br>
"anti_phishing_key" => $anti_phishing_key,<br>
"exter_invoke_ip" => $exter_invoke_ip,<br>
"_input_charset" => trim(strtolower($alipay_config['input_charset']))<br>
);<br>
//建立请求<br>
$alipaySubmit = new AlipaySubmit($alipay_config);<br>
$html_text = $alipaySubmit->buildRequestForm($parameter,"post", "确认");<br>
echo $html_text;<br>
}<br>
<br>
<br>
/******************************<br>
服务器异步通知页面方法<br>
其实这里就是将notify_url.php文件中的代码复制过来进行处理<br>
<br>
*******************************/<br>
function notifyurl(){<br>
/*<br>
同理去掉以下两句代码;<br>
*/ <br>
//require_once("alipay.config.php");<br>
//require_once("lib/alipay_notify.class.php");<br>
<br>
//这里还是通过C函数来读取配置项,赋值给$alipay_config<br>
$alipay_config=C('alipay_config');<br>
<br>
//计算得出通知验证结果<br>
$alipayNotify = new AlipayNotify($alipay_config);<br>
$verify_result = $alipayNotify->verifyNotify();<br>
<br>
if($verify_result) {<br>
//验证成功<br>
//获取支付宝的通知返回参数,可参考技术文档中服务器异步通知参数列表<br>
$out_trade_no = $_POST['out_trade_no']; //商户订单号<br>
$trade_no = $_POST['trade_no']; //支付宝交易号<br>
$trade_status = $_POST['trade_status']; //交易状态<br>
$total_fee = $_POST['total_fee']; //交易金额<br>
$notify_id = $_POST['notify_id']; //通知校验ID。<br>
$notify_time = $_POST['notify_time']; //通知的发送时间。格式为yyyy-MM-dd HH:mm:ss。<br>
$buyer_email = $_POST['buyer_email']; //买家支付宝帐号;<br>
$parameter = array(<br>
"out_trade_no" => $out_trade_no, //商户订单编号;<br>
"trade_no" => $trade_no, //支付宝交易号;<br>
"total_fee" => $total_fee, //交易金额;<br>
"trade_status" => $trade_status, //交易状态<br>
"notify_id" => $notify_id, //通知校验ID。<br>
"notify_time" => $notify_time, //通知的发送时间。<br>
"buyer_email" => $buyer_email, //买家支付宝帐号;<br>
);<br>
if($_POST['trade_status'] == 'TRADE_FINISHED') {<br>
//<br>
}else if ($_POST['trade_status'] == 'TRADE_SUCCESS') { if(!checkorderstatus($out_trade_no)){<br>
orderhandle($parameter); <br>
//进行订单处理,并传送从支付宝返回的参数;<br>
}<br>
}<br>
echo "success"; //请不要修改或删除<br>
}else {<br>
//验证失败<br>
echo "fail";<br>
} <br>
}<br>
<br>
/*<br>
页面跳转处理方法;<br>
这里其实就是将return_url.php这个文件中的代码复制过来,进行处理; <br>
*/<br>
function returnurl(){<br>
//头部的处理跟上面两个方法一样,这里不罗嗦了!<br>
$alipay_config=C('alipay_config');<br>
$alipayNotify = new AlipayNotify($alipay_config);//计算得出通知验证结果<br>
$verify_result = $alipayNotify->verifyReturn();<br>
if($verify_result) {<br>
//验证成功<br>
//获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表<br>
$out_trade_no = $_GET['out_trade_no']; //商户订单号<br>
$trade_no = $_GET['trade_no']; //支付宝交易号<br>
$trade_status = $_GET['trade_status']; //交易状态<br>
$total_fee = $_GET['total_fee']; //交易金额<br>
$notify_id = $_GET['notify_id']; //通知校验ID。<br>
$notify_time = $_GET['notify_time']; //通知的发送时间。<br>
$buyer_email = $_GET['buyer_email']; //买家支付宝帐号;<br>
<br>
$parameter = array(<br>
"out_trade_no" => $out_trade_no, //商户订单编号;<br>
"trade_no" => $trade_no, //支付宝交易号;<br>
"total_fee" => $total_fee, //交易金额;<br>
"trade_status" => $trade_status, //交易状态<br>
"notify_id" => $notify_id, //通知校验ID。<br>
"notify_time" => $notify_time, //通知的发送时间。<br>
"buyer_email" => $buyer_email, //买家支付宝帐号<br>
);<br>
<br>
if($_GET['trade_status'] == 'TRADE_FINISHED' || $_GET['trade_status'] == 'TRADE_SUCCESS') {<br>
if(!checkorderstatus($out_trade_no)){<br>
orderhandle($parameter); //进行订单处理,并传送从支付宝返回的参数;<br>
}<br>
$this->redirect(C('alipay.successpage'));//跳转到配置项中配置的支付成功页面;<br>
}else {<br>
echo "trade_status=".$_GET['trade_status'];<br>
$this->redirect(C('alipay.errorpage'));//跳转到配置项中配置的支付失败页面;<br>
}<br>
}else {<br>
//验证失败<br>
//如要调试,请看alipay_notify.php页面的verifyReturn函数<br>
echo "支付失败!";<br>
}<br>
}<br>
}<br>
?>
3、这里有几个支付处理过程中需要用到的函数,我把这些函数写到了项目的Common/common.php中,这样不用手动调用,即可直接使用这些函数,代码如下://////////////////////////////////////////////////////<br>
//Orderlist数据表,用于保存用户的购买订单记录;<br>
/* Orderlist数据表结构;<br>
CREATE TABLE `tb_orderlist` (<br>
`id` int(11) NOT NULL AUTO_INCREMENT,<br>
`userid` int(11) DEFAULT NULL,购买者userid<br>
`username` varchar(255) DEFAULT NULL,购买者姓名<br>
`ordid` varchar(255) DEFAULT NULL,订单号<br>
`ordtime` int(11) DEFAULT NULL,订单时间<br>
`productid` int(11) DEFAULT NULL,产品ID<br>
`ordtitle` varchar(255) DEFAULT NULL,订单标题<br>
`ordbuynum` int(11) DEFAULT '0',购买数量<br>
`ordprice` float(10,2) DEFAULT '0.00',产品单价<br>
`ordfee` float(10,2) DEFAULT '0.00',订单总金额<br>
`ordstatus` int(11) DEFAULT '0',订单状态<br>
`payment_type` varchar(255) DEFAULT NULL,支付类型<br>
`payment_trade_no` varchar(255) DEFAULT NULL,支付接口交易号<br>
`payment_trade_status` varchar(255) DEFAULT NULL,支付接口返回的交易状态<br>
`payment_notify_id` varchar(255) DEFAULT NULL,<br>
`payment_notify_time` varchar(255) DEFAULT NULL,<br>
`payment_buyer_email` varchar(255) DEFAULT NULL,<br>
`ordcode` varchar(255) DEFAULT NULL, //这个字段不需要的,大家看我西面的修正补充部分的说明!<br>
`isused` int(11) DEFAULT '0',<br>
`usetime` int(11) DEFAULT NULL,<br>
`checkuser` int(11) DEFAULT NULL,<br>
PRIMARY KEY (`id`)<br>
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;<br>
<br>
*/<br>
//在线交易订单支付处理函数<br>
//函数功能:根据支付接口传回的数据判断该订单是否已经支付成功;<br>
//返回值:如果订单已经成功支付,返回true,否则返回false;<br>
function checkorderstatus($ordid){<br>
$Ord=M('Orderlist');<br>
$ordstatus=$Ord->where('ordid='.$ordid)->getField('ordstatus');<br>
if($ordstatus==1){<br>
return true;<br>
}else{<br>
return false; <br>
}<br>
}<br>
<br>
//处理订单函数<br>
//更新订单状态,写入订单支付后返回的数据<br>
function orderhandle($parameter){<br>
$ordid=$parameter['out_trade_no'];<br>
$data['payment_trade_no'] =$parameter['trade_no'];<br>
$data['payment_trade_status'] =$parameter['trade_status'];<br>
$data['payment_notify_id'] =$parameter['notify_id'];<br>
$data['payment_notify_time'] =$parameter['notify_time'];<br>
$data['payment_buyer_email'] =$parameter['buyer_email'];<br>
$data['ordstatus'] =1;<br>
$Ord=M('Orderlist');<br>
$Ord->where('ordid='.$ordid)->save($data);<br>
} <br>
<br>
<br>
<b><br>
/*-----------------------------------<br>
2013.8.13更正<br>
下面这个函数,其实不需要,大家可以把他删掉,<br>
具体看我下面的修正补充部分的说明<br>
------------------------------------*/<br>
</b><br>
//获取一个随机且唯一的订单号;<br>
function getordcode(){<br>
$Ord=M('Orderlist');<br>
$numbers = range (10,99);<br>
shuffle ($numbers); <br>
$code=array_slice($numbers,0,4); <br>
$ordcode=$code[0].$code[1].$code[2].$code[3];<br>
$oldcode=$Ord->where("ordcode='".$ordcode."'")->getField('ordcode');<br>
if($oldcode){<br>
getordcode();<br>
}else{<br>
return $ordcode;<br>
}<br>
}
四、总结几点
1、接口包中lib文件中的文件复制到Vendor后,重命名为TP规范的命名规则,为的是调用方便,当然你要改成其他名称也可以;
2、把执行支付操作(doalipay),处理异步返回结果(notifyurl),处理跳转返回结果(returnurl)三个支付接口的核心页面写到一个PayAction控制器中。
3、提交支付的页面中,可以在提交之前先把一些参数要传递的内容先通过隐藏域的方法组合好,比如金额先计算好,订单名称,订单描述等先用字符串组合好。然后提交表单,这样,在doalipay方法中只要直接构造传递参数,直接进行提交就行过了。
4、支付返回后的处理因为要在异步和跳转两个方法中都要进行相应的判断和处理,所以,把这些判断和处理写到一个自定义函数中,这样只要调用函数即可,使得代码更加清晰明了。
5、notify_url和return_url两种模式的返回url必须使用http://xxxxxxx这样的绝对路径,因为里是从支付宝平台返回到你的项目页面。不能使用相对路径。
以上代码在ThinkPHP3.0中正常使用!!
------------------------修正补充!!2013.08.13------------------------------
在第三部分中Orderlist数据表结构中,我有一个字段是OrdCode,这个字段是我系统中用来发送短信给客户的消费密码,也就是客户凭手机短信来消费时就要验证这个字段。
其实,大家在做系统的时候,可以把这个字段忽略,可以不用他。代码最后部分中,有一个获取一个随机且唯一的订单号的函数 getordcode(),这里我其实写错了,不是获取订单号,是ordcode,也就是消费密码,这个函数也不需要。系统中的订单号(ordid字段),我用的是时间戳。
在此修正!
--------------------解决签名错误问题 修正 13-08-16------------------------
有人说在在调试时,签名出现无法通过的问题,产生问题的原因是在返回的URL地址中返回的参数中,可能存在__URL__这样的字符串。导致无法正确过滤参数。
解决办法:
方法1:
在向支付宝提交需要的参数时,不要使用__URL__,__PUBLIC__等TP中的模版替换变量,如果TP对这些变量解析不成功,会直接传递过去,所以,在这些地方直接使用原始的URL地址。
方法2:
在接口的Core文件中,加入改造后的过滤函数,如下:/**<br>
* 除去数组中的空值和签名参数<br>
* @param $para 签名参数组<br>
* return 去掉空值与签名参数后的新签名参数组<br>
*/<br>
function paraFilter($para) {<br>
$para_filter = array();<br>
while (list ($key, $val) = each ($para)) {<br>
if($key == "sign" || $key == "sign_type" || $key == '_URL_' || $val == "")continue; //添加了$key == '_URL_' <br>
else $para_filter[$key] = $para[$key];<br>
}<br>
return $para_filter;<br>
}<br>
<br>
function myparaFilter($para) {<br>
$para_filter = array();<br>
while (list ($key, $val) = each ($para)) {<br>
if($key == '_URL_')continue;<br>
else $para_filter[$key] = $para[$key];<br>
}<br>
return $para_filter;<br>
}
=============================================
作为一个程序猿,分享一直是我秉承的理念,坚持开源和免费,与大家在WEB开发方面共同进步!
如果您对我的成果表示认同并且觉得对你有所帮助,您可以对我提供捐赠^_^。
支付宝捐赠:
用手机扫描二位码支付
谢谢您的支持!!
Alipay.zip
( 9.83 KB 下载:9413 次 )
AD:真正免费,域名+虚机+企业邮箱=0元

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











入門コード例で Python プログラミングについて学ぶ Python は、習得が簡単でありながら強力なプログラミング言語です。初心者にとって、Python プログラミングの入門コード例を理解することは非常に重要です。この記事では、すぐに始めるのに役立つ具体的なコード例をいくつか紹介します。 Print HelloWorldprint("HelloWorld") これは、Python の最も単純なコード例です。 print()関数は、指定された内容を出力するために使用されます。

「Go 言語プログラミングの例: Web 開発におけるコード例」 インターネットの急速な発展に伴い、Web 開発はさまざまな業界で不可欠な部分になりました。 Go 言語は、強力な機能と優れたパフォーマンスを備えたプログラミング言語として、Web 開発の開発者にますます好まれています。この記事では、読者が Go 言語をより深く理解し、Go 言語を使用して独自の Web アプリケーションを構築できるように、具体的なコード例を通じて Web 開発に Go 言語を使用する方法を紹介します。 1. シンプルな HTTP サーバー まず、

PHP 変数はプログラムの実行中に値を保存し、動的でインタラクティブな WEB アプリケーションを構築するために重要です。この記事では、PHP 変数を詳しく説明し、10 個の実際の例を使用して実際に動作する様子を示します。 1. ユーザー入力の保存 $username=$_POST["username"];$passWord=$_POST["password"]; この例では、フォーム送信からユーザー名とパスワードを抽出し、その後の処理のために変数に保存します。 2. 構成値 $database_host="localhost";$database_username="username";$database_pa を設定します。

PHP を使用して在庫管理システムの在庫管理機能コードを記述する方法 在庫管理は多くの企業にとって不可欠な部分です。複数の倉庫を持つ企業にとって、在庫管理機能は特に重要です。在庫を適切に管理および追跡することで、企業は異なる倉庫間で在庫を割り当て、運用コストを最適化し、コラボレーション効率を向上させることができます。この記事では、PHP を使用して在庫倉庫管理機能のコードを記述する方法を紹介し、関連するコード例を示します。 1. 在庫倉庫管理機能のコードを書き始める前に、データベースを確立します。

Java バブル ソートの最も簡単なコード例 バブル ソートは一般的な並べ替えアルゴリズムであり、その基本的な考え方は、並べ替える順序を、隣接する要素の比較と交換を通じて順序付けされた順序に徐々に調整することです。次に、バブル ソートの実装方法を示す簡単な Java コード例を示します。 publicclassBubbleSort{publicstaticvoidbubbleSort(int[]arr){int

Huawei クラウド エッジ コンピューティング相互接続ガイド: インターフェイスを迅速に実装するための Java コード サンプル IoT テクノロジーの急速な発展とエッジ コンピューティングの台頭により、ますます多くの企業がエッジ コンピューティングのアプリケーションに注目し始めています。 Huawei Cloud はエッジ コンピューティング サービスを提供し、企業に信頼性の高いコンピューティング リソースと便利な開発環境を提供し、エッジ コンピューティング アプリケーションの実装を容易にします。この記事では、Java コードを通じて Huawei Cloud エッジ コンピューティング インターフェイスを迅速に実装する方法を紹介します。まずは開発環境を準備する必要があります。 Java Development Kit がインストールされていることを確認してください (

タイトル: 初心者から熟練者まで: Go 言語で一般的に使用されるデータ構造のコード実装 データ構造はプログラミングにおいて重要な役割を果たし、プログラミングの基礎です。 Go 言語には、一般的に使用されるデータ構造が多数あり、これらのデータ構造の実装をマスターすることは、優れたプログラマーになるために重要です。この記事では、Go 言語で一般的に使用されるデータ構造を紹介し、読者がこれらのデータ構造を使い始めて習熟するのに役立つ、対応するコード例を示します。 1. 配列 配列は基本的なデータ構造であり、同じ型のグループです。

Java 選択ソート方法のコード記述ガイドと例 選択ソートは、シンプルで直観的なソート アルゴリズムです。そのアイデアは、ソートされていない要素から毎回最小 (または最大) の要素を選択し、すべての要素がソートされるまで交換することです。この記事では、選択項目の並べ替えのためのコード作成ガイドを提供し、特定の Java サンプル コードを添付します。アルゴリズム原理 選択ソートの基本原理は、ソート対象の配列をソート済み部分とソートされていない部分の 2 つの部分に分割し、その都度、未ソート部分から最小 (または最大) の要素が選択され、ソート済み部分の最後に配置されます。上記を繰り返します
