TinkPHP框架開發的CRMEB小程序商城v4.0二次開發集成支付寶支付

前言php

你們都知道支付寶支付和微信支付寶都只能侷限在本身的平臺,微信內支付寶支付是根本就不能使用,即便是公衆號支付也須要跳轉到外部瀏覽器才能夠喚起支付寶支付,而且QQ瀏覽器喚起支付寶支付仍是問題不少,因此通常在微信生態內的應用通常都不考慮接入支付寶,但仍然有很多用戶有這方面的需求,今天就給你們作個詳細接入流程!數據庫

開通支付寶支付

  1. 開經過程省略,可查看幫助文檔:http://help.crmeb.net/crmeb_z...

這是crmeb知識付費系統的支付寶支付,不過開通的流程都是同樣的。json

2. 下載支付寶支付SDK後端

3. 建立aliapay支付類設計模式

建立路徑:crmeb/services/AlipayService.php,
把下載好的文件解壓在vendor目錄中目錄結構爲: /vendor/alipay/api

4. AlipayService瀏覽器

採用單例設計模式,支付寶支付的SDK舊版本不能用composer加載這就很不優雅了,不能在類初始化的時候加載,並且第一次載入時很是慢。微信

<?php
/**
 * @author: liaofei<136327134@qq.com>
 * @day: 2020/8/19
 */

namespace crmeb\services;

use think\exception\ValidateException;
use think\facade\Route as Url;
use think\facade\Log;

/**
 * Class AlipayService
 * @package crmeb\services
 */
class AlipayService
{
    /**
     * @var static
     */
    protected static $instance;

    /**
     * @var string
     */
    protected $alipayAppId;

    /**
     * @var string
     */
    protected $alipayPublicKey;

    /**
     * 支付寶
     * @var string
     */
    protected $alipayPrivateKey;

    /**
     * 同步回調地址
     * @var string
     */
    protected $returnUrl;

    /**
     * 異步回調地址
     * @var string
     */
    protected $notifyUrl;

    /**
     * 請求網關
     * @var string
     */
    protected $gatewayUrl = 'https://openapi.alipay.com/gateway.do';

    /**
     * 是否開啓日誌
     * @var bool
     */
    protected $isLog = false;

    /**
     * AlipayService constructor.
     */
    protected function __construct()
    {
        $this->initialize();
        $this->options();
    }

    /**
     * @param $name
     * @param $arguments
     */
    public function __call($name, $arguments)
    {
        if (strstr($name, 'set') !== false) {
            $name = ucwords(substr($name, 3, -1));
            if (in_array($name, ['returnUrl', 'isLog', 'notifyUrl', 'alipayPrivateKey', 'alipayAppId', 'alipayPublicKey'])) {
                $this->{$name} = $arguments[0];
            }
        } else {
            throw new ValidateException('訪問方法不存在');
        }
    }

    /**
     * 初始化加載阿里雲pay
     */
    protected function initialize()
    {
        $dir = app()->getRuntimePath() . 'alipay';
        if (!file_exists($dir)) {
            mkdir($dir, 0775, true);
        }
        define('AOP_SDK_WORK_DIR', $dir);
        include app()->getRootPath() . DS . 'vendor' . DS . 'alipay' . DS . 'AopSdk.php';
    }

    /**
     * 獲取參數配置
     */
    protected function options()
    {
        $this->alipayAppId = sys_config('alipay_app_id');
        $this->alipayPublicKey = sys_config('alipay_public_key');
        $this->alipayPrivateKey = sys_config('alipay_private_key');
        $this->returnUrl = Url::buildUrl('/api/alipay/synchro')->domain(true)->build();
        $this->notifyUrl = Url::buildUrl('/api/alipay/notify')->domain(true)->build();
    }

