程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 嘔心之作:支付寶的手機網站支付接口的應用,嘔心之作

嘔心之作:支付寶的手機網站支付接口的應用,嘔心之作

編輯:關於PHP編程

嘔心之作:支付寶的手機網站支付接口的應用,嘔心之作


  由於去年做手機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 Portal\Controller;
use Common\Component\FilterComponent;
use Portal\Service\LogPaycallbacksService;
use Portal\Service\GuozhanOrderService;
use Portal\Model\Pengwifi\Guozhan\OrderModel;
use Portal\Service\TokenService;
use Portal\Service\UserService;
use Portal\Service\SetMotoRadiusService;
use Common\Model\Radius\RadcheckModel;
/*
 * 購買上網卡的手機頁面支付寶接口
 */
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';
        }
    }
}

  








  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved