由于目前所在的公司是以CI
框架为主的项目开发,所以博主对CI
的了解程度比其它框架要好一点。之前有篇文章简要的介绍了使用支付宝支付的示例demo
,这篇文章就主要说一下支付宝在CI
框架上的代码集成。
框架版本
CodeIgniter 2.0
要点
新建配置项
默认情况下,CI
中已经有一个配置文件,位于application/config
目录下的config.php
文件。文件内容是一个$config
数组:
<?php
$config['base_url'] = '';
$config['index_page'] = '';
$config['uri_protocol'] = 'AUTO';
$config['url_suffix'] = '';
......
自定义一个配置项的话,就要在application/config
目录下建立一个文件,但是文件内容也要和config.php
里的内容相仿,配置项也要放在$config
的数组中,比如我新建一个支付宝的配置项alipay.php
:
内容如下:
<?php
// 合作身份者ID,签约账号,
$config['partner'] = '';
// 收款支付宝账号 == 签约账号
$config['seller_id'] = $config['partner'];
// MD5密钥 安全检验码 查看地址:https://b.alipay.com/order/pidAndKey.htm
$config['key'] = '';
// 服务器异步通知页面路径
$config['notify_url'] = base_url()."sz_front/alipay/asyn_notify";
// 页面跳转同步通知页面路径
$config['return_url'] = base_url()."sz_front/alipay/sync_return";
//签名方式
$config['sign_type'] = strtoupper('MD5');
//字符编码格式 目前支持 gbk 或 utf-8
$config['input_charset'] = strtolower('utf-8');
//ca证书路径地址,用于curl中ssl校验
$config['cacert'] = getcwd().'\\application\\libraries\Payment\\Alipaylib\cacert.pem';
//访问模式
$config['transport'] = 'http';
// 支付类型
$config['payment_type'] = "1";
// 产品类型(支付)
$config['service'] = "create_direct_pay_by_user";
// 产品类型(退款)
$config['refund_service'] = 'refund_fastpay_by_platform_pwd';
// 调试日志文件路径
$config['log_path'] = base_url().'application/logs/alipay/alipay-'.date('Y-m-d').'.log';
//↓↓↓↓↓↓↓↓↓↓ 配置防钓鱼信息↓↓↓↓↓↓↓↓↓↓↓
// 防钓鱼时间戳 若要使用请调用类文件submit中的query_timestamp函数
$config['anti_phishing_key'] = "";
// 客户端的IP地址 非局域网的外网IP地址,如:221.0.0.1
$config['exter_invoke_ip'] = "";
加载自定义配置项
手动加载
CI
中调用自定义配置项也很简单,如下:
$this->config->load('alipay', TRUE);
$this->alipay_config = $this->config->item('alipay');
$this->config->load(filename, bool, bool )
调用配置文件,有三个参数:
filename
自定义配置项的文件名,比如我这里文件名是alipay.php
,第一个参数写alipay
就可以了;bool
这里可以填写bool
类型的值,如果为TRUE
的话,可以使每个配置项单独保存在以文件名为索引的数组中,在同时调用多个配置项的时候,这样做可以避免冲突;bool
如果为TRUE
的话,可以屏蔽当配置文件不存在时产生的错误信息
$this->config->item(filename)
从配置文件中检索元素,里面填写配置文件名。
自动加载
自动加载,故名思议就是在程序初始化的时候就加载,类似于TP3
以下的所有扩展类库一股脑全加载,不过在CI
中是可以定义自动加载内容的,这个文件就在application/config
目录下的autoload.php
文件,打开找到:
在array
里添加alipay
即可:
$autoload['config'] = array('alipay');
加载类库
CI
中的加载类库也很方便,直接就哼哼嘿哈哈就好了(智障.jpg
),当然是开玩笑,加载类库方法如下:
$this->load->library( '' );
调用类库中方法的方式有两种:
$this->class_name->action();
$this->类名->方法名;$class_obj = new class_name();
这种就是php实例化类的方法,方便使用构造函数进行类初始化。
正(大)文(招)
其实也没啥正文,就是代码部分(哈哈哈哈~张全蛋的笑声.mp3
),代码中的注释我认为已经写的很清楚了,就不分割开一一叙述了:
<?php
header("content-type:text/html;charset=utf-8");
/**
* 支付
* @version 1.0
* @package application
* @subpackage application/controllers/alipay/
*/
class Alipay extends Front_Controller {
private $alipay_config;
public function __construct() {
parent::__construct();
$this->load->model('user_model');
$this->load->model('order_info_model');
$this->load->model('order_goods_model');
$this->_site_path = "sz_front";
//加载alipay配置文件
$this->config->load('alipay', TRUE);
$this->alipay_config = $this->config->item('alipay');
}
/**
* 跳转到支付宝支付页面
* @param int $order_id 订单ID
*/
public function index($order_id) {
$field = array('user_id', 'order_sn', 'pay_status', 'pay_fee');
$order_info = $this->order_info_model->get_appoint_values($field, array('order_id'=>$order_id));
if(empty( $order_id ) || ( empty( $order_info ) && !is_array( $order_info ) ) ) {
$this->error_js_noref('无此订单信息');exit;
}
//查询此订单是不是登陆者订单
$user_id = $this->session->userdata('userid');
if($order_info[0]['user_id'] != $user_id) $this->error_js_noref('订单数据错误');
$goods_name = $this->order_goods_model->get_value_by_notpk('goods_name', array('order_id' => $order_id));
$pay_info = array(
'trade_no' => $order_info[0]['order_sn'],//订单名称,必填
'goods_name' => $goods_name, //商品名称
'total_fee' => $order_info[0]['pay_fee'],//付款金额,必填
'remark' => $goods_name,//商品描述,可空
);
//调用支付宝支付接口
$result = $this->alipay_submit($pay_info);
if (is_bool($result) && ! $result) {
$this->message_js('订单信息不存在!', base_url('sz_front/myorder/orders'));
}
if (is_bool($result) && $result == true) {
$this->error_js_noref('订单已支付,请不要重复支付!', base_url('sz_front/myorder/orders'));
}
echo $result;
}
/**
* 支付宝支付参数整合并调用支付页面
* @param array $pay_info 商品信息
* @return html 支付宝页面
*
*/
public function alipay_submit($pay_info) {
//整合支付参数
$parameter = array(
"service" => $this->alipay_config['service'],
"partner" => $this->alipay_config['partner'],
"seller_id" => $this->alipay_config['seller_id'],
"payment_type" => $this->alipay_config['payment_type'],
"notify_url" => $this->alipay_config['notify_url'],
"return_url" => $this->alipay_config['return_url'],
"anti_phishing_key" => $this->alipay_config['anti_phishing_key'],
"exter_invoke_ip" => $this->alipay_config['exter_invoke_ip'],
"out_trade_no" => $pay_info['trade_no'],
"subject" => $pay_info['goods_name'],
"total_fee" => $pay_info['total_fee'],
"body" => $pay_info['remark'],
"_input_charset" => trim(strtolower($this->alipay_config['input_charset']))
);
//调用支付接口类
$this->load->library( 'Payment/Alipaylib/AlipaySubmit' );
//实例化支付接口类并对构造函数传参
$AlipaySubmit = new AlipaySubmit($this->alipay_config);
//调用获取支付页面方法
$html_text = $AlipaySubmit->buildRequestForm($parameter, "post", "确认");
//返回支付页面
return $html_text;
}
/**
* asynNotify 异步通知
* @access public
* @param void
* @return void
*/
public function asyn_notify(){
//调用支付接口类
$this->load->library( 'Payment/Alipaylib/AlipayNotify' );
// require_once APPPATH.'libraries/Payment/Alipaylib/alipay_notify.class.php';
$alipayNotify = new AlipayNotify($this->alipay_config);
$verify_result = $alipayNotify->verifyNotify();
if($verify_result) {//验证成功
//商户订单号
$out_trade_no = $this->input->post('out_trade_no');
//支付宝交易号
$trade_no = $this->input->post('trade_no');
//交易状态
$trade_status = $this->input->post('trade_status');
$gmt_payment = $this->input->post('gmt_payment');
if($this->input->post('trade_status') == 'TRADE_FINISHED') {
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
//如果有做过处理,不执行商户的业务程序
//注意:
//退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
//调试用,写文本函数记录程序运行情况是否正常
$date = date('Y-m-d H:i:s', time());
$str = "订单号:".$out_trade_no.'于'.$gmt_payment.'付款'.$trade_status.'\n';
logResult($date.$str);
}
else if ($this->input->post('trade_status') == 'TRADE_SUCCESS') {
$this->update_order_info($out_trade_no, 'asyn');
//判断该笔订单是否在商户网站中已经做过处理
//如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
//如果有做过处理,不执行商户的业务程序
//注意:
//付款完成后,支付宝系统发送该交易状态通知
//调试用,写文本函数记录程序运行情况是否正常
//logResult("这里写入想要调试的代码变量值,或其他运行的结果记录");
}
//——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
echo "success"; //请不要修改或删除
}
else {
//验证失败
echo "fail";
//调试用,写文本函数记录程序运行情况是否正常
//logResult("这里写入想要调试的代码变量值,或其他运行的结果记录");
}
exit;
$notify_data = $this->input->post(null, true);
$verify_result = $this->verify($notify_data);
if ($verify_result) { //验证成功
//交易状态
$trade_status = $notify_data['trade_status'];
// 根据交易状态处理订单支付状态
if ($trade_status == 'TRADE_FINISHED' || $trade_status == 'TRADE_SUCCESS') {
$this->updateOrderPay($notify_data);
}
echo 'success'; //请不要修改或删除
} else { //验证失败
echo 'fail'; //请不要修改或删除
}
}
/**
* syncReturn 页面跳转同步通知
* @access public
* @param void
* @return void
*/
public function sync_return(){
// require_once APPPATH.'libraries/Payment/Alipaylib/alipay_notify.class.php';
$this->load->library( 'Payment/Alipaylib/AlipayNotify' );
$alipayNotify = new AlipayNotify($this->alipay_config);
$verify_result = $alipayNotify->verifyReturn();
if($verify_result) {
//商户订单号
$out_trade_no = $this->input->get('out_trade_no', TRUE);
//支付宝交易号
$trade_no = $this->input->get('trade_no', TRUE);
//交易状态
$trade_status = $this->input->get('trade_status', TRUE);
echo $trade_status;
if($_GET['trade_status'] == 'TRADE_FINISHED' || $_GET['trade_status'] == 'TRADE_SUCCESS') {
$date = date('Y-m-d H:i:s', time());
$str = "订单号:".$out_trade_no.'于'.$gmt_payment.'付款'.$trade_status.'\n';
echo $date.$str;
$this->update_order_info($out_trade_no, 'sync');
$this->message_js('支付成功!', base_url('sz_front/myorder/orders/ship'));
} else {
$this->message_js('支付失败!', base_url('sz_front/myorder/orders/ship'));
}
}else {
$this->message_js('支付失败!', base_url('sz_front/myorder/orders/ship'));
}
}
/**
* 更新订单信息页面
* @param string $order_sn 订单编号
* @param string $callback 同步、异步
* @author 命中水、
* @date(2016-11-26 am)
*/
public function update_order_info($order_sn, $callback) {
$search_field = array('order_id', 'user_id', 'order_status', 'pay_fee', 'pay_status');
$order_info = $this->order_info_model->get_appoint_values($search_field, array('order_sn' => $order_sn));
//如果未支付
if($order_info[0]['pay_status'] != '2') {
$my_sellid = $this->alipay_config['seller_id'];
$my_payfee = $order_info[0]['pay_fee'];
if( $callback == 'sync' ) { //同步页面
$back_sellid = $this->input->get('seller_id', TRUE);
$back_payfee = $this->input->get('total_fee', TRUE);
$pay_time = $this->input->get('notify_time', TRUE);
$pay_info_data = $this->get_request_data('GET', $order_info[0]['order_id']);
}
if( $callback == 'asyn') { //异步页面
$back_sellid = $this->input->post('seller_id', TRUE);
$back_payfee = $this->input->post('total_fee', TRUE);
$pay_time = $this->input->post('gmt_payment', TRUE);
$pay_info_data = $this->get_request_data('POST', $order_info[0]['order_id']);
}
//验证收款支付宝账户是否与配置项一致
if( $my_sellid != $back_sellid ) {
}
//验证支付订单金额是否一致 这里是单 订单支付验证
//如果一次性提交
if ( $my_payfee != $back_payfee ) {
}
$order_update = array(
'pay_id' => 1,
'pay_name' => '支付宝',
'pay_status' => 2,
'pay_time' => $pay_time,
);
$update_flag = $this->order_info_model->update_by_attributes($order_update, array('order_sn' => $order_sn));
$this->load->model( 'pay_info_model' );
$this->pay_info_model->insert($pay_info_data);
}
}
/**
* 整理插入付款详情表的数据
* @param string $request 请求方式
* @param int $order_id 订单ID
* @return array
* @author 命中水、
* @date(2016-10-24)
*/
public function get_request_data( $request, $order_id) {
if( $request == 'POST' ) {
$request = $this->input->post();
}
if( $request == 'GET' ) {
$request = $this->input->get();
}
return $request_data = array(
'order_id' => $order_id,
'exterface' => $request['exterface'],
'trade_no' => $request['trade_no'],
'trede_status' => strtolower($request['trade_status']),
'notify_time' => strtotime($request['notify_time']),
'notify_type' => $request['notify_type'],
'buyer_email' => $request['buyer_email'],
'buyer_id' => $request['buyer_id'],
'seller_email' => $request['seller_email'],
'seller_id' => $request['seller_id'],
'total_fee' => $request['total_fee'],
'gmt_payment' => $request['gmt_payment'],
'refund_status' => $request['refund_status'],
'gmt_refund' => $request['gmt_refund'],
);
}
}
支付宝类
由于支付宝已经封装好了接口类库,我们稍微改变下,拿过来调用就好了,如何调用在上个段落中的代码中已放出,接下来就是支付宝类的存放位置了,放在application\libraries
下就好了,目录结构如下(application\libraries\Payment\Alipaylib
):
Payment
是为了以后方便接入其它的支付类库,比如微信、网银什么的,Alipaylib
就是专门存放支付宝的类库文件夹了,这里呢改了支付类和回调类的文件名字,为了文件名和类名统一,CI调用的时候不会出错。
总结
CI
中调用类的时候,类名和文件名一定要统一,不然会调用不成功;文章中中部的那一大段代码我是放在了支付
controller
层,有兴趣的小伙伴可以封装一下放在类库(libraries
)里也可以。