微信商家转账零钱

<?php

declare(strict_types=1);
namespace App\Logic\Point;

use App\Helper\GuzzleHttp;
use App\Kernel\Mongodb\PointMongodb;
use App\Kernel\Redis\PointPaymentRedis;
use App\Model\Point\Exchange\Order\OrderStatus;
use App\Model\Qa\PointReviewProcess;
use App\Repository\Point\PointExchangeOrderRepository;
use GuzzleHttp\HandlerStack;
use Hyperf\DbConnection\Db;
use Hyperf\Guzzle\CoroutineHandler;
use Hyperf\Redis\RedisFactory;
use Hyperf\Utils\ApplicationContext;
use Hyperf\Di\Annotation\Inject;
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;

class WeChatPay
{
    /**
     * @Inject
     * @var PointExchangeOrderRepository
     */
    private $order;

    /**
     * @Inject()
     * @var GuzzleHttp
     */
    protected $guzzle;

    /**
     * 支付转账
     * @param $batch_num
     * @return string[]
     * @throws \Hyperf\Mongodb\Exception\MongoDBException
     */
    public function Payment($batch_num)
    {

        $Data = $this->order->DownLoadFinances($batch_num);

        $Collection = 'point-log' . date('Y-m-d');

        $container = ApplicationContext::getContainer();

        $this->mongoDb = $container->get(PointMongodb::class);

        if($Data) {

            $Builder = $this->GetInstance($Data[0]['user_type']);

            $instance = Builder::factory($Builder['factory']);

            $platformPublicKeyInstance = $Builder['platformPublicKeyInstance'];

            # 获取总额度
            $columns = array_column($Data,'grant_money');

            $TotalMoney = array_sum($columns);

            $TotalCount = count($columns);

            $APPID = env('POINT_YXX_APP_ID');

            if($Data[0]['user_type'] == 2)   $APPID = env('POINT_YYY_APP_ID');

            $PayData ['json']['appid'] =  $APPID;

            $PayData ['json']['out_batch_no'] =  $Data[0]['batch_num'];

            $PayData ['json']['batch_name'] =  '批次名称';

            $PayData ['json']['batch_remark'] =  '备注';

            $PayData ['json']['total_amount'] = (int) bcmul((string) $TotalMoney , '100',2) ;

            $PayData ['json']['total_num'] =  (int) $TotalCount;

            # 实名制
            $PayData['headers'] = [
                'Wechatpay-Serial' => $Builder['merchantCertificateSerial'],
            ];

            $encryptor = static function(string $msg) use ($platformPublicKeyInstance): string {
                return Rsa::encrypt($msg, $platformPublicKeyInstance);
            };

            $PointReviewProcess = [];

            foreach ($Data as $k=>$datum) {

                # 验证转账金额 次数
                $resRedisMoney = $this->CheckUserTYperPaymentInfoRedis($datum['uid'], $datum['user_type'], $datum['grant_money']);

                if($resRedisMoney['status'] == 'warning')  return $resRedisMoney;

                # 构建发送数据
                $PayData['json']['transfer_detail_list'][$k] = [

                        'out_detail_no'     => $datum['order_num'],

                        'transfer_amount'   => (int) bcmul((string) $datum['grant_money'] , '100',2),   # 转账金额

                        'transfer_remark'   => '备注',

                        'openid'            => $datum['user_openid'],

                        'user_name'         => $encryptor($datum['user_name']),    # 收款人姓名    大于2000必须实名制   

                ];

                # 记录流程
                $PointReviewProcess[] = [
                    'order_num'      => $datum['order_num'],
                    'status'         => OrderStatus::POINTPAYING,
                    'operator_id'    => Db::raw("null"),
                    'operation_desc' => '转账成功',
                    'created_at'     => date('Y-m-d H:i:s'),
                ];
            }


            try {

                $resp = $instance->chain('v3/transfer/batches')->post($PayData);

                $body = (string) $resp->getBody();

                $this->order->updateBatch($batch_num,json_decode($body,true));

                PointReviewProcess::insert($PointReviewProcess);

                $this->mongoDb->insert( $Collection, [
                    'cr_dt' =>  date('Y-m-d H:i:s'),
                    'batch_num' =>  $Data[0]['batch_num'],
                    'status' =>  'normal',
                    'response_code' =>  $resp->getStatusCode(),
                    'response_body' =>  $body,
                    'send_data'     =>  json_encode($PayData,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES)]);

                return ['status'=>'success','msg'=>''];

            } catch (\Exception $e) {

$Message = $e->getMessage(); $code = $Phrase = $Body = $TraceAsString = ''; if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { $r = $e->getResponse(); $code = $r->getStatusCode(); $Phrase = $r->getReasonPhrase(); $Body = $r->getBody(); } $this->mongoDb->insert( $Collection, [ 'cr_dt' => date('Y-m-d H:i:s'), 'batch_num' => $Data[0]['batch_num'], 'status' => 'error', 'response_code' => $code.' -- '.$Phrase, 'response_body' => $Body.' MSG:'.$Message, 'send_data' => json_encode($PayData,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES), ]); } $msg = json_decode((string)$Body , true); return ['status'=>'warning','msg'=>$msg['message']]; }else { return ['status'=>'warning','msg'=>'数据不存在']; } } /** * @param $uid * @param $user_type * @param $money * @return string[] */ private function CheckUserTYperPaymentInfoRedis($uid, $user_type, $money) { $containerRedis = ApplicationContext::getContainer(); $redis = $containerRedis->get(PointPaymentRedis::class); $nextDay = strtotime('+1 day', strtotime(date('Y-m-d'))); $ttl = $nextDay - time(); $TotalMoneyTypeKey = 'point_payment:total_money:t_:'.$user_type.':'.date("Ymd"); # 账户总额度 $TotalNumTypeKey = 'point_payment:total_num:t_'.$user_type.':'.date("Ymd"); # 账户总次数 $UserMoneyTypeKey = 'point_payment:total_money:user'.$uid.$user_type.':'.date("Ymd"); # 用户总额度 $UserNumTypeKey = 'point_payment:total_num:user:'.$uid.$user_type.':'.date("Ymd"); # 用户总次数 $TotalNumTypeExc = $redis->get($TotalNumTypeKey); $UserNumTypeKeyExc = $redis->get($UserNumTypeKey); $TotalMoneyTypeExc = $redis->get($TotalMoneyTypeKey); $UserMoneyTypeExc = $redis->get($UserMoneyTypeKey); $TotalNumTypeRds = $TotalNumTypeExc ? $TotalNumTypeExc : 0; $UserNumTypeKeyRds = $UserNumTypeKeyExc ? $UserNumTypeKeyExc : 0; $TotalMoneyTypeRds = $TotalMoneyTypeExc ? $TotalMoneyTypeExc : 0; $UserMoneyTypeRds = $UserMoneyTypeExc ? $UserMoneyTypeExc : 0; # 账户当日转账总次数 if(++$TotalNumTypeRds > env('GRANT_TOTALNUM_USER_TYPE')){ return ['status'=>'warning','msg'=>'该账户已到达当日转账次数上限']; }else{ # 当前用户转账总次数 if(++$UserNumTypeKeyRds <= env('GRANT_NUM_USER')){ # 检查当日账户转账总金额 if(($TotalMoneyTypeRds + $money) <= env('GRANT_TOTALMONEY_USER_TYPE')){ # 检查当日用户转账总金额 if(($UserMoneyTypeRds +$money) <= env('GRANT_TOTALMONEY_USER')){ # 设置缓存 # 账户总次数 $redis->set($TotalNumTypeKey, $TotalNumTypeRds); $redis->expire($TotalNumTypeKey, $ttl); # 用户总次数 // $UserNumTypeKeyRds ++; $redis->set($UserNumTypeKey, $UserNumTypeKeyRds); $redis->expire($UserNumTypeKey, $ttl); # 账户总额度 $TotalMoneyTypeRds += $money; $redis->set($TotalMoneyTypeKey, $TotalMoneyTypeRds); $redis->expire($TotalMoneyTypeKey, $ttl); # 用户总金额 $UserMoneyTypeRds += $money; $redis->set($UserMoneyTypeKey, $UserMoneyTypeRds); $redis->expire($UserMoneyTypeKey, $ttl); return ['status'=>'success','msg'=>'']; }else{ return ['status'=>'warning','msg'=>'该用户已到达当日转账总额度上限']; } }else{ return ['status'=>'warning','msg'=>'该账户已到达当日转账总额度上限']; } }else{ return ['status'=>'warning','msg'=>'该批次中用户超出当日转账次数,请择日重新提交转账申请!']; } } } /** * 查并确认订单 * @throws \Hyperf\Mongodb\Exception\MongoDBException */ public function PointCheckWXPayDetail() { $Data = $this->order->GetPointReviewBatch(); try { if($Data){ foreach ($Data as $datum) { $batch_num = $datum['batch_num']; $order_num = $datum['order_num']; $batch_id = $datum['batch_id']; $Collection = 'point-check-log' . date('Y-m-d'); $container = ApplicationContext::getContainer(); $this->mongoDb = $container->get(PointMongodb::class); $Builder = $this->GetInstance($datum['user_type']); $instance = Builder::factory($Builder['factory']); $url = 'v3/transfer/batches/out-batch-no/'.$batch_num; $urlArr = [ 'need_query_detail'=>true, 'limit'=>100, 'detail_status'=>'ALL', ];
            # ****** 此处巨坑 ****** #
            # 前因后果是这样的,下单都正常了,当来到查询转账是否成功时 发现一直报错,说我参数错误,纳闷了很久 比对了很久 没错啊!
            # 最后打印出请求的url才发现 NND 他这个官方SDK把我订单号自动转成了小写“我生成的订单号前四个字母是大写的”,结果转成“-X-X-X-X”这种形式。
            # 如 “PYHM123456789” 会转为 “-P-Y-H-M123456789”

            # 问了几个微信官方的技术支持 也愣是说不出个所以然 还要我提供给他们各种数据,搞死了那一天,最终都没告诉我为什么
            # 无奈的我最终选择了把订单号的前几个字母换成小写就成功了。 但这只是个开始,奇葩的在下面这个
            # 先了解一下微信官方文档 detail_status 要求必须是大写,但是这个接口会默认把所有大写转换成小写 和上面一样转换了,
            # 这样接口就会不停报错,这可怎么办啊,这我没招了啊,无奈只能再次去问了几个微信官方技术 终于有一个给出了答案,但是我没有采用。
            # 而是采纳公司大佬说的 把所有参数处理成一个数组
 
$resp = $instance->chain($url)->get(['query'=>$urlArr]);
            # ****** 此处巨坑 ****** #
$body = (string) $resp->getBody(); $bodyArr = json_decode($body,true); if($bodyArr && $bodyArr['transfer_detail_list']){ $PointReviewProcess = []; foreach ($bodyArr['transfer_detail_list'] as $everyDetail) { if($everyDetail['detail_status'] === 'FAIL'){ Db::connection('qa')->beginTransaction(); # 查询 详细中每一份订单 $this->GetBatchDetailEveryLast($instance, $everyDetail['out_detail_no'], $bodyArr['transfer_batch']['out_batch_no']); }elseif ($everyDetail['detail_status'] === 'SUCCESS'){ # 更新转账成功 根据得到的信息 处理订单 $this->order->UpdatePointReviewBatchByOrderNumSuccess($bodyArr['transfer_batch']['out_batch_no'], $everyDetail['out_detail_no']); # 记录流程 $PointReviewProcess[] = [ 'order_num' => $everyDetail['out_detail_no'], 'status' => OrderStatus::POINTPAYPASS, 'operator_id' => Db::raw("null"), 'operation_desc' => '转账成功', 'created_at' => date('Y-m-d H:i:s'), ];                   
                   # 记录 mongodb 
$this->mongoDb->insert( $Collection, [ 'batch_num' => $bodyArr['transfer_batch']['out_batch_no'], 'cr_dt' => date('Y-m-d H:i:s'), 'status' => 'normal', 'response_code' => $resp->getStatusCode(), 'response_body' => $body, 'send_data' => 'batch_num:'.$batch_num.' order_num'. $order_num.' batch_id'. $batch_id]); } } if(count($PointReviewProcess) > 0) PointReviewProcess::insert($PointReviewProcess); } } } Db::connection('qa')->commit(); } catch (\Exception $e) { // 进行错误处理 echo $e->getMessage(), PHP_EOL; $Message = $e->getMessage(); $code = $Phrase = $Body = $TraceAsString = ''; if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { $r = $e->getResponse(); $code = $r->getStatusCode(); $Phrase = $r->getReasonPhrase(); $Body = $r->getBody(); } $this->mongoDb->insert( $Collection, [ 'cr_dt' => date('Y-m-d H:i:s'), 'status' => 'error', 'response_code' => $code.' -- '.$Phrase, 'response_body' => $Body.' MSG:'.$Message, 'send_data' => '', ]); Db::connection('qa')->rollBack(); } } /** * 查询批次内每一份订单失败原因 * @param array $transfer_detail_list * @throws \Hyperf\Mongodb\Exception\MongoDBException */ private function GetBatchDetailEveryLast($instance, $out_detail_no, $out_batch_no) { $Collection = 'point-every-last-log' . date('Y-m-d'); $container = ApplicationContext::getContainer(); $this->mongoDb = $container->get(PointMongodb::class); $send_data = 'batch_num:'.$out_batch_no.' order_num'. $out_detail_no; try { $url = 'v3/transfer/batches/out-batch-no/'.$out_batch_no.'/details/out-detail-no/'.$out_detail_no; $resp = $instance->chain($url)->get(); $body = (string) $resp->getBody(); $bodyArr = json_decode($body,true); $this->order->UpdatePointReviewBatchByOrderNumFail($out_batch_no, $out_detail_no, $bodyArr['fail_reason']); # 记录流程 $PointReviewProcess = [ 'order_num' => $out_detail_no, 'status' => OrderStatus::POINTPAYFAIL, 'operator_id' => Db::raw("null"), 'operation_desc' => '转账失败:'. $bodyArr['fail_reason'], 'created_at' => date('Y-m-d H:i:s'), ]; PointReviewProcess::insert($PointReviewProcess); $this->mongoDb->insert( $Collection, [ 'batch_num' => $out_batch_no, 'cr_dt' => date('Y-m-d H:i:s'), 'status' => 'normal', 'response_code' => $resp->getStatusCode(), 'response_body' => $body, 'send_data' => $send_data]); } catch (\Exception $e) { echo $e->getMessage(), PHP_EOL; $Message = $e->getMessage(); $code = $Phrase = $Body = $TraceAsString = ''; if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) { $r = $e->getResponse(); $code = $r->getStatusCode(); $Phrase = $r->getReasonPhrase(); $Body = $r->getBody(); } $this->mongoDb->insert( $Collection, [ 'batch_num' => $out_batch_no, 'cr_dt' => date('Y-m-d H:i:s'), 'status' => 'error', 'response_code' => $code.' -- '.$Phrase.' -- '.$TraceAsString, 'response_body' => $Body.' MSG:'.$Message, 'send_data' => $send_data, ]); } } /** * 配置 构造一个 APIv3 客户端实例 * @return \WeChatPay\BuilderChainable */ private function GetInstance($user_type) {# 商户号 $merchantId = env('POINT_YYY_MERCHANTID'); # 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名 $merchantPrivateKeyFilePath = 'file:///var/www/storage/keys/apiclient_YYY_key.pem'; $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE); # 「商户API证书」的「证书序列号」 $merchantCertificateSerial = env('POINT_YYY_SERIAL'); # 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名 $platformCertificateFilePath = 'file:///var/www/storage/keys/platform_YYY_cert.pem'; $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC); # 从「微信支付平台证书」中获取「证书序列号」 $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath); $handler = new CoroutineHandler(); // 设置 HttpClient,部分接口直接使用了 http_client。 $stack = HandlerStack::create($handler); $Builder['platformPublicKeyInstance'] = $platformPublicKeyInstance; $Builder['merchantCertificateSerial'] = $merchantCertificateSerial; $Builder['factory'] = [ 'handler' => $stack, 'mchid' => $merchantId, 'serial' => $merchantCertificateSerial, 'privateKey' => $merchantPrivateKeyInstance, 'certs' => [ $platformCertificateSerial => $platformPublicKeyInstance, ], ];
     return $Builder;
}
################################################## 手动下载平台证书 这个是借鉴网友的,可以直接用最下方微信提供的SDK更简便 ##################################################

    /**
     * 获取证书
     * @return mixed
     */
    public static function certificates(){

        //请求参数(报文主体)
        $headers = self::sign('GET','https://api.mch.weixin.qq.com/v3/certificates','',2);

        $result = self::curl_get('https://api.mch.weixin.qq.com/v3/certificates',$headers);
        var_dump($result);
        var_dump('----- ');

        $result = json_decode($result,true);

        var_dump($result);
        $aa = self::decryptToString(2,$result['data'][0]['encrypt_certificate']['associated_data'],$result['data'][0]['encrypt_certificate']['nonce'],$result['data'][0]['encrypt_certificate']['ciphertext']);
        var_dump('  ==============  ');
        var_dump($aa);
    }
