• 呕心之作:支付宝的手机网站支付接口的应用


      由于去年做手机Portl接口的工作,需要使用支付宝的支付,于是手机网站支付接口就成了首选。

    1.首先下载接口包

     支付宝商家服务中心链接https://b.alipay.com/login.htm?goto=https://b.alipay.com:443/newIndex.htm

      手机网站支付的产品介绍https://b.alipay.com/order/productDetail.htm?productId=2013080604609688

      demo下载链接https://doc.open.alipay.com/doc2/detail.htm?treeId=54&articleId=104511&docType=1   (请点击关键字demo,进行下载)

      

      解压下载的文件可以看到文件夹的结构如下图:

      

      我使用的是RSA签名方式,PHP-UTF-8的文件夹

    2.readme.txt的文档说明

      红色字体的文件是最重要的文件,也是必需的!


    ├lib┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈类文件夹
    │ │
    │ ├alipay_core.function.php ┈┈┈┈┈┈支付宝接口公用函数文件
    │ │
    │ ├alipay_notify.class.php┈┈┈┈┈┈┈支付宝通知处理类文件
    │ │
    │ ├alipay_submit.class.php┈┈┈┈┈┈┈支付宝各接口请求提交类文件
    │ │
    │ └alipay_rsa.function.php┈┈┈┈┈┈┈支付宝接口RSA函数文件

    ├log.txt┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈日志文件

    alipay.config.php┈┈┈┈┈┈┈┈┈┈┈┈基础配置类文件

    alipayapi.php┈┈┈┈┈┈┈┈┈┈┈┈┈┈支付宝接口入口文件

    notify_url.php ┈┈┈┈┈┈┈┈┈┈┈┈┈服务器异步通知页面文件

    return_url.php ┈┈┈┈┈┈┈┈┈┈┈┈┈页面跳转同步通知文件

    ├key┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈私钥公钥文件夹(用法见下方※注意※)
    │ │
    │ ├rsa_private_key.pem┈┈┈┈┈┈┈┈┈商户的私钥文件
    │ │
    │ └alipay_public_key.pem┈┈┈┈┈┈┈┈支付宝的公钥文件

    ├openssl┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈缺省dll文件(用法见下方※注意※)
    │ │
    │ ├libeay32.dll
    │ │
    │ ├ssleay32.dll
    │ │
    │ └php_openssl.dll

    cacert.pem ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈用于CURL中校验SSL的CA证书文件

    readme.txt ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈使用说明文本


    3. 把必需的文件整合到框架里(我当时用的是thinkPHP框架)
      (1)在 里新建一个文件夹叫AliMobilePay

          
         把上图里的4个文件拷贝到AliMobilePay文件夹里,

          对以上文件进行重命名,

          alipay_core.function.php重命名为:Corefunction.php

          alipay_notify.class.php重命名为:Notify.php

          alipay_rsa.function.php重命名为:Rsafunction.php

          alipay_submit.class.php重命名为:Submit.php

          打开Notify.php,去掉一下代码,

          require_once("alipay_core.function.php");
          require_once("alipay_rsa.function.php");

          同样的道理去掉其他3个文件里的包含文件。

      (2)在根目录下建立一个文件夹key
         在key文件夹里放入
    商户的私钥文件、支付宝的公钥文件、CA证书文件

            

           如何生成RSA密钥https://cshall.alipay.com/enterprise/help_detail.htm?help_id=474010&keyword=%C8%E7%BA%CE%C9%FA%B3%C9%B9%AB%CB%BD%D4%BF&sToken=s-5d0c889ac47741fd8094b26d4862696b&from=search&flag=0   (此文中描述的rsa_private_key.pem就是商家的私钥文件

             

          ◆商户的私钥
          1、不需要对刚生成的(原始的)私钥做pkcs8编码
          2、不需要去掉去掉“-----BEGIN RSA PRIVATE KEY-----”、“-----END RSA PRIVATE KEY-----”
          简言之,只要维持用openssl工具刚生成出来的私钥的内容即可

          

          ◆支付宝公钥
          1、须保留“-----BEGIN PUBLIC KEY-----”、“-----END PUBLIC KEY-----”这两条文字。
          简言之,支付宝公钥只需要维持demo里的原样即可

          

       (3)alipay_config.php 配置文件

          把alipay_config.php 配置文件整合到thinkPHP框架的配置文件里

          

    <?php
    /**
     * Created by PhpStorm.
     * User: zhangxiaoliu
     * Date: 16/4/15
     * Time: 上午10:39
     */
    //支付宝商家服务中心链接:https://b.alipay.com/login.htm?goto=https://b.alipay.com:443/newIndex.htm
    return array(
        'ALIMOBILEPAY_CONFIG'=>array(
            //合作身份者id,以2088开头的16位纯数字, (合作身份者id的查看链接:https://b.alipay.com/order/pidAndKey.htm)
            'partner' => '2088XXXXXXXXXXXX',
    
            //收款支付宝账号,与partner的值一样
            'seller_id' => '2088XXXXXXXXXXXX',
    
            //商户的私钥(后缀是.pem)文件相对路径
            'private_key_path'=> NEW_PORTAL_DOMAIN.'key/rsa_private_key.pem',
    
            //支付宝公钥(后缀是.pem)文件相对路径
            'ali_public_key_path'=> NEW_PORTAL_DOMAIN.'key/alipay_public_key.pem',
    
            //签名方式 不需修改
            'sign_type' => strtoupper('RSA'),
    
            //字符编码格式 目前支持 gbk 或 utf-8
            'input_charset'=> 'utf-8',
    
            //ca证书路径地址,用于curl中ssl校验
            'cacert' => NEW_PORTAL_DOMAIN.'key/cacert.pem',
    
            //访问模式,根据自己的服务器是否支持ssl访问,若支持请选择https;若不支持请选择http
            'transport' => 'http',
    
            //这里是异步通知页面url,提交到项目的Payment控制器的notifyurl方法;
            //需http://格式的完整路径,不能加?id=123这类自定义参数
            'notify_url'=> NEW_PORTAL_DOMAIN.'portal.php/AliMobilePay/notify_url.php',
    
            //这里是页面跳转通知url,提交到项目的Payment控制器的returnurl方法;
            //需http://格式的完整路径,不能加?id=123这类自定义参数
            'return_url'=> NEW_PORTAL_DOMAIN.'portal.php/AliMobilePay/return_url.php',
    
            //支付成功跳转到的页面
            'successpage'=>NEW_PORTAL_DOMAIN.'portal.php/Success/index',
            //支付失败跳转到的页面
            'errorpage'=>NEW_PORTAL_DOMAIN.'portal.php/Error/index',
            //商品展示地址
            'product_url'=>NEW_PORTAL_DOMAIN.'portal.php/Product/index',
        )
    );

        (4)支付宝帮助中心

          https://cshall.alipay.com/enterprise/index.htm

    4.调用支付宝接口

        (1)新建一个AliMobilePay控制器

    <?php
    namespace PortalController;
    use CommonComponentFilterComponent;
    use PortalServiceLogPaycallbacksService;
    use PortalServiceGuozhanOrderService;
    use PortalModelPengwifiGuozhanOrderModel;
    use PortalServiceTokenService;
    use PortalServiceUserService;
    use PortalServiceSetMotoRadiusService;
    use CommonModelRadiusRadcheckModel;
    /*
     * 购买上网卡的手机页面支付宝接口
     */
    class AliMobilePayController extends CommonController{
        protected $_order_model=null;
        protected $_order_service=null;
        protected $_token_service = null;
        protected $_Set_MotoRadius_service=null;
        protected $_RadcheckModel=null;
        protected $_log_pay_callbacks = null;
    
        protected function afterInit() {
            parent::afterInit();
            vendor('AliMobilePay.Corefunction');
            vendor('AliMobilePay.Rsafunction');
            vendor('AliMobilePay.Notify');
            vendor('AliMobilePay.Submit');
    		$this->_order_model= new OrderModel();
            $this->_order_service= new GuozhanOrderService();
            $this->_log_pay_callbacks = new LogPaycallbacksService();
            $this->_service = new UserService();
            $this->_token_service = new TokenService();
            $this->_RadcheckModel = new RadcheckModel();
            $this->_Set_MotoRadius_service = new SetMotoRadiusService();
        }
    
    
    	/**
    	 * 执行新增订单
    	 */
        protected function _post(){
            if(isset($this->params['name']) && ($this->params['name']=="notify_url")){
                $this->notify_url('notify_url');
                die;
            }
            $this->insert_order();
    	}
    
    
        protected function _get(){
            /*
             *根据配置文件里的路由规则:
             *':'.$var_controller.'/[:name]/[:action]'=>		':1/_index?',	//匹配控制器后紧跟字符串,表示name
             * 例如:http://portal_v2.com/portal.php/Payment/Return.html
             * $notify_url会返回Return
             */
            $notify_url = isset($this->params['name']) ? FilterComponent::getString($this->params['name']) : 'Unknown';
            switch($notify_url){
                case 'return_url':
                    $this->return_url($notify_url);
                    break;
    
                default:
                    $this->_log_pay_callbacks->update(array('request_from'=>'Unknown'), false);
                    exit('Wrong request url');
            }
        }
        
        //服务器异步通知页面方法
        private function notify_url($notify_url){
            $alipay_config = C('ALIMOBILEPAY_CONFIG');
            //计算得出通知验证结果
            $alipayNotify = new AlipayNotify($alipay_config);
            $verify_result = $alipayNotify->verifyNotify();
            if($verify_result) {//验证成功
                //商户订单号
                $order_sn = $this->params['out_trade_no'];
                //支付宝交易号
                //$trade_no = $this->params['trade_no'];
                //交易状态
                $trade_status = $this->params['trade_status'];
                $this->_log_pay_callbacks->update(array('request_from'=>$notify_url, 'order_sn'=>$order_sn, 'response_status'=>$trade_status), false);
                if (in_array($trade_status,array('TRADE_SUCCESS','TRADE_FINISHED'))) {
                    //判断该笔订单是否在商户网站中已经做过处理
                    //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
                    //如果有做过处理,不执行商户的业务程序
                     if(!$this->checkorderstatus($order_sn)){
                         $result=$this->orderhandle($order_sn);
                         if($result==true){
                             echo "success";
                         }else{
                             echo "fail";
                         }
                     }
                }else{
                    echo "fail";
                }
            }else {
                //验证失败
                echo "fail";
            }
        }
    
        //页面跳转同步通知
        private function return_url($notify_url){
            $alipay_config=C('ALIMOBILEPAY_CONFIG');
            //计算得出通知验证结果
            $alipayNotify = new AlipayNotify($alipay_config);
            $verify_result = $alipayNotify->verifyReturn();
            if($verify_result) {//验证成功
                //商户订单号
                $order_sn = $this->params['out_trade_no'];
                //支付宝交易号
                //$trade_no = $this->params['trade_no'];
                //交易状态
                $trade_status = $this->params['trade_status'];
                $this->_log_pay_callbacks->update(array('request_from'=>$notify_url, 'order_sn'=>$order_sn, 'response_status'=>$trade_status), false);
                if (in_array($trade_status,array('TRADE_SUCCESS','TRADE_FINISHED'))) {
                    //判断该笔订单是否在商户网站中已经做过处理
                    //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
                    //如果有做过处理,不执行商户的业务程序
                    if(!$this->checkorderstatus($order_sn)){
                        $result=$this->orderhandle($order_sn);
                        //——请根据您的业务逻辑来编写程序(以上代码仅作参考)——
                        if($result==true){
                            header("Location:".C('ALIMOBILEPAY_CONFIG.successpage'));//跳转到配置项中配置的支付成功页面;
                        }else{
                            header("Location:".C('ALIMOBILEPAY_CONFIG.errorpage'));//跳转到配置项中配置的支付失败页面;
                        }
                    }
                }else {
                    header("Location:".C('ALIMOBILEPAY_CONFIG.errorpage'));//跳转到配置项中配置的支付失败页面;
                }
            }else {
                //支付宝页面“返回商户”按钮的链接,商品页面
                header("Location:".C('ALIMOBILEPAY_CONFIG.product_url'));
            }
        }
    
        //在线交易订单支付处理函数
        //函数功能:根据支付接口传回的数据判断该订单是否已经支付成功;
        //返回值:如果订单已经成功支付,返回true,否则返回false;
        private function checkorderstatus($order_sn){
            $status=$this->_order_model->where("order_sn='$order_sn'")->getField('order_status');
            if($status == OrderModel::ORDER_STATUS_PAYED){
                return true;
            }else{
                return false;
            }
        }
        //处理订单函数
        //更新订单状态,写入订单支付后返回的数据
        private function orderhandle($order_sn){
            try{
                //开启事务
                $this->_order_model->startTrans();
                $data['order_status']=OrderModel::ORDER_STATUS_PAYED;
                $affected_row=$this->_order_model->where("order_sn='$order_sn'")->save($data);
                $find=$this->_order_model->where("order_sn='$order_sn'")->field('location_id,goods_id,mobile,goods_number')->find();
                //根据goods_id查找card_name对应的上网时长
                $goods_model=M('goods');
                $card_model=M('card');
                $card_name=$goods_model->where("id={$find['goods_id']}")->getField('card_name');
                $duration=$card_model->where("location_id={$find['location_id']} and card_name='$card_name'")->order('id desc')->getField('duration');
                $incre_time=($find['goods_number']) * $duration;
                $user_model=M('user');
                $mobile=$find['mobile'];
                $user_info=$user_model->where("user_name='{$mobile}'")->field('id,end_time')->find();
                $affected_row2=$user_model->where("user_name='{$mobile}'")->setInc('usable_time',$incre_time);
                //如果end_time 大于当前的时间戳就累计,否则就更新:使用当前时间戳 加上 $incre_time
                if($user_info['end_time'] >= time()){
                    $user_model->where("user_name='{$mobile}'")->setInc('end_time',$incre_time);
                }else{
                    $update_data['end_time']=time()+$incre_time;
                    $user_model->where("user_name='{$mobile}'")->save($update_data);
                }
                if(empty($affected_row)){
                    $this->_log_pay_callbacks->setException(L('ERROR_FAILED_UPDATE_ORDER'), $this->_log_pay_callbacks->getException('code'));
                    throw new Exception();
                }
                if(empty($affected_row2)){
                    $this->_log_pay_callbacks->setException(L('ERROR_FAILED_UPDATE_USABLETIME'), $this->_log_pay_callbacks->getException('code'));
                    throw new Exception();
                }
                //提交更新
                if($affected_row && $affected_row2) {
                    $this->_order_model->commit();
                    return true;
                }
            }catch(Exception $e){
                $this->_order_model->rollback();
                return false;
            }
        }
    
        private function insert_order(){
            $gw_id = isset($this->params['gw_id']) ? FilterComponent::get($this->params['gw_id']) : '';
            if (empty($gw_id)) {
                exit('400_EMPTY_GWID');
            }
            $router=M('router');
            $location_id=$router->where("gw_id='$gw_id'")->getField('supplier_location_id');
            $goods_number = isset($this->params['goods_number']) ? FilterComponent::get($this->params['goods_number'],'int') : '';
            if (empty($goods_number)) {
                exit('400_EMPTY_GOODSNUMBER');
            }
            $mobile = isset($this->params['mobile']) ? FilterComponent::get($this->params['mobile']) : '';
            if (!preg_match('/^1[0-9]{10}$/',$mobile)) {
                exit('400_ERROR_MOBILE');
            }
            $user=M('user');
            //查询充值号码是否存在
            $user_name=$user->where("user_name='$mobile'")->getField('user_name');
            if(!$user_name){
                exit('400_EMPTY_USERNAME');
            }
            $goods_id = isset($this->params['goods_id']) ? FilterComponent::get($this->params['goods_id'],'int') : '';
            if (empty($goods_id)) {
                exit('400_EMPTY_GOODSID');
            }
    
            $goods=M('goods');
            $unit_price=$goods->where("id=$goods_id")->getField('unit_price');
            $this->params['WIDtotal_fee']=$unit_price * $goods_number;
    
            $data['location_id']=$location_id;
            $data['mobile']=$mobile;
            $data['goods_id']=$goods_id;
            $data['goods_type']=1;//1代表充值卡
            $data['goods_number']=$goods_number;
            $data['total_price']=$this->params['WIDtotal_fee'];
            $data['pay_type']=OrderModel::PAY_TYPE_ALIPAY;//支付宝
            //执行添加操作
            $insert_id=$this->_order_service->update($data,false);
    //        var_dump($this->_order_service->getError());
    //        var_dump($this->_order_service->model->getError());
    //        var_dump($this->_order_service->model->getlastsql());die;
            if($insert_id){
                $this->params['WIDout_trade_no']=$this->_order_model->where("id=$insert_id")->getField('order_sn');
                /**************************请求参数**************************/
                //支付类型
                $payment_type = "1";
                //必填,不能修改
    
                //商户订单号
                $out_trade_no = $this->params['WIDout_trade_no'];
                //商户网站订单系统中唯一订单号,必填
    
                $this->params['WIDsubject']='pengwifi_card';
                //订单名称
                $subject = $this->params['WIDsubject'];
                //必填
    
                //付款金额
                $total_fee = $this->params['WIDtotal_fee'];
                //必填
    
                //$this->params['WIDshow_url']=trim(C('ALIMOBILEPAY_CONFIG.product_url'));
                $this->params['WIDshow_url']=$_SERVER['HTTP_REFERER'];
                //商品展示地址
                $show_url = $this->params['WIDshow_url'];
                //必填,需以http://开头的完整路径,例如:http://www.商户网址.com/myorder.html
    
                //订单描述
                $body = $this->params['WIDbody'];
                //选填
    
                //超时时间
                $it_b_pay = $this->params['WIDit_b_pay'];
                //选填
    
                //钱包token
                $extern_token = $this->params['WIDextern_token'];
                //选填
    
                /************************************************************/
                //构造要请求的参数数组,无需改动
                $parameter = array(
                    "service" => "alipay.wap.create.direct.pay.by.user",
                    "partner" => trim(C('ALIMOBILEPAY_CONFIG.partner')),
                    "seller_id" => trim(C('ALIMOBILEPAY_CONFIG.seller_id')),
                    "payment_type"	=> $payment_type,
                    "notify_url"	=> trim(C('ALIMOBILEPAY_CONFIG.notify_url')),
                    "return_url"	=> trim(C('ALIMOBILEPAY_CONFIG.return_url')),
                    "out_trade_no"	=> $out_trade_no,
                    "subject"	=> $subject,
                    "total_fee"	=> $total_fee,
                    "show_url"	=> $show_url,
                    "body"	=> $body,
                    "it_b_pay"	=> $it_b_pay,
                    "extern_token"	=> $extern_token,
                    "_input_charset"	=> trim(strtolower(C('input_charset')))
                );
    
                $alipay_config=C('ALIMOBILEPAY_CONFIG');
    
                //建立请求
                $alipaySubmit = new AlipaySubmit($alipay_config);
                //建立请求,以表单HTML形式构造(默认),经测试post方法不行
                $html_text = $alipaySubmit->buildRequestForm($parameter,"get", "确认");
    
                echo $html_text;
            }else{
                echo 'fail';
            }
        }
    }
    

    如果您阅读过此文章有所收获,请为我顶一个,如果文章中有错误的地方,欢迎指出。

    相互学习,共同进步!

     

  • 相关阅读:
    动态加载JS脚本【转】
    定义并且立即执行JS匿名函数拾遗
    javascript操作ASCII码与字符对转
    win7的mklink命令
    [Yii Framework] How to get the current static page name?
    [Ubuntu] 利用Ubuntu光盘破解win7用户登录 Crark the win7 user via Ubuntu live CD
    [Ubuntu] reload the .bashrc file without logout nor restart.
    [Ubuntu] the permissions of lampp mysql and phpmyadmin
    [Zend PHP5 Cerification] Some note when studying
    [eZ publish] How to modify the $view_parameters valus in the template.
  • 原文地址:https://www.cnblogs.com/zhangxiaoliu/p/5392301.html
Copyright © 2020-2023  润新知