探索:CodeIgniter集成MD5签名版本支付宝方案(持续更新中)

本文使用的支付宝即时到账业务接口,API文档下载地址:支付宝即时到账接口文档

1. 集成支付宝的CI目录结构

├─config 配置文件目录
│ ├─alipay.php 支付宝配置文件
│ └─...
├─...
├─libraries 扩展类库目录
│ ├─MY_AlipayNotify.php 支付宝返回通知
│ ├─MY_AlipaySubmit.php 支付提交
│ ├─Payment 各种支付类库目录
│ │ ├─Alipaylib 支付宝类库包目录
│ │ │ ├─alipay_core.function.php
│ │ │ ├─alipay_md5.function.php
│ │ │ ├─alipay_submit.class.php
│ │ │ ├─alipay_notify.class.php
│ │ │ └─cacert.pem
│ │ ├─Wechatpay 微信支付类库目录
│ │ └─... 更多支付类库目录
│ └─... 更多第三方类库目录
└─...

2. alipay.php 支付宝配置文件
<?php defined('BASEPATH') OR exit('No direct script access allowed');
/**
 * Alipay 配置文件
 */
//↓↓↓↓↓↓↓↓↓↓请在这里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
//合作身份者ID,签约账号,以2088开头由16位纯数字组成的字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
$config['partner']       = '';

//收款支付宝账号,以2088开头由16位纯数字组成的字符串,一般情况下收款账号就是签约账号
$config['seller_id'] = $config['partner'];

// MD5密钥,安全检验码,由数字和字母组成的32位字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
$config['key']           = '';

// 服务器异步通知页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
$config['notify_url'] = site_url('pay/alipaynotify');

// 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
$config['return_url'] = site_url('pay/alipayreturn');

//签名方式
$config['sign_type']    = strtoupper('MD5');

//字符编码格式 目前支持 gbk 或 utf-8
$config['input_charset']= strtolower('utf-8');

//ca证书路径地址,用于curl中ssl校验
//请保证cacert.pem文件在当前文件夹目录中
$config['cacert']    = getcwd().'\\cacert.pem';

//访问模式,根据自己的服务器是否支持ssl访问,若支持请选择https;若不支持请选择http
$config['transport']    = 'http';

// 支付类型 ,无需修改
$config['payment_type'] = "1";
        
// 产品类型,无需修改
$config['service'] = "create_direct_pay_by_user";

//↑↑↑↑↑↑↑↑↑↑请在这里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑


//↓↓↓↓↓↓↓↓↓↓ 请在这里配置防钓鱼信息,如果没开通防钓鱼功能,为空即可 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
    
// 防钓鱼时间戳  若要使用请调用类文件submit中的query_timestamp函数
$config['anti_phishing_key'] = "";
    
// 客户端的IP地址 非局域网的外网IP地址,如:221.0.0.1
$config['exter_invoke_ip'] = "";
        
//↑↑↑↑↑↑↑↑↑↑请在这里配置防钓鱼信息,如果没开通防钓鱼功能,为空即可 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
?>
3. MY_AlipaySubmit.php 支付宝支付
<?php defined('BASEPATH') OR exit('No direct script access allowed');

// 加载支付宝支付提交类
require_once APPPATH.'libraries/Payment/Alipaylib/alipay_submit.class.php';

/**
 * MY_AlipaySubmit 
 * @author Mike Lee
 */
class MY_AlipaySubmit extends AlipaySubmit {
    
    protected $_CI;

    public function  __construct($config = array()){
        // 获得 _CI 超级对象
        $this->_CI = & get_instance();
        
        // 判断是否存在配置文件
        if (empty($config)) {
            // 加载 Alipay 配置文件
            $this->_CI->load->config('alipay', TRUE);
            $config = $this->_CI->config->item('alipay');
        }

        parent::__construct($config);
    }

    /**
     * submit 支付提交
     * @access public
     * @param string $out_trade_no 商户订单号 商户网站订单系统中唯一订单号
     * @param string $subject 订单名称
     * @param float $total_fee 付款金额
     * @param string $body 商品描述 可空
     * @return void
     * @author Mike Lee
     */
    public function submit($order_code){
        $this->_CI->load->model('order_model');
        $order_info = $this->_CI->order_model->getOrderByID($order_code, true);
        if ( ! is_array($order_info) || ! $order_info) return false;
        $subject = 'XXXX订单';
        $total_fee = $order_info['order_amount'];
        $body = 'XXXX网站商品购买';
        // 构造要请求的参数数组
        $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"      => $order_code,
            "subject"           => $subject,
            "total_fee"         => $total_fee,
            "body"              => $body,
            "_input_charset"    => trim(strtolower($this->alipay_config['input_charset']))
        );

        $html_text = $this->buildRequestForm($parameter, 'get', '确认');
        return $html_text;
    }


}

/* End of file MY_AlipaySubmit.php */
/* Location: ./application/libraries/MY_AlipaySubmit.php */
?>
4. MY_AlipayReturn.php 支付宝返回通知
<?php defined('BASEPATH') OR exit('No direct script access allowed');

// 加载支付宝通知类
require_once APPPATH.'libraries/Payment/Alipaylib/alipay_notify.class.php';

/**
 * MY_AlipayNotify
 * @author Mike Lee
 */
class MY_AlipayNotify extends AlipayNotify {
    
    protected $_CI;

    public function  __construct($config = array()){
        // 获得 CI 超级对象 使得自定义类可以使用Controller类的方法
        $this->_CI = & get_instance();
        
        // 判断是否存在配置文件
        if (empty($config)) {
            // 加载 Alipay 配置文件
            $this->_CI->load->config('alipay', TRUE);
            $config = $this->_CI->config->item('alipay');
        }

        parent::__construct($config);
    }

