tp5實現支付寶電腦支付(詳解)

首先吐槽一下支付寶的官方文檔,它只是簡單介紹一下開發的流程和參數,而對於新人來講若是隻看它的官方文檔不少時候是看不懂的,我也是邊看文檔邊網上查資料才把它弄懂。下面我詳細介紹支付寶的電腦支付是如何實現php

電腦網站支付數據庫

第一步:進入官網,在電腦網站支付下載它的demojson

第二步:沙箱環境api

想要實現支付寶支付,你要準備一堆東西,對於只想測試的人來講這太麻煩了,而支付寶爲咱們提供了沙箱環境,這裏面有咱們開發測試須要的東西數組

進入沙箱環境,在沙箱應用裏下載沙箱錢包(只支持掃一掃、付款碼、門店詳情頁功能,其他功能不提供),它是用戶測試掃碼支付用的瀏覽器

第三步:把demo放進項目服務器

通常來講支付寶demo屬於擴展文件,因此我把它放到extend目錄下cookie

而後把config.php放到application\extra(注:能夠不放,我放在裏面只是方便調用它配置參數)session

第四步: 填寫配置參數php7

這裏的參數在沙箱環境都已經有,你就根據它的註釋填寫就能夠

支付寶的公鑰和商戶私鑰,你要下載"支付寶開放平臺開發助手",它能夠生成祕鑰,而後把應用公鑰放在設置裏面

這樣他就生成支付寶公鑰,你就把它放到alipay的裏面的支付寶公鑰,而應用私鑰直接放到alipay的商戶祕鑰

這裏我主要說一下同步和異步通知地址,支付寶支付成功後會執行這兩個方法(注:同步是支付寶支付成功要跳轉的地址),系統會把你支付的信息用POST方式異步傳給你的方法。由於是異步,因此頁面是沒有變化的,在異步這個方法裏能夠寫你本身的業務邏輯。好比接收值,存入數據庫之類。這裏有個大坑,坑了我兩天,即在異步方法裏是無法用session取值的,我本來想用session取用戶登陸id存入數據庫中,後來問了師傅才知道,異步是服務器和服務器之間的交互,因此沒有cookieId,沒有cookieId固然沒有session值。

注意:異步通知程序執行完後必須打印輸出「success」(不包含引號)。若是商戶反饋給支付寶的字符不是success這7個字符,支付寶服務器會不斷重發通知,直到超過24小時22分鐘。通常狀況下,25小時之內完成8次通知(通知的間隔頻率通常是:4m,10m,10m,1h,2h,6h,15h);

第五步: 調用支付接口

首先咱們要修改extend\alipay\pagepay\Pagepay.php,由於直接調用的話會報錯:

<?php
use think\Loader;
Loader::import("alipay.pagepay.service.AlipayTradeService",EXTEND_PATH);
Loader::import('alipay.pagepay.buildermodel.AlipayTradePagePayContentBuilder',EXTEND_PATH);

class Pagepay
{
    //支付入口
    public static function pay($params)
    {
        //第一步:校檢參數
        self::checkParams($params);

        //第二步:構造參數
        $payRequestBuilder = new AlipayTradePagePayContentBuilder();
        $payRequestBuilder->setBody($params['t_body']);//描述
        $payRequestBuilder->setSubject($params['trade_name']);//訂單名稱,必填
        $payRequestBuilder->setTotalAmount($params['total_amount']);//付款金額,必填
        $payRequestBuilder->setOutTradeNo($params['out_trade_no']);//商戶訂單號,商戶網站訂單系統中惟一訂單號,必填

        //第三步:獲取配置
        $config = config('alipay');
        $aop = new AlipayTradeService($config);

        /**
         * 第四步:電腦網站支付請求(會自動跳轉到支付頁面)
         * @param $builder 業務參數,使用buildmodel中的對象生成。
         * @param $return_url 同步跳轉地址,公網能夠訪問
         * @param $notify_url 異步通知地址,公網能夠訪問
         * @return $response 支付寶返回的信息
         */
        $aop->pagePay($payRequestBuilder, $config['return_url'], $config['notify_url']);
    }

    //支付檢驗
    private static function checkParams($params)
    {
        //商戶訂單號
        if(empty(trim($params['out_trade_no']))){
            self::processError("你輸入的商戶訂單號有誤!");
        }
        //訂單名稱
        if(empty(trim($params['trade_name']))){
            self::processError("訂單名稱爲空!");
        }
        //付款金額
        if(floatval(trim($params['total_amount'])) <= 0){
            self::processError("付款金額有誤!!");
        }
    }

    //統一錯誤處理接口
    private static function processError($msg)
    {
        throw new \think\Exception($msg);
    }

}
View Code

修改完以後咱們就能夠在控制器裏調用支付寶接口:

<?php
namespace app\aliyun\controller;
use think\Controller;

//支付控制器
class Pay extends Controller
{
    public function index(){
        return $this->fetch();
    }

    //電腦支付寶接口
    public function index2(){
        //付款金額
        $total_amount = input('WIDtotal_amount');
        if($total_amount){
            $params = [
                //商戶訂單號,商戶網站訂單系統中惟一訂單號,必填
                'out_trade_no'  =>  input('WIDout_trade_no'),
                //訂單名稱,必填
                'trade_name'       =>  input('WIDsubject'),
                //付款金額,必填
                'total_amount'  =>  $total_amount,
                //描述
                't_body'        =>  input('WIDbody'),
            ];

            import('alipay.pagepay.Pagepay',EXTEND_PATH);
            \Pagepay::pay($params);
        }
    }

    //異步通知地址
    public function notify_url(){
        /* *
         *************************頁面功能說明*************************
         * 建立該頁面文件時,請留心該頁面文件中無任何HTML代碼及空格。
         * 該頁面不能在本機電腦測試,請到服務器上作測試。請確保外部能夠訪問該頁面。
         * 若是沒有收到該頁面返回的 success 信息,支付寶會在24小時內按必定的時間策略重發通知
         */
        import('alipay.pagepay.service.AlipayTradeService',EXTEND_PATH);

        //$arr = $this->request->param();使用這個沒法接受數據
        $arr = $_POST;
        $alipaySevice = new \AlipayTradeService(config('alipay'));
        $alipaySevice->writeLog(var_export($arr, true));
        $result = $alipaySevice->check($arr);

        /* 實際驗證過程建議商戶添加如下校驗。
        一、商戶須要驗證該通知數據中的out_trade_no是否爲商戶系統中建立的訂單號,
        二、判斷total_amount是否確實爲該訂單的實際金額(即商戶訂單建立時的金額),
        三、校驗通知中的seller_id(或者seller_email) 是否爲out_trade_no這筆單據的對應的操做方(有的時候,一個商戶可能有多個seller_id/seller_email)
        四、驗證app_id是否爲該商戶自己。
        */
        if ($result) {//驗證成功
            //商戶訂單號
            $out_trade_no = $arr['out_trade_no'];
            //支付寶交易號
            $trade_no = $arr['trade_no'];
            //交易狀態
            $trade_status = $arr['trade_status'];

            //TRADE_FINISHED:交易完成,  TRADE_SUCCESS:支付成功
            if ($arr['trade_status'] == 'TRADE_FINISHED' || $arr['trade_status'] == 'TRADE_SUCCESS') {
                //作邏輯處理,例如:保存數據
                file_put_contents("../extend/sites.txt",implode(",",$arr).PHP_EOL,FILE_APPEND | LOCK_EX);
            }
            echo "success";
        } else {
            //驗證失敗
            echo "fail";
        }
    }

    //同步跳轉,支付成功,由客戶的瀏覽器觸發的一個通知
    public function return_url(){
        /* *
         * 功能:支付寶頁面跳轉同步通知頁面
         * 版本:2.0
         * 修改日期:2017-05-01
         * 說明:
         * 如下代碼只是爲了方便商戶測試而提供的樣例代碼,商戶能夠根據本身網站的須要,按照技術文檔編寫,並不是必定要使用該代碼。

         *************************頁面功能說明*************************
         * 該頁面可在本機電腦測試
         * 可放入HTML等美化頁面的代碼、商戶業務邏輯程序代碼
         */
        import('alipay.pagepay.service.AlipayTradeService',EXTEND_PATH);

        //$arr = $this->request->param();
        $arr = $_GET;
        $alipaySevice = new \AlipayTradeService(config('alipay'));
        $result = $alipaySevice->check($arr);

        /* 實際驗證過程建議商戶添加如下校驗。
        一、商戶須要驗證該通知數據中的out_trade_no是否爲商戶系統中建立的訂單號,
        二、判斷total_amount是否確實爲該訂單的實際金額(即商戶訂單建立時的金額),
        三、校驗通知中的seller_id(或者seller_email) 是否爲out_trade_no這筆單據的對應的操做方(有的時候,一個商戶可能有多個seller_id/seller_email)
        四、驗證app_id是否爲該商戶自己。
        */
        if ($result) {//驗證成功
            //作邏輯處理,例如:保存數據
            echo "<pre>";
            print_r($arr);
            //file_put_contents("../extend/sites.txt",implode(",",$arr).PHP_EOL,FILE_APPEND | LOCK_EX);
        } else {
            //驗證失敗
            echo "驗證失敗";
        }
    }
}

到了這一步基本就完成了,點擊付款就會跳轉到這個頁面,咱們可使用沙箱錢包掃碼支付或登陸沙箱帳號支付

 

 

付款遇到的問題及解決方案

若是是新手的第一次接入支付寶接口,或多或少遇到一些問題,今天我就把我遇到的問題做總結

問題一:調試錯誤,請回到請求來源地,從新發起請求

這頗有能夠就是你得網關有問題,沙箱環境支付寶網關以下:

https://openapi.alipaydev.com/gateway.do

沒有修改以前demo文件支付寶網關是已經寫好,但默認的網關沒有加dev,若是你在沙箱環境中必須加上dev

問題二:each()函數報錯

支付寶支付的時候遇到的,由於php7+以上版本拋棄了each函數致使,找到extend\alipay\aop\AopClient.php

while (list ($key, $val) = each ($para_temp)) {

改成

foreach ($para_temp as $key => $val) {

 問題三:收不到異步通知自查方案-支付寶接口常見錯誤系列

  1.需http://或者https://格式的完整路徑 
  例:https://您的域名/notify_url.php  ,支持ip地址方式。(推薦使用域名) 

  2.不能加?id=123這類自定義參數 
  錯誤示例:https://您的域名/notify_url.php?id=123&test=abc 

  3.必須外網能夠正常訪問,這個不難理解,在您的異步地址沒有代碼邏輯的狀況下,直接訪問應該是一個空白頁面而且http狀態是200(不支持http200之外的狀態),即不能在本機電腦測試,要到服務器上作測試,同時也要確保外部能夠訪問該頁面。

  4.不能有重定向 如:http302 

  5.異步通知,經過 POST 請求的形式將支付結果做爲參數通知到商戶系統,使用POST方式接收,請確保服務器路由已經開放POST通知 

支付寶社區:https://openclub.alipay.com/club/history/read/1314

 總結:

一、notify_url:經過 POST 請求的形式將支付結果做爲參數通知到商戶系統,使用$_POST獲取數據

二、return_url:經過 GET 請求的形式,使用$_GET獲取數據

三、notify_url是異步通知,return_url是同步跳轉;異步沒法使用Cookie和Session保存數據

 

 

 

查詢交易記錄

修改extend\alipay\pagepay\Query.php,由於直接調用的話會報錯:

<?php
use think\Loader;
Loader::import('alipay.pagepay.service.AlipayTradeService',EXTEND_PATH);
Loader::import('alipay.pagepay.buildermodel.AlipayTradeQueryContentBuilder',EXTEND_PATH);

class Query
{
    // 商戶訂單號(out_trade_no) or 支付寶交易號(trade_no) 二選一
    const QUERY_TYPE = 'out_trade_no';

    public static function exec($query_no)
    {
        // 1.設置請求參數
        $RequestBuilder = new \AlipayTradeQueryContentBuilder();
        if (self::QUERY_TYPE == 'trade_no') {
            $RequestBuilder->setTradeNo(trim($query_no));
        } else {
            $RequestBuilder->setOutTradeNo($query_no);
        }

        // 2.獲取配置
        $config = config('alipay');
        $aop    = new \AlipayTradeService($config);

        // 3.發起請求
        $response = $aop->Query($RequestBuilder);

        // 4.轉爲數組格式返回
        $response = json_decode(json_encode($response), true);

        // 5.進行結果處理
        if (!empty($response['code']) && $response['code'] != '10000') {
            self::processError('交易查詢接口出錯, 錯誤碼: '.$response['code'].' 錯誤緣由: '.$response['sub_msg']);
        }

        return $response;
    }


    /**
     * 統一錯誤處理接口
     * @param  string $msg 錯誤描述
     */
    private static function processError($msg)
    {
        throw new \think\Exception($msg);
    }
}
View Code

商戶訂單號和支付寶交易號二選一,控制器這裏默認獲取商戶訂單號:

    //交易查詢
    public function query(){
        //商戶訂單號
        $trade_no = input('WIDTQout_trade_no');
        echo $trade_no;
//        return;

        if($trade_no){
            import('alipay.pagepay.Query',EXTEND_PATH);
            $response = \Query::exec($trade_no);
            print_r($response);
        }
    }

注意:商戶訂單號和支付寶交易號不能同時爲空。 trade_no、 out_trade_no若是同時存在優先取trade_no

 

 

退款

修改extend\alipay\pagepay\Refund.php

<?php
use think\Loader;
Loader::import('alipay.pagepay.service.AlipayTradeService',EXTEND_PATH);
Loader::import('alipay.pagepay.buildermodel.AlipayTradeRefundContentBuilder',EXTEND_PATH);

class Refund
{
    /**
     * 主入口
     * @param  array $params 退款參數, 具體以下
     * @param string $params['trade_no']/$params['out_trade_no'] 商戶訂單號或支付寶單號其中之一
     * @param string $params['out_request_no'] 商戶退款號(可選, 如同一筆交易屢次退款, 則必填)
     * @param float  $params['refund_amount'] 退款金額
     */
    public static function exec($params)
    {
        // 1.校檢參數
        self::checkParams($params);

        // 2.構造請求參數
        $RequestBuilder = self::builderParams($params);

        // 3.獲取配置
        $config = config('alipay');
        $aop    = new \AlipayTradeService($config);

        // 4.發送請求
        $response = $aop->Refund($RequestBuilder);

        // 5.轉爲數組格式返回
        $response = json_decode(json_encode($response), true);

        // 6.進行結果處理
        if (!empty($response['code']) && $response['code'] != '10000') {
            self::processError('交易退款接口出錯, 錯誤碼: '.$response['code'].' 錯誤緣由: '.$response['sub_msg']);
        }

        return $response;
    }

    /**
     * 校檢參數
     */
    private static function checkParams($params)
    {
        if (empty($params['trade_no']) && empty($params['out_trade_no'])) {
            self::processError('商戶訂單號(out_trade_no)和支付寶單號(trade_no)不得通知爲空');
        }

        if (floatval(trim($params['refund_amount'])) <= 0) {
            self::processError('退款金額(refund_amount)爲大於0的數');
        }
    }

    /**
     * 構造請求參數
     */
    private static function builderParams($params)
    {
        $RequestBuilder = new \AlipayTradeRefundContentBuilder();

        // 1.判斷單號類型
        if (isset($params['trade_no'])) {
            $RequestBuilder->setTradeNo($params['trade_no']);
        } else {
            $RequestBuilder->setOutTradeNo($params['out_trade_no']);
        }

        // 2.判斷是否部分退款
        if (!empty($params['out_request_no'])) {
            $RequestBuilder->setOutRequestNo($params['out_request_no']);
        }

        $RequestBuilder->setRefundAmount($params['refund_amount']);
        return $RequestBuilder;
    }

    /**
     * 統一錯誤處理接口
     * @param  string $msg 錯誤描述
     */
    private static function processError($msg)
    {
        throw new \think\Exception($msg);
    }
}
View Code

商戶訂單號和支付寶交易號二選一,控制器這裏默認商戶訂單號:

    //退款
    public function refund(){
        //付款金額
        $total_amount = input('WIDTRrefund_amount');
        if($total_amount){
            $params = [
                //商戶訂單號,商戶網站訂單系統中惟一訂單號,必填
                'out_trade_no'      =>  input('WIDTRout_trade_no'),
                //付款金額,必填
                'refund_amount'     =>  $total_amount,
                //退款單號,若是是部分退款,必填
                'out_request_no'    =>  input('WIDTRout_request_no'),
            ];
            import('alipay.pagepay.Refund',EXTEND_PATH);
            $res = \Refund::exec($params);

            echo "<pre>";
            print_r($res);
        }
    }

退款單號(out_request_no):

一、全額退款不傳,部分退款必傳

二、out_request_no:標識一次退款請求,同一筆交易屢次退款須要保證惟一,如需部分退款,則此參數必傳。也能夠理解爲同一筆交易退款,退款金額小於付款金額是,必須傳這個參數,並且同一筆交易分屢次退款的話out_request_no每次傳值都不能重複,必須保證惟一性

 

 

退款查詢

修改extend\alipay\pagepay\RefundQuery.php

<?php
use think\Loader;
Loader::import('alipay.pagepay.service.AlipayTradeService',EXTEND_PATH);
Loader::import('alipay.pagepay.buildermodel.AlipayTradeFastpayRefundQueryContentBuilder',EXTEND_PATH);

class RefundQuery
{
    /**
     * 主入口
     * @param  array $params 退款查詢參數, 具體以下:
     * @param string $params['trade_no']/$params['out_trade_no'] 商戶訂單號或支付寶單號其中之一
     * @param string $params['out_request_no'] 可空, 爲空時, 退款號爲訂單號
     */
    public static function exec($params)
    {
        // 1.校檢參數
        if (empty($params['trade_no']) && empty($params['out_trade_no'])) {
            throw new \think\Exception('商戶訂單號(out_trade_no)和支付寶單號(trade_no)不得通知爲空');
        }

        // 2.構造請求參數
        $RequestBuilder = self::builderParams($params);

        // 3.獲取配置
        $config = config('alipay');
        $aop    = new \AlipayTradeService($config);

        // 4.發起請求
        $response = $aop->refundQuery($RequestBuilder);

        // 5.轉爲數組格式返回
        $response = json_decode(json_encode($response), true);

        // 6.進行結果處理
        if (!empty($response['code']) && $response['code'] != '10000') {
            self::processError('退款查詢接口出錯, 錯誤碼爲: '.$response['code'].', 錯誤緣由爲: '.$response['sub_msg']);
        }

        return $response;
    }

    /**
     * 構造請求參數
     */
    private static function builderParams($params)
    {
        $RequestBuilder = new \AlipayTradeFastpayRefundQueryContentBuilder();
        if (isset($params['trade_no'])) {
            $RequestBuilder->setTradeNo($params['trade_no']);
        } else {
            $RequestBuilder->setOutTradeNo($params['out_trade_no']);
        }

        // 若是未傳退款號, 則以單號爲退款號查詢
        if (isset($params['out_request_no'])) {
            $RequestBuilder->setOutRequestNo($params['out_request_no']);
        } else {
            $out_request_no = isset($params['trade_no']) ? $params['trade_no'] : $params['out_trade_no'];
            $RequestBuilder->setOutRequestNo($out_request_no);
        }

        return $RequestBuilder;
    }

    /**
     * 統一錯誤處理接口
     * @param  string $msg 錯誤描述
     */
    private static function processError($msg)
    {
        throw new \think\Exception($msg);
    }
}
View Code

商戶訂單號和支付寶交易號二選一,控制器這裏默認商戶訂單號:

    //退款查詢
    public function refundquery(){
        //商戶訂單號
        $params['out_trade_no'] = input('WIDRQout_trade_no');
        //退款請求號
        $params['out_request_no'] = input('WIDRQout_request_no');

        if($params['out_trade_no']){
            import('alipay.pagepay.RefundQuery',EXTEND_PATH);
            $response = \RefundQuery::exec($params);
            echo "<pre>";
            print_r($response);
        }
    }

注意:退款請求號值(out_request_no)必傳,退款時傳的值,若是退款時沒傳則沒法查詢;商戶訂單號和支付寶交易號不能同時爲空。 trade_no、 out_trade_no若是同時存在優先取trade_no

 

 

關閉交易

修改extend\alipay\pagepay\Close.php

<?php
use think\Loader;
Loader::import('alipay.pagepay.service.AlipayTradeService',EXTEND_PATH);
Loader::import('alipay.pagepay.buildermodel.AlipayTradeCloseContentBuilder',EXTEND_PATH);

class Close
{
    // 商戶訂單號(out_trade_no) or 支付寶交易號(trade_no) 二選一
    const QUERY_TYPE = 'out_trade_no';

    public static function exec($query_no)
    {
        // 1.構建請求參數
        $RequestBuilder = new \AlipayTradeCloseContentBuilder();
        if (self::QUERY_TYPE == 'trade_no') {
            $RequestBuilder->setTradeNo(trim($query_no));
        } else {
            $RequestBuilder->setOutTradeNo(trim($query_no));
        }

        // 2.獲取配置
        $config = config('alipay');
        $aop    = new \AlipayTradeService($config);

        // 3.發起請求
        $response = $aop->Close($RequestBuilder);

        // 4.轉爲數組格式返回
        $response = json_decode(json_encode($response), true);

        // 5.進行結果處理
        if (!empty($response['code']) && $response['code'] != '10000') {
            self::processError('交易關閉接口出錯, 錯誤碼: '.$response['code'].' 錯誤緣由: '.$response['sub_msg']);
        }

        return $response;
    }

    /**
     * 統一錯誤處理接口
     * @param  string $msg 錯誤描述
     */
    private static function processError($msg)
    {
        throw new \think\Exception($msg);
    }
}
View Code

商戶訂單號和支付寶交易號二選一,控制器這裏默認商戶訂單號:

    //關閉交易
    public function close(){
        //商戶訂單號
        $out_trade_no = input('WIDTCout_trade_no');

        if($out_trade_no){
            import('alipay.pagepay.Close',EXTEND_PATH);
            $response = \Close::exec($out_trade_no);
            echo "<pre>";
            print_r($response);
        }
    }

注意:

1.商戶訂單號和支付寶交易號不能同時爲空。 trade_no、 out_trade_no若是同時存在優先取trade_no

2.交易開始不是在點付款時,而是在掃碼付款但沒有支付時,這是才能夠關閉交易

相關文章
相關標籤/搜索