    /**
     * @return static
     */
    public static function instance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new static();
        }
        return self::$instance;
    }

    /**
     * 支付寶支付同步回調
     */
    public static function aliPayReturn()
    {
        
    }

    /**
     * 支付支付同步回調
     */
    public static function handleNotify()
    {

    }

    /**
     * 下單支付手機網站支付版本
     * @param string $outTradeNo 下單號
     * @param string $totalAmount 訂單金額 單位元
     * @param string $subject 訂單標題
     * @param string $passbackParams 訂單備註 會原樣返回一般用於回調監聽函數
     * @param string $productCode 銷售產品碼,商家和支付寶簽約的產品碼
     * @param bool $isView 是否直接輸出
     * @return $response 支付寶返回的信息
     */
    public function aliPayWap(string $outTradeNo, string $totalAmount, string $subject, string $passbackParams, string $productCode = 'QUICK_MSECURITY_PAY', bool $isView = true)
    {

    }

    /**
     * 統一收單交易退款接口
     * @param string $outTradeNo 下單訂單號
     * @param string $tradeNo 支付寶訂單號
     * @param string $refundAmount 退款金額
     * @param string $refundReason 退款說明
     * @param string $passbackParams 備註
     * @return $response 支付寶返回的信息
     */
    public function aliPayRefund(string $outTradeNo, string $tradeNo, string $refundAmount, string $refundReason, string $passbackParams)
    {

    }
    
    /**
     * 設置業務參數
     * @param array $biz_content
     * @return string
     */
    protected function setBizContent(array $bizContent = [])
    {
        if (isset($bizContent['passback_params'])) $bizContent['passback_params'] = urlencode($bizContent['passback_params']);
        if (isset($bizContent['trade_no']) && empty($bizContent['trade_no'])) unset($bizContent['trade_no']);
        $bizContent = json_encode($bizContent);
        //打印業務參數
        $this->isLog && $this->writeLog($bizContent);
        return $bizContent;
    }

    /**
     * 寫入日誌
     * @param $content string | array | object
     * @return Log
     */
    protected function writeLog($content)
    {
        if (is_array($content)) $content = 'response: ' . var_export($content, true);
        if (is_object($content)) $content = 'response: ' . var_export($content, true);
        return Log::write(date('Y-m-d H:i:s', time()) . '   ' . $content);
    }


}

首先要把從數據庫中獲取到的參數放入支付配置裏

建立aopclientRequestExecute()方法把 options()方法獲取到的參數賦值給支付寶此方法會輸出或者直接返回HTML的文本,先後端分離可直接用返回提交數據.閉包

/**
     * 初始化參數
     * @param $request
     * @param bool $isView
     * @return mixed|\SimpleXMLElement|string|\提交表單HTML文本
     * @throws \Exception
     */
    protected function aopclientRequestExecute(\AlipayTradeWapPayRequest $request, bool $isView = false)
    {
        $aop = new \AopClient();
        //網管地址
        $aop->gatewayUrl = $this->gatewayUrl;
        //appid
        $aop->appId = $this->alipayAppId;
        //私鑰
        $aop->rsaPrivateKey = $this->alipayPrivateKey;
        //公鑰
        $aop->alipayrsaPublicKey = $this->alipayPublicKey;
        //版本
        $aop->apiVersion = "1.0";
        //編碼格式
        $aop->postCharset = 'UTF-8';
        //內容格式
        $aop->format = 'JSON';
        //加密方式
        $aop->signType = 'RSA2';
        // 開啓頁面信息輸出
        $aop->debugInfo = false;
        if ($isView) {
            $result = $aop->pageExecute($request, "post");
            echo $result;
        } else {
            $result = $aop->Execute($request);
        }
        //打開後,將報文寫入log文件
        $this->isLog && $this->writeLog($result);
        return $result;
    }

建立訂單

上面已經建立好aliPayWap()方法,接下來咱們來完成它,aliPayWap()方法的邏輯也很是的簡單,只須要傳入訂單號,支付金額,訂單標題,訂單備註.訂單備註通常會原樣返回的,這樣能夠利用訂單備註來讓異步回調執行對應的方法
例若有用戶下單支付和用戶充值回調回調方法爲一個,爲一個回調方法處理的邏輯就比較混亂。app

/**
     * 下單支付手機網站支付版本
     * @param string $outTradeNo 下單號
     * @param string $totalAmount 訂單金額 單位元
     * @param string $subject 訂單標題
     * @param string $passbackParams 訂單備註 會原樣返回一般用於回調監聽函數
     * @param string $productCode 銷售產品碼,商家和支付寶簽約的產品碼
     * @param bool $isView 是否直接輸出
     * @return $response 支付寶返回的信息
     */
    public function aliPayWap(string $outTradeNo, string $totalAmount, string $subject, string $passbackParams, string $productCode = 'QUICK_MSECURITY_PAY', bool $isView = true)
    {
        $request = new \AlipayTradeWapPayRequest();
        //設置異步回調地址
        $request->setNotifyUrl($this->notifyUrl);
        //設置同步回調地址
        $request->setReturnUrl($this->returnUrl);
        //用內置方法格式化參數
        $content = $this->setBizContent([
            'out_trade_no' => $outTradeNo,
            'total_amount' => $totalAmount,
            'subject' => $subject,
            'passback_params' => $passbackParams,
            'product_code' => $productCode,
        ]);
        //設置下單參數
        $request->setBizContent($content);
        //執行請求進行下單,返回對應的支付參數
        return $this->aopclientRequestExecute($request, $isView);
    }

