preface

As we all know, Alipay and wechat Alipay can only be limited to their own platforms. Wechat alipay can not be used at all. Even the public account payment also needs to jump to the external browser to arouse Alipay payment, and QQ browser arouses Alipay payment or many problems. So generally in the application of wechat ecology are generally not considered access to Alipay, but there are still many users have this need, today to give you a detailed access process!

Open alipay payment

  1. The opening process is omitted, you can see the help document :help.crmeb.net/crmeb_zsff/… .

This is the Alipay payment of CRMEB knowledge payment system, but the opening process is the same.

2. Download alipay Payment SDK

  • Download address: opendocs.alipay.com/open/54/103…
  • Note: The new SDK can be installed with Composer, but the old SDK cannot be installed with Composer

3. Create the Aliapay payment class

Create paths: crmeb/services/AlipayService. PHP, put the downloaded file decompression in the vendor list in the directory structure for: / vendor/alipay

4. AlipayService

Using singleton design mode, the old version of SDK of Alipay Payment cannot be loaded with Composer, which is very inelegant. It cannot be loaded when the class is initialized, and the first loading is very slow.


      
/ * * *@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;

    /** * Alipay *@var string
     */
    protected $alipayPrivateKey;

    /** * Sync callback address *@var string
     */
    protected $returnUrl;

    /** * Asynchronous callback address *@var string
     */
    protected $notifyUrl;

    /** * Request gateway *@var string
     */
    protected $gatewayUrl = 'https://openapi.alipay.com/gateway.do';

    /** * Whether to enable log *@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('Access method does not exist'); }}/** * initialize ali Cloud 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';
    }

    /** * Get parameter configuration */
    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(a); }return self: :$instance;
    }

    /** * Alipay payment synchronization callback */
    public static function aliPayReturn()
    {}/** * Pay pay synchronous callback */
    public static function handleNotify()
    {}/** * Order payment mobile website payment version *@paramString $outTradeNo *@paramString $totalAmount Order amount unit: yuan *@paramString $subject Order title *@paramString $passbackParams Order remarks are returned as is. Usually used for callback listening functions *@paramString $productCode Indicates the code of the product that the merchant signed with Alipay *@paramBool whether $isView outputs * directly@return$Response alipay returned information */
    public function aliPayWap(string $outTradeNo.string $totalAmount.string $subject.string $passbackParams.string $productCode = 'QUICK_MSECURITY_PAY'.bool $isView = true)
    {}/** * Unified purchase transaction refund interface *@paramString $outTradeNo Order number *@paramString $tradeNo alipay order number *@paramString $refundamt refundAmount *@paramString $refundReason Refund description *@paramString $passbackParams Remarks *@return$Response alipay returned information */
    public function aliPayRefund(string $outTradeNo.string $tradeNo.string $refundAmount.string $refundReason.string $passbackParams)
    {}/** * Set service parameters *@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);
        // Print the service parameters
        $this->isLog && $this->writeLog($bizContent);
        return $bizContent;
    }

    /** * Write log *@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); }}Copy the code

The first step is to put the parameters obtained from the database into the payment configuration

Create aopclientRequestExecute() and assign the parameters obtained from options() to Alipay. This method outputs or returns HTML text.

	/** * Initialize parameters *@param $request
     * @param bool $isView
     * @returnMixed | \ SimpleXMLElement | string | \ submission form HTML text *@throws \Exception
     */
    protected function aopclientRequestExecute(\AlipayTradeWapPayRequest $request.bool $isView = false)
    {
        $aop = new \AopClient();
        // Network management address
        $aop->gatewayUrl = $this->gatewayUrl;
        //appid
        $aop->appId = $this->alipayAppId;
        / / the private key
        $aop->rsaPrivateKey = $this->alipayPrivateKey;
        / / the public key
        $aop->alipayrsaPublicKey = $this->alipayPublicKey;
        / / version
        $aop->apiVersion = "1.0";
        // Encoding format
        $aop->postCharset = 'UTF-8';
        // Content format
        $aop->format = 'JSON';
        // Encryption mode
        $aop->signType = 'RSA2';
        // Enable the output of page information
        $aop->debugInfo = false;
        if ($isView) {
            $result = $aop->pageExecute($request."post");
            echo $result;
        } else {
            $result = $aop->Execute($request);
        }
        // After opening, write the packet to the log file
        $this->isLog && $this->writeLog($result);
        return $result;
    }
Copy the code

Create the order