/** * 签名 * @param string $http_method 请求方式GET|POST * @param string $url url * @param string $body 报文主体 * @return array */ public static function sign($http_method = 'POST',$url = '',$body = '', $utype) { $apiclient_key = 'apiclient_key.pem'; $name = 'YXX'; if($utype == 2) { $name = 'YYY'; $apiclient_key = 'apiclient_YYY_key.pem'; } $mch_private_key = openssl_get_privatekey(file_get_contents('file:///usr/local/project/admin-api/'.$apiclient_key)); # 私钥文件存放路径 【 根据实际存放地址 】 $timestamp = time(); # 时间戳 $nonce = self::getRandomStr(32); # 随机串 $url_parts = parse_url($url); $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : "")); # 构造签名串 $message = $http_method."\n". $canonical_url."\n". $timestamp."\n". $nonce."\n". $body."\n"; # 报文主体 # 计算签名值 openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption'); $sign = base64_encode($raw_sign); # 设置HTTP头 $config = [ 'appid' => env('POINT_'.$name.'_APP_ID'), 'mchid' => env('POINT_'.$name.'_MERCHANTID'), # 商户号 'serial_no' => env('POINT_'.$name.'_SERIAL'), # 证书序列号 ]; $token = sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $config['mchid'], $nonce, $timestamp, $config['serial_no'], $sign); $headers = [ 'Accept: application/json', 'User-Agent: */*', 'Content-Type: application/json; charset=utf-8', 'Authorization: '.$token, ]; return $headers; } /** * 获得随机字符串 * @param $len integer 需要的长度 * @param $special bool 是否需要特殊符号 * @return string 返回随机字符串 */ public static function getRandomStr($len, $special=false) { $chars = array( "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ); if($special){ $chars = array_merge($chars, array( "!", "@", "#", "$", "?", "|", "{", "/", ":", ";", "%", "^", "&", "*", "(", ")", "-", "_", "[", "]", "}", "<", ">", "~", "+", "=", ",", "." )); } $charsLen = count($chars) - 1; shuffle($chars); # 打乱数组顺序 $str = ''; for($i=0; $i<$len; $i++){ $str .= $chars[mt_rand(0, $charsLen)]; # 随机取出一位 } return $str; } /** * 请求 * @param $url * @param array $headers * @return bool|string */ public static function curl_get($url,$headers=array()) { $info = curl_init(); curl_setopt($info,CURLOPT_RETURNTRANSFER,true); curl_setopt($info,CURLOPT_HEADER,0); curl_setopt($info,CURLOPT_NOBODY,0); curl_setopt($info,CURLOPT_SSL_VERIFYPEER,false); curl_setopt($info,CURLOPT_SSL_VERIFYPEER,false); curl_setopt($info,CURLOPT_SSL_VERIFYHOST,false); # 设置header头 curl_setopt($info, CURLOPT_HTTPHEADER,$headers); curl_setopt($info,CURLOPT_URL,$url); $output = curl_exec($info); curl_close($info); return $output; } const KEY_LENGTH_BYTE = 32; const AUTH_TAG_LENGTH_BYTE = 16; /** * Decrypt AEAD_AES_256_GCM ciphertext * @param string $associatedData AES GCM additional authentication data * @param string $nonceStr AES GCM nonce * @param string $ciphertext AES GCM cipher text * @return string|bool Decrypted string on success or FALSE on failure */ public static function decryptToString($utype, $associatedData, $nonceStr, $ciphertext) { $aesKey = env('POINT_YXX_AESKEY'); if($utype == 2) $aesKey = env('POINT_YYY_AESKEY'); $ciphertext = \base64_decode($ciphertext); if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) { return false; } // ext-sodium (default installed on >= PHP 7.2) if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) { return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey); } // ext-libsodium (need install libsodium-php 1.x via pecl) if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) { return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $aesKey); } // openssl (PHP >= 7.1 support AEAD) if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) { $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE); $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE); return \openssl_decrypt($ctext, 'aes-256-gcm', $aesKey, \OPENSSL_RAW_DATA, $nonceStr, $authTag, $associatedData); } throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php'); } }

 

# 下载平台证书SDK
<?php

require_once('vendor/autoload.php');

use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;

// 设置参数

// 商户号
$merchantId = '190000****';

// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

// 「商户API证书」的「证书序列号」
$merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********';

// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
$platformCertificateFilePath = 'file:///path/to/wechatpay/cert.pem';
$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

// 从「微信支付平台证书」中获取「证书序列号」
$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

// 构造一个 APIv3 客户端实例
$instance = Builder::factory([
    'mchid'      => $merchantId,
    'serial'     => $merchantCertificateSerial,
    'privateKey' => $merchantPrivateKeyInstance,
    'certs'      => [
        $platformCertificateSerial => $platformPublicKeyInstance,
    ],
]);

// 发送请求
$resp = $instance->chain('v3/certificates')->get(
    ['debug' => true] // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
);
echo $resp->getBody(), PHP_EOL;

 

  

posted @ 2022-10-27 14:54  现世中的素人  阅读(254)  评论(0编辑  收藏  举报