訂單退款

aliPayRefund()方法負責退款處理,須要參數下單訂單號,支付寶訂單號,退款金額,退款說明,備註.支付寶訂單號須要在異步支付回調中或者同步回調中獲取更新在數據庫中,方便退款處理。

/**
     * 統一收單交易退款接口
     * @param string $outTradeNo 下單訂單號
     * @param string $tradeNo 支付寶訂單號
     * @param string $refundAmount 退款金額
     * @param string $refundReason 退款說明
     * @param string $passbackParams 備註
     * @return $response 支付寶返回的信息
     */
    public function aliPayRefund(string $outTradeNo, string $tradeNo, string $refundAmount, string $refundReason, string $passbackParams)
    {
        $request = new \AlipayTradeRefundRequest();
        $content = $this->setBizContent([
            'out_trade_no' => $outTradeNo,
            'trade_no' => $tradeNo,
            'refund_amount' => $refundAmount,
            'passback_params' => $passbackParams,
            'refund_reason' => $refundReason,
            'product_code' => $passbackParams,
        ]);
        $request->setBizContent($content);
        return $this->aopclientRequestExecute($request);
    }

回調驗籤

主要給異步和同步回調驗證簽名和數據處理方便調用

/**
     * 驗籤方法
     * @param array $post 驗籤支付寶返回的信息,使用支付寶公鑰。
     * @return boolean
     */
    protected function aliPaycheck(array $post)
    {
        $aop = new \AopClient();
        $aop->alipayrsaPublicKey = $this->alipayPublicKey;
        return $aop->rsaCheckV1($post, $this->alipayPrivateKey, 'RSA2');
    }

異步回調

建立異步回調路由,修改文件:app/api/route/v1.php,在頂部中增長如下代碼,別忘了建立對應的AlipayController文件

Route::any('alipay/notify', 'v1.alipay.AlipayController/notify');//支付寶支付回調

首先須要處理支付包異步返回來的數據,新增aliPayNotify()方法來解析處理異步回調返回的參數。

/**
     * 支付寶異步回調
     * @param callable $notifyFn 閉包函數 參數1,回調返回的參數,回調結果
     * @return bool
     */
    protected function aliPayNotify(callable $notifyFn)
    {
        $post = app()->request->post();
        $result = $this->aliPaycheck($post);
        if ($result) {
            //商戶訂單號
            $post['out_trade_no'] = isset($post['out_trade_no']) ? $post['out_trade_no'] : '';
            //支付寶交易號
            $post['trade_no'] = isset($post['trade_no']) ? $post['trade_no'] : '';
            //交易狀態
            $post['trade_status'] = isset($post['trade_status']) ? $post['trade_status'] : '';
            //備註
            $post['attach'] = isset($post['passback_params']) ? urldecode($post['passback_params']) : '';
            //異步回調成功執行
            try {
                if (is_callable($notifyFn)) $notifyFn((object)$post, $result);
            } catch (\Exception $e) {
                $this->isLog && $this->writeLog('支付寶支付成功,訂單號爲:' . $post['out_trade_no'] . '.回調報錯:' . $e->getMessage());
            }
            echo 'success';
        } else {
            echo 'fail';
        }
        $this->isLog && $this->writeLog($result);
        return true;

    }

執行instance()方法實例本類調用aliPayNotify()方法

/**
     * 支付寶支付同步回調
     */
    public static function aliPayReturn()
    {
        self::instance()->aliPayNotify(function ($data, $result) {
            //$data爲支付寶回調返回經過解析後重組的數據
            //$result爲驗簽結果
            //這裏要寫異步回調的邏輯
            //可根據$data->attach來判斷須要執行的邏輯
            //能夠調用TP6中的事件來執行對應的業務邏輯
            //event($data->attach,$data)
        });
    }

同步回調

同步回調使用在支付成功後跳轉回去的頁面一遍驗證下返回的數據是否正常,記錄下支付寶訂單號,或者其餘邏輯

/**
     * 支付支付同步回調
     */
    public static function handleNotify()
    {
        //獲取返回參數
        $get = app()->request->get();
        //驗籤成功與否
        $result = self::instance()->aliPaycheck($get);
        //記錄日誌
        self::instance()->isLog && self::instance()->writeLog(compact('result', 'get'));
        return compact('result', 'get');
    }

其餘的業務邏輯可根據自身需求編寫,以上就是支付寶支付的下單,退款,異步同步回調
記得進入後臺增長對應的支付寶配置。

相關文章
相關標籤/搜索