php 支付寶 SDK 單筆轉賬

沈小明發表於2020-11-08

支付寶單筆轉賬,記錄下使用過程和一些碰到的問題,以便為同樣需求的提供一些參考.

官方的一些文件:單筆轉賬 , SDK,如何生成及配置公鑰證照(視訊),公鑰證照

上面差不多是準備的工作,配置好後把三個證照都下載下來,這裡可能有一些用錯檔案的情況

php 支付寶 SDK 單筆轉賬

下載後的檔名稱

php 支付寶 SDK 單筆轉賬

<?php
//基礎配置程式碼

namespace App\Services\Alipay;


class AlipayBase{

    protected $aop;
    private $error;

    protected $appCertPath;
    protected $rootCertPath;
    protected $appId;
    protected $rsaPrivateKey;
    protected $alipayrsaPublicKey;
    protected $rsaPrivateKeyPath;
    protected $alipayrsaPublicKeyPath;

    protected  $payer_name = '支付返利';
    public function __construct(){

        $this->appId = config('alipay.app_id');//TODO 支付寶應用id
        $this->rsaPrivateKey =  file_get_contents(storage_path('cert'.DIRECTORY_SEPARATOR.'rsaPrivateKey.txt')); //TODO 使用支付寶開放平臺開發助手生成的應用私鑰
        $this->appCertPath =  storage_path('cert'.DIRECTORY_SEPARATOR.'appCertPublicKey.crt');//TODO 上傳證照後生成的crt檔案 證照
        $this->rootCertPath = storage_path('cert'.DIRECTORY_SEPARATOR.'alipayRootCert.crt');//TODO 上傳證照後生成的crt檔案 支付寶根證照
        $this->alipayrsaPublicKeyPath = storage_path('cert'.DIRECTORY_SEPARATOR.'alipayCertPublicKey_RSA2.crt');//TODO 上傳證照後生成的txt檔案 支付寶公鑰證照(不能使用支付寶開放平臺開發助手生成的應用公鑰,這樣子會導致支付回撥驗籤失敗)


        require_once(__DIR__.'/../../Libs/Alipay/aop/AopCertClient.php');//SDK的路徑

        //1、execute 使用
        $aop = new \AopCertClient ();
        $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
        $aop->apiVersion = '1.0';
        $aop->signType = 'RSA2';
        $aop->postCharset = 'utf-8';
        $aop->format = 'json';
        $aop->isCheckAlipayPublicCert = true;//是否校驗自動下載的支付寶公鑰證照,如果開啟校驗要保證支付寶根證照在有效期內

        $aop->appId = $this->appId;


        $aop->rsaPrivateKey = $this->rsaPrivateKey;
        $aop->alipayrsaPublicKey = $aop->getPublicKey($this->alipayrsaPublicKeyPath);//呼叫getPublicKey從支付寶公鑰證照中提取公鑰
        $aop->appCertSN = $aop->getCertSN($this->appCertPath);//呼叫getCertSN獲取證照序列號
        $aop->alipayRootCertSN = $aop->getRootCertSN($this->rootCertPath);//呼叫getRootCertSN獲取支付寶根證照序列號

        $this->aop = $aop;
    }

    /**
     * 獲取錯誤資訊
     * @return mixed
     */
    public function getError()
    {
        return $this->error;
    }

    /**
     * 設定錯誤資訊
     * @param mixed $error
     */
    public function setError($error = '支付寶服務異常,請重試')
    {
        $this->error = $error;
        return false;
    }
}

//轉賬類


<?php
namespace App\Services\Alipay;
use App\Models\Basic\AlipayRecord;

/**
 * 轉賬類
 * Class Trans
 * @package app\models\alipay
 */