Now that we have created the aliPayWap() method, let’s complete it. The logic of the aliPayWap() method is very simple, just pass in the order number, payment amount, order title, order remarks. Order comments are generally returned as is, so you can use order comments to make the asynchronous callback to execute the corresponding method, such as the user order pay and user recharge callback method is a callback method, for a callback method processing logic is more chaotic.

	/** * Order payment mobile website payment version *@paramString $outTradeNo *@paramString $totalAmount Order amount unit: yuan *@paramString $subject Order title *@paramString $passbackParams Order remarks are returned as is. Usually used for callback listening functions *@paramString $productCode Indicates the code of the product that the merchant signed with Alipay *@paramBool whether $isView outputs * directly@return$Response alipay returned information */
    public function aliPayWap(string $outTradeNo.string $totalAmount.string $subject.string $passbackParams.string $productCode = 'QUICK_MSECURITY_PAY'.bool $isView = true)
    {
        $request = new \AlipayTradeWapPayRequest();
		// Set the asynchronous callback address
        $request->setNotifyUrl($this->notifyUrl);
		// Set the synchronization callback address
        $request->setReturnUrl($this->returnUrl);
		// Format parameters using built-in methods
        $content = $this->setBizContent([
            'out_trade_no'= >$outTradeNo.'total_amount'= >$totalAmount.'subject'= >$subject.'passback_params'= >$passbackParams.'product_code'= >$productCode,]);// Set the single parameter
        $request->setBizContent($content);
		// Execute the request to place the order, and return the corresponding payment parameters
        return $this->aopclientRequestExecute($request.$isView);
    }
Copy the code

Order a refund

The aliPayRefund() method is responsible for refund processing, requiring parameters of order number, Alipay order number, refund amount, refund description, and remarks. Alipay order number needs to be updated in the database in the asynchronous payment callback or synchronous callback to facilitate refund processing.

/** * Unified purchase transaction refund interface *@paramString $outTradeNo Order number *@paramString $tradeNo alipay order number *@paramString $refundamt refundAmount *@paramString $refundReason Refund description *@paramString $passbackParams Remarks *@return$Response alipay returned information */
    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);
    }
Copy the code

The callback attestation

Mainly for asynchronous and synchronous callback verification signature and data processing convenient invocation

	/** * Check method *@paramArray $POST Checks the information returned by Alipay. The alipay public key is used. *@return boolean
     */
    protected function aliPaycheck(array $post)
    {
        $aop = new \AopClient();
        $aop->alipayrsaPublicKey = $this->alipayPublicKey;
        return $aop->rsaCheckV1($post.$this->alipayPrivateKey, 'RSA2');
    }
Copy the code

An asynchronous callback

To create an asynchronous callback route, modify the file :app/ API /route/v1.php, add the following code at the top, and don’t remember to create the corresponding AlipayController file

Route::any('alipay/notify'.'v1.alipay.AlipayController/notify');// Alipay payment callback
Copy the code

We first need to process the data returned by the asynchronous payment package. We added aliPayNotify() to resolve the parameters returned by the asynchronous callback.

	/** * Alipay asynchronous callback *@paramCallable $notifyFn closes function with argument 1, callback returns parameter, callback result *@return bool
     */
    protected function aliPayNotify(callable $notifyFn)
    {
        $post = app()->request->post();
        $result = $this->aliPaycheck($post);
        if ($result) {
            // The merchant order number
            $post['out_trade_no'] = isset($post['out_trade_no'])?$post['out_trade_no'] : ' ';
            // Alipay transaction number
            $post['trade_no'] = isset($post['trade_no'])?$post['trade_no'] : ' ';
            // Transaction status
            $post['trade_status'] = isset($post['trade_status'])?$post['trade_status'] : ' ';
            / / note
            $post['attach'] = isset($post['passback_params'])? urldecode($post['passback_params') :' ';
            // The asynchronous callback executed successfully
            try {
                if (is_callable($notifyFn)) $notifyFn((object)$post.$result);
            } catch (\Exception $e) {
                $this->isLog && $this->writeLog('Alipay paid successfully, order no. :' . $post['out_trade_no'].'. Callback error :' . $e->getMessage());
            }
            echo 'success';
        } else {
            echo 'fail';
        }
        $this->isLog && $this->writeLog($result);
        return true;

    }
Copy the code

This class calls the aliPayNotify() method

	/** * Alipay payment synchronization callback */
    public static function aliPayReturn()
    {
        self::instance()->aliPayNotify(function ($data.$result) {
			//$data for alipay callback to return the reorganized data after parsing
			$result = $result
			// This is where the asynchronous callback logic is written
			$data->attach = $data->attach
			// You can invoke events in TP6 to execute the corresponding business logic
			//event($data->attach,$data)
        });
    }
Copy the code

Synchronous callback

Synchronous callback uses the page skipped back after successful payment to verify whether the returned data is normal, record the Alipay order number, or other logic

Public static function handleNotify() {$get = app()->request->get(); $result = self::instance()->aliPaycheck($get); Self ::instance()->isLog && self::instance()->writeLog(compact('result', 'get')); self::instance()->isLog && self::instance()->writeLog(compact('result', 'get')); return compact('result', 'get'); }Copy the code

Other business logic can be written according to their own needs, the above is alipay payment order, refund, asynchronous and synchronous callback remember to enter the background to add the corresponding Alipay configuration.