    /**
     * asynNotify 异步通知
     * @access public
     * @param void
     * @return void
     * @author Mike Lee
     */
    public function asynNotify(){
        $verify_result = $this->verifyNotify();
        if($verify_result) {    //验证成功
            $notify_data = $this->_CI->input->post(null, true);
            //交易状态
            $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
     * @author Mike Lee
     */
    public function syncReturn(){
        $verify_result = $this->verifyReturn();
        if($verify_result) {    // 验证成功
            $return_data = $this->_CI->input->get(null, true);
            // 交易状态
            $trade_status = $return_data['trade_status'];
            if($trade_status == 'TRADE_FINISHED' || $trade_status == 'TRADE_SUCCESS') {
                // 更新订单支付信息
                $this->updateOrderPay($return_data);
                return true;
            } else {
                return false;
            }
            return true;
        } else {
            // 验证失败
            return false;
        }
    }

    /**
     * updateOrderPay 更新订单支付信息
     * @access private
     * @param string $order_code
     * @return mixed
     * @author Mike Lee
     */
    public function updateOrderPay($alipay_data){
        $this->_CI->load->model('order_model');
        // 根据订单号获取订单信息
        $order_code = $alipay_data['out_trade_no'];
        $order_info = $this->_CI->order_model->getOrderByID($order_code, true);
        // 判断订单状态以及是否已经支付
        if ($order_info['order_status'] == 0 && $order_info['pay_status'] != '1') {
            // 更新订单的支付状态及支付方式
            // pay_status => 1 表示支付成功
            // pay_method => 3 表示支付方式为支付宝支付
            $pay_info = array('pay_status' => '1', 'pay_method' => 3);
            $pay_result = $this->_CI->order_model->updateOrderPayStatus($order_code, $pay_info, true);
            // 添加订单支付信息
            // user_id 用户ID
            // order_id 订单ID
            // total_fee 支付总额
            // openid 用户支付宝ID buyer_id
            $order_pay_info = array(
                'user_id'   => $order_info['user_id'],
                'order_id'  => $order_info['id'],
                'total_fee' => $alipay_data['total_fee'],
                #'total_fee' => $alipay_data['price'],
                'buyer_id'    => $alipay_data['buyer_id'],
                'time'      => time()
            );
            $this->_CI->order_model->addOrderPayLog($order_pay_info);
            // 添加用户收支日志
            // correlation_id 关联ID 这里为订单ID
            // action_type => 1 表示为XXXX商品购买事件
            // point_type => 5 表示为支付宝支付
            $payment_log = array(
                'user_id'        => $order_info['user_id'],
                'correlation_id' => $order_info['id'],
                'action_type'    => 1,
                'pay_type'     => 5,
                'total_fee'  => $alipay_data['total_fee'],
                'remark'         => 'XXX网站商品购买:支付宝支付'.$alipay_data['total_fee'],
                'action_ip'      => $_SERVER['REMOTE_ADDR'],
                'add_time'       => time()
            );
            $this->_CI->order_model->addUserPayLog($payment_log);
        }
        
        return true;
    }


}

/* End of file MY_AlipayNotify.php */
/* Location: ./application/libraries/MY_AlipayNotify.php */
5. Pay.php 控制器
<?php defined('BASEPATH') OR exit('No direct script access allowed');
/**
 * pay 支付控制器
 */
class Pay extends MY_Controller {

    public function __construct(){
        $this->isNeedLogin = FALSE;
        parent::__construct();
    }

    /**
     * submitPaymentToAlipay 提交支付宝支付
     * @access public
     * @param string $order_code
     * @return void
     * @author Mike Lee
     */
    public function submitPaymentToAlipay($order_code){
        $this->load->library('MY_AlipaySubmit');
        $result = $this->my_alipaysubmit->submit($order_code);
        if ($result === false) {
            // 无效订单跳转
            // 请注意这里的跳转方法是自己写的
            $this->jumpNoticePage('无订单信息!', $_SERVER['HTTP_REFERER'], 'ERROR');
        }

        $this->assign('html_text', $result);
        $this->display('order/alipay.html');
    }

    /**
     * aliPayNotify 支付宝异步回调
     * notify_url http://www.xxxx.com/index.php/pay/alipaynotify
     * @access public
     * @param void
     * @return void
     * @author Mike Lee
     */
    public function aliPayNotify(){
        $this->load->library('MY_AlipayNotify');
        $this->my_alipaynotify->asynNotify();
    }

    /**
     * aliPayReturn 支付宝同步通知
     * return_url http://www.xxxx.com/index.php/pay/alipayreturn
     * @access public
     * @param void
     * @return void
     * @author Mike Lee
     */
    public function aliPayReturn(){
        $this->load->library('MY_AlipayNotify');
        if ($this->my_alipaynotify->syncReturn()) {
            // 验证成功 这里可以执行你想要的跳转
            $this->jumpNoticePage('订单支付成功!', site_url('order'));
        } else {
            // 验证失败跳转
            $this->jumpNoticePage('订单支付失败!', site_url('order'), 'ERROR');
        }
    }
6. 小结

总体来说,支付宝支付的使用还是很简单的,目前支付宝即时到账有密退款DEMO还没有去研究,有时间肯定会继续更新的。另外我觉得支付宝即时到账DEMO中库文件太多,后期准备将这些库文件整理成一个文件一个类,这样可以让整个集成结构更加清晰。
在调用支付宝支付成功后支付宝的同步跳转中出现了一个不明原因的验证错误,经过调试发现,在执行verifyReturn验证方法的过程中有个去除空值及签名参数的函数paraFilter执行后会使得$_GET中的第一个参数丢失,比如$para_temp['body']为第一个参数且不为空,执行paraFilter($para_temp)后body丢失了,但这个函数本身是没有问题的,这就导致我至今还没有找到造成参数丢失的原因,只好按照支付宝技术支持的方法在丢失参数的地方手动加上这个参数,这才验证通过。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,820评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,648评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,324评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,714评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,724评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,328评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,897评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,804评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,345评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,431评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,561评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,238评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,928评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,417评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,528评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,983评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,573评论 2 359

推荐阅读更多精彩内容