热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

呕心之作:支付宝的手机网站支付接口的应用-张小刘

呕心之作:支付宝的手机网站支付接口的应用-张小刘
  由于去年做手机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控制器

_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';
        }
    }
}

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

相互学习,共同进步!


推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 禁止程序接收鼠标事件的工具_VNC Viewer for Mac(远程桌面工具)免费版
    VNCViewerforMac是一款运行在Mac平台上的远程桌面工具,vncviewermac版可以帮助您使用Mac的键盘和鼠标来控制远程计算机,操作简 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • MACElasticsearch安装步骤及验证方法
    本文介绍了MACElasticsearch的安装步骤,包括下载ZIP文件、解压到安装目录、启动服务,并提供了验证启动是否成功的方法。同时,还介绍了安装elasticsearch-head插件的方法,以便于进行查询操作。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
手机用户2602919547
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有