美文网首页PHP档案馆
探索:CodeIgniter集成MD5签名版本支付宝方案(持续更

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

作者: 三天大愚 | 来源:发表于2016-06-16 14:23 被阅读460次

    本文使用的支付宝即时到账业务接口,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丢失了,但这个函数本身是没有问题的,这就导致我至今还没有找到造成参数丢失的原因,只好按照支付宝技术支持的方法在丢失参数的地方手动加上这个参数,这才验证通过。

    相关文章

      网友评论

        本文标题:探索:CodeIgniter集成MD5签名版本支付宝方案(持续更

        本文链接:https://www.haomeiwen.com/subject/btpmdttx.html