class Trans extends AlipayBase {
    /**
     * 轉賬給支付寶
     * @param $data
     * @param string $order_title
     * @return bool|\SimpleXMLElement
     */
    public function transfer($uid,$order_number,$pay_no,$pay_name,$amount,$inviteFlowPayIds,$memo=''){
        //判斷是否是本地壞境
        $isLocal = app()->environment('local');
        if($isLocal){
            return ['msg' => '本地壞境不轉賬'];
        }
        try {
            require_once(__DIR__.'/../../Libs/Alipay/aop/request/AlipayFundTransUniTransferRequest.php');
            $request = new \AlipayFundTransUniTransferRequest ();

            $param = [
                'biz_scene' => 'DIRECT_TRANSFER',//業務場景,單筆無密轉賬固定為DIRECT_TRANSFER
                'product_code' => 'TRANS_ACCOUNT_NO_PWD',//銷售產品碼,單筆無密轉賬固定為TRANS_ACCOUNT_NO_PWD
                'order_title' => $memo,//TODO 轉賬備註
                'out_biz_no' => $order_number,//TODO 訂單號
                'trans_amount' => $amount,//TODO 金額
                'payee_info' => [
                    'identity_type' => 'ALIPAY_LOGON_ID',
                    'identity' => $pay_no,//TODO 收款方賬號
                    'name' => $pay_name,//TODO 姓名
                ],
                'business_params'=>[
                    'payer_show_name'=>$this->payer_name //付款方賬戶
                ]
            ];
            $bizcontent = json_encode($param);

            $request->setBizContent($bizcontent);
            $result = $this->aop->execute($request);

            $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
            $resultCode = $result->$responseNode->code;

            //驗證sign
            $params=[];
            foreach ($result->$responseNode as $key=>$value){
                $params[$key]= $value;
            }


            if(!empty($resultCode)&&$resultCode == 10000){
                return true;
            } else {
                if (isset($params['sub_msg'])) {
                    $msg = $params['sub_msg'];
                } else {
                    $msg = '未知錯誤,請檢視日誌';
                }
                return ['msg' => $msg];
            }
        } catch (\Exception $e) {
            return $this->setError($e->getMessage());
        }
    }

    /**
     * 查詢轉賬訂單
     * @param $out_biz_no
     * @return bool|\SimpleXMLElement
     */
    public function query($out_biz_no,$order_id=''){
        try {
            require_once(__DIR__.'/../../Libs/Alipay/aop/request/AlipayFundTransOrderQueryRequest.php');

            $request = new \AlipayFundTransOrderQueryRequest ();

            $param = [
                'out_biz_no' => $out_biz_no,//TODO 訂單號
                'order_id'=>$order_id,
                'biz_scene' => 'DIRECT_TRANSFER',//業務場景,單筆無密轉賬固定為DIRECT_TRANSFER
                'product_code' => 'TRANS_ACCOUNT_NO_PWD',//銷售產品碼,單筆無密轉賬固定為TRANS_ACCOUNT_NO_PWD
            ];
            $bizcontent = json_encode($param);

            $request->setBizContent($bizcontent);

            $result = $this->aop->execute ( $request);
            $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
            $resultCode = $result->$responseNode->code;
            $res_arr = [];
            if(!empty($resultCode)&&$resultCode == 10000){
                $res_arr['code'] = '1';
                $res_arr['data'] = $result->$responseNode;
            } else {
                $res_arr['code'] = '-1';
                $res_arr['data'] = $result->$responseNode;
            }
            return $res_arr;
        } catch (\Exception $e) {
            return $this->setError($e->getMessage());
        }
    }
}

SDK裡面已經寫了一個例子,這裡貼出來

php 支付寶 SDK 單筆轉賬
這裡需要注意兩個地方
使用證照模式本地必須開始SSL
如果出現SSL certificate: unable to get local issuer certificate錯誤資訊
解決辦法:到 curl.haxx.se/ca/cacert.pem 下載pem檔案,並將檔案拷貝到如下路徑(根據自己的實際情況),並在php.ini 增加
curl.cainfo =”E:/wamp64/bin/php/php7.0.10/extras/ssl/cacert.pem”

php 支付寶 SDK 單筆轉賬
另外一個就是這個SDK 的alipaySDK中定義的Encrypt()/Decrypt()函式與Laravel中定義的Encrypt()/Decrypt()函式重名了。這裡在檔案中查詢encrypt/decrypt替換為alipayEncrypt/alipayDecrypt即可。

php 支付寶 SDK 單筆轉賬

php 支付寶 SDK 單筆轉賬

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章