laravel實現支付寶支付功能

原由

前段時間由於項目中須要實現支付寶手機網站支付功能,因此寫下這篇文章以做記錄,不足之處,歡迎指教。php

 

 

後端框架:Laravel 5.5

 

 

業務功能

適用於商家在移動端網頁應用中集成支付寶支付功能。商家在網頁中調用支付寶提供的網頁支付接口調起支付寶客戶端內的支付模塊,商家網頁會跳轉到支付寶中完成支付,支付完後跳回到商家網頁內,最後展現支付結果。若沒法喚起支付寶客戶端,則在必定的時間後會自動進入網頁支付流程。html

 

一. 建立應用

連接:支付寶螞蟻金服開放平臺laravel

注意:git

  1. 需擁有實名認證的支付寶帳戶。
  2. 企業或個體工商戶可申請
  3. 須要有真實有效的營業執照,切網站必須經過ICP備案

進入螞蟻金服開放平臺->開發者中心->網頁&移動應用。按需求建立應用,在這裏我建立的是網頁/移動類應用github

建立完成後提交審覈,大部分應用須要簽約後才能使用,簽約須要營業執照。express

 

二. 配置應用環境

    

        

配置完成後,可提交審覈,開發者點擊提交審覈後,預計會有一個工做日的審覈時間。應用上線成功後,狀態變爲以上線,該狀態下的應用可以調用生產環境的接口。json

 

三. 接口調用配置

目前laravel中集成alipay SDK的支付接口很豐富。經常使用的有下面幾種:後端

OmniPay-laravel:github OmniPay-laravel連接api

latrell/alipay:github latrell/alipay連接服務器

...

由於項目的須要,在這裏我採用的是alipay的原生SDK包。

首先下載PHP版本的Demo:支付寶手機網站支付PHP demo

從index.php中能夠看出該demo支持如下功能

手機網站2.0支付(接口名:alipay.trade.wap.pay)
手機網站2.0訂單查詢 (接口名:alipay.trade.query)
手機網站2.0訂單退款  (接口名:alipay.trade.refund)
手機網站2.0訂單退款查詢(接口名:alipay.trade.fastpay.refund.query)
手機網站2.0帳單下載(接口名:alipay.data.dataservice.bill.downloadurl.query)

其中config.php是配置文件:

<?php
$config = array (    
        //應用ID,您的APPID。
        'app_id' => "",

        //商戶私鑰,您的原始格式RSA私鑰
        'merchant_private_key' => "",
        
        //異步通知地址
        'notify_url' => "",
        //http://工程公網訪問地址/alipay.trade.wap.pay-PHP-UTF-8/notify_url.php

        //同步跳轉
        'return_url' => "",
        //http://mitsein.com/alipay.trade.wap.pay-PHP-UTF-8/return_url.php
        // jk.mrwangqi.com

        //編碼格式
        'charset' => "UTF-8",

        //簽名方式
        'sign_type'=>"RSA2",

        //支付寶網關
        'gatewayUrl' => "https://openapi.alipay.com/gateway.do",

        //支付寶公鑰,查看地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。
        'alipay_public_key' => "",
);

配置完成後,修改demo權限
sudo chmod -R 777 alipayDemo

 

訪問demo下的index.php

這樣子這個demo就能夠運行了。

 

 

具體開發

如今下載SDK:支付寶手機網站支付PHP SDK

 

一.  引入SDK包

        在laravel中引入SDK包的步驟:

  1.     在app/新建libs文件夾,將SDK包放在該目錄下

       2.   找到根目錄下的composer.json文件,添加以下配置:

"autoload": {  
        "classmap": [  
            "database",  
            "app/libs/alipay" //這裏是自定義包的文件位置,我將我項目中的該SDK包命名爲alipay 
        ],  
        "psr-4": {  
            "App\\": "app/"  
        }  
    },  

 

        3.   執行如下命令

composer dump-autoload     //當在包中加入新的類,須要更新autoloader  

 

二.  移動/新建文件

在alipay目錄下新建wappay目錄,在wappay目錄下新建buildermodelservice兩個目錄。將上面demo目錄下的wappay/buildermodel/AlipayTradeWapPayContentBuilder.phpwappay/service/AlipayTradeService.php兩個文件分別複製到本身項目SDK包中新建的wappay中的相應目錄下。

AlipayTradeWapPayContentBuilder.php是alipay demo對支付寶手機網站支付接口業務參數的封裝。AlipayTradeService.php是alipay demo對支付寶手機網站支付接口業務功能的封裝。

在SDK目錄下新建log.txt。做爲支付寶支付日誌存放文件

 

三.  設置/引入命名空間

AlipayTradeWapPayContentBuilder.phpAlipayTradeService.php設置命名空間,我設置的是

namespace App\libs\alipay\wappay\buildermodel;
namespace App\libs\alipay\wappay\buildermodel;

 

alipay/aop/request/AlipayTradeWapPayRequest.phpalipay/aop/AopClient.php設置命名空間,我設置的是:

namespace App\libs\alipay\aop\request;
namespace App\libs\alipay\aop;

 

AlipayTradeWapPayContentBuilder.php中引入上面兩個命名空間:

use App\libs\alipay\aop\request\AlipayTradeWapPayRequest;
use App\libs\alipay\aop\AopClient;

 

AlipayTradeService.php中的下面代碼註釋:

// require_once dirname ( __FILE__ ).DIRECTORY_SEPARATOR.'./../../AopSdk.php';
// require dirname ( __FILE__ ).DIRECTORY_SEPARATOR.'./../../config.php';

 

四.  配置config(alipay.php)

在上面中alipay的demo中是有一個config.php文件做爲配置文件的,這裏咱們不須要這個文件,咱們利用laravel的特性,在laravel項目目錄下的config目錄新建一個alipay.php:

return [

            //應用ID,您的APPID。
        'app_id' => "",

        //商戶私鑰,您的原始格式RSA私鑰
        'merchant_private_key' => "",
        
        //異步通知地址
        'notify_url' => "",
        //http://工程公網訪問地址/alipay.trade.wap.pay-PHP-UTF-8/notify_url.php

        //同步跳轉
        'return_url' => "",
        //http://mitsein.com/alipay.trade.wap.pay-PHP-UTF-8/return_url.php
        // jk.mrwangqi.com

        //編碼格式
        'charset' => "UTF-8",

        //簽名方式
        'sign_type'=>"RSA2",

        //支付寶網關
        'gatewayUrl' => "https://openapi.alipay.com/gateway.do",

        //支付寶公鑰,查看地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。
        'alipay_public_key' => "",
];

 

五.  對應config修改函數

在alipay.php中進行配置支付接口所需參數。下面咱們修改alipay/wappay/service/AlipayTradeService.php

class AlipayTradeService {

    //支付寶網關地址
    public $gateway_url = "https://openapi.alipay.com/gateway.do";

    //支付寶公鑰
    public $alipay_public_key;

    //商戶私鑰
    public $private_key;

    //應用id
    public $appid;

    //編碼格式
    public $charset = "UTF-8";

    public $token = NULL;
    
    //返回數據格式
    public $format = "json";

    //簽名方式
    public $signtype = "RSA";

    function __construct(){
        $this->gateway_url = config('alipay.gatewayUrl'); //得到config文件夾下的alipay.php中的gatewayUrl參數,下同。
        $this->appid = config('alipay.app_id');
        $this->private_key = config('alipay.merchant_private_key');
        $this->alipay_public_key = config('alipay.alipay_public_key');
        $this->charset = config('alipay.charset');
        $this->signtype= config('alipay.sign_type');

        if(empty($this->appid)||trim($this->appid)==""){
            throw new Exception("appid should not be NULL!");
        }
        if(empty($this->private_key)||trim($this->private_key)==""){
            throw new Exception("private_key should not be NULL!");
        }
        if(empty($this->alipay_public_key)||trim($this->alipay_public_key)==""){
            throw new Exception("alipay_public_key should not be NULL!");
        }
        if(empty($this->charset)||trim($this->charset)==""){
            throw new Exception("charset should not be NULL!");
        }
        if(empty($this->gateway_url)||trim($this->gateway_url)==""){
            throw new Exception("gateway_url should not be NULL!");
        }

    }
    function AlipayWapPayService($alipay_config) {
        $this->__construct($alipay_config);
    }

    /**
     * alipay.trade.wap.pay
     * @param $builder 業務參數,使用buildmodel中的對象生成。
     * @param $return_url 同步跳轉地址,公網可訪問
     * @param $notify_url 異步通知地址,公網能夠訪問
     * @return $response 支付寶返回的信息
     */
    function wapPay($builder,$return_url,$notify_url) {
    
        $biz_content=$builder->getBizContent();
        //打印業務參數
        $this->writeLog($biz_content);
    
        $request = new AlipayTradeWapPayRequest();
    
        $request->setNotifyUrl($notify_url);
        $request->setReturnUrl($return_url);
        $request->setBizContent ( $biz_content );
    
        // 首先調用支付api
        $response = $this->aopclientRequestExecute ($request,true);
        // $response = $response->alipay_trade_wap_pay_response;
        return $response;
    }

     function aopclientRequestExecute($request,$ispage=false) {

        $aop = new AopClient ();
        $aop->gatewayUrl = $this->gateway_url;
        $aop->appId = $this->appid;
        $aop->rsaPrivateKey =  $this->private_key;
        $aop->alipayrsaPublicKey = $this->alipay_public_key;
        $aop->apiVersion ="1.0";
        $aop->postCharset = $this->charset;
        $aop->format= $this->format;
        $aop->signType=$this->signtype;
        // 開啓頁面信息輸出
        $aop->debugInfo=true;
        if($ispage)
        {
            $result = $aop->pageExecute($request,"post");
            echo $result;
        }
        else 
        {
            $result = $aop->Execute($request);
        }
        
        //打開後,將報文寫入log文件
        $this->writeLog("response: ".var_export($result,true));
        return $result;
    }
    //請確保項目文件有可寫權限,否則打印不了日誌。
    function writeLog($text) {
        // $text=iconv("GBK", "UTF-8//IGNORE", $text);
        //$text = characet ( $text );
        file_put_contents ( dirname ( __FILE__ ).DIRECTORY_SEPARATOR."./../../log.txt", date ( "Y-m-d H:i:s" ) . "  " . $text . "\r\n", FILE_APPEND );
    }
}

?>

其餘接口暫時用不到,因此在這裏我將其隱去。

 

六.  新建控制器(AlipayController)

php artisan make:controller AlipayController

 

由於須要實現手機網站支付,因此須要定義支付接口:

<?php

namespace App\Http\Controllers\User\Alipay;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

use App\libs\alipay\wappay\buildermodel\AlipayTradeWapPayContentBuilder;
use App\libs\alipay\wappay\service\AlipayTradeService;

class AlipayWapController extends Controller {

     /**
      *支付接口
     */
    public function alipayWapPay(Request $request) {
        $out_trade_no = getTradeNOString();          //公共方法生成惟一訂單號
        $subject = 'test';                           //數據僅供測試,下同
        $total_amount = 0.01;
        $body = 'test test!';
        $timeout_express="1m";

        $payRequestBuilder = new AlipayTradeWapPayContentBuilder();
        $payRequestBuilder->setBody($body);
        
        $payRequestBuilder->setSubject($subject);
        $payRequestBuilder->setOutTradeNo($out_trade_no);
        $payRequestBuilder->setTotalAmount($total_amount);
       
        $payRequestBuilder->setTimeExpress($timeout_express);
        

        $payResponse = new AlipayTradeService();

        $result=$payResponse->wapPay($payRequestBuilder,config('alipay.return_url'),config('alipay.notify_url'));


    }

     /**
     *支付同步回調接口,在config/alipay.php的return_url參數進行配置

     */
    public function alipayReturn() {
        
    }

    /**
     *支付異步回調接口,在config/alipay.php的notify_url參數進行配置
     */
    public function alipayNotify() {

    }
}

 

七.  定義路由

定義支付路由及同步和異步回調路由

    Route::group(['prefix' => 'alipay'],function() {
        Route::get('wappay','AlipayWapController@alipayWapPay');
        Route::get('return','AlipayWapController@alipayReturn');
        Route::get('notify','AlipayWapController@alipayNotify');
    });

 

要注意的一點是同步路由是GET形式調用,而異步路由是POST形式調用,在調用支付接口的時候會出現CSRF錯誤,如今最簡單的方法是利用laravel的中間件避免CSRF,在app/Http/Middleware/VerifyCsrfToken.php中增長路由

    protected $except = [
        //
        'alipay/pay',
        'alipay/return',
        'alipay/notify'
    ];

 

八.  修改衝突

這時就能夠經過定義路由進行調用支付接口,可是在調用時會報下面這個錯誤:

Cannot redeclare Encrypt() (previously declared in .../vendor/laravel/lumen-framework/src/helpers.php:126)
//或:
Cannot redeclare Decrypt() (previously declared in .../vendor/laravel/lumen-framework/src/helpers.php:126)

 

這是由於Laravel 5使用Alipay SDK時,Laravel內帶的加密解密函數Encrypt()/Decrypt()函數和Alipay SDK中的加密解密函數Encrypt()/Decrypt()函數命名衝突

解決方法:只需修改Alipay SDK中定義的函數名稱,修改引用的函數名稱。

修改步驟:

    在Alipay SDK中,一共有須要修改三個文件的內容:

aop/AopEncrypt.php

aop/AopClient.php

lotusphp_runtime/Cookie/Cookie.php

 

    在文件中查找encrypt/decrypt替換爲alipayEncrypt/alipayDecrypt便可。

注:若是服務器是在Linux下,可能會報一個沒有權限的錯誤,這是由於咱們以前在SDK包中新建了一個log.txt,在alipay/wappay/service/AlipayTradeService.php中的writeLog()函數中向該文件寫入支付日誌時沒有寫入權限,給它個權限就行了。

 

 

結束

到此,在Laravel中支付寶手機網站支付功能就實現了,不足之處,歡迎請教。

相關文章
相關標籤/搜索