tp5 composer wechatpay微信支付v3

发布时间:2023-10-19作者:冰貂主人点击:165

1,安装wechatpay

正常使用中的tp5程序需要安装微信支付v3。
必须使用composer安装,否则报错:
致命错误: Class 'WechatPay\GuzzleMiddleware\Util\PemUtil' not found
tp5使用cmd面板composer require wechatpay/wechatpay会报错。
比如下面
1,The "topthink/think-installer" plugin was skipped because it requires a Plugin API version ("^1.0") that does not match your Composer installation ("2.3.0"). You may need to run composer update with the "--no-plugins" option.
2,Could not find package wechatpay/wechatpay. It was however found via repository search, which indicates a consisten   cy issue with the repository.
3,topthink/think-installer contains a Composer plugin which is currently not in your allow-plugins config. See https://getcomposer.org/allow-plugins

在composer.json文件中添加这两行
 "require": {
        "php": ">=5.4.0",
        "topthink/framework": "5.0.*",
        "wechatpay/wechatpay": "^1.4",
        "topthink/think-captcha": "^1.0",
        "wechatpay/wechatpay-guzzle-middleware": "^0.2.0"
    },
然后执行 composer update。但是安装完成后vendor/topthink/里面的think-captcha、think-helper等文件都被删除了。那么就使用cmd面板 composer require topthink/think-captha 逐个安装即可。
当打开网站时报错模块不存在:__module__。。即便修改了think/library/think/view.php文件。还是报错怎么办?
那就打开__MODULE__所在的html文件。把__MODULE__修改成__URL__。。修改一处即可,保存后,刷新页面就正常了。

2,部署代码

<?php

  namespace app\common\controller;

  use think\Controller;
  use think\Db;
  use think\Request;
  use think\Loader;
  use think\Log;
 use WeChatPay\Builder;
 use WeChatPay\Crypto\Rsa;
 use WeChatPay\Util\PemUtil;
 use WeChatPay\Formatter;
 use WeChatPay\Crypto\AesGcm;
    /**
    * 微信支付类包含二维码链接、查询、退款
    */

  class Wechatpay extends Controller{
   
    protected $instance = null;//支付对象
    protected $appid='ww6220c7';//公众号的APPID
    protected $merchantId='84540';//直连商户号
    protected $serial='407D62E09FDCA6557E9A3';//证书序列号
    protected $apiv3key='NXq7SYRNPlO1uP';// 在商户平台上设置的APIv3密钥
    protected $notifyUrl;//支付回调链接
    protected $refundNotify;//退款回调链接
     public function _initialize(){
          $this->notifyUrl=request()->domain()."/common/wechatpay/notify";             
        $this->refundNotify=request()->domain()."/common/wechatpay/refundNotify";
      
        // 商户号
        $merchantId = $this->merchantId;

        // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
        $filekey='file://'.EXTEND_PATH.'php_sdk_v3/cert/apiclient_key.pem';
        $merchantPrivateKeyFilePath = str_replace('\\','/',$filekey);

        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

        // 「商户API证书」的「证书序列号」
        $merchantCertificateSerial = $this->serial ;

        // 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
        $filecert='file://'.ROOT_PATH.'wechatpay_7ECC4533801129683DE4FD04.pem';
        $platformCertificateFilePath = str_replace('\\','/',$filecert);
        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
        // The `certs(49FDCA6557E9A3)` contains the merchant's certificate serial number(EB3862E09FDCA6557E9A3) which is not allowed here.微信支付平台证书需要下载
        // 从「微信支付平台证书」中获取「证书序列号」
        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

        // 构造一个 APIv3 客户端实例
        $instance = Builder::factory([
            'mchid'      => $merchantId,
            'serial'     => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs'      => [
                $platformCertificateSerial => $platformPublicKeyInstance,
            ],
        ]);
        return $this->instance=$instance;
    }
    
    //调用微信支付付款二维码链接
    public function showQrcode($request){
        $code=1000;
        $msg="下单成功";
        $codeurl="";
        try {
            $resp = $this->instance
            ->chain('v3/pay/transactions/native')
            ->post([
                'json' => [
                    'mchid'        => $this->merchantId,
                    'out_trade_no' => $request['out_trade_no'],
                    'appid'        => $this->appid,
                    'description'  => $request['trade_name'],
                    'notify_url'   => $this->notifyUrl,
                    'amount'       => [
                        'total'    => $request['total_amount'],
                        'currency' => 'CNY'
                    ],
                ]
            ]);        
           
            if($resp->getStatusCode()==200 && $resp->getReasonPhrase()=='OK'){               
           
                $codeurl=json_decode($resp->getBody(),true)['code_url'];
            }

        } catch (\Exception $e) {         
            // 进行错误处理
            $code=1001;
            $msg=$e->getMessage();
            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                $r = $e->getResponse();
            
            }      
        }
        return ['code'=>$code,'msg'=>$msg,'data'=>$codeurl];

    }
    
    //将用户支付成功消息通知给商户,同样的通知可能会多次发送给商户系统。
    public function notify(Request $request){   
        // 同样的通知可能会多次发送给商户系统
   
        //1,从请求头部Headers,拿到Wechatpay-Signature、Wechatpay-Nonce、Wechatpay-Timestamp、Wechatpay-Serial及Request-ID,       
        $inWechatpaySignature =  $request->header('wechatpay-signature');// 签名值
        $inWechatpayTimestamp =  $request->header('wechatpay-timestamp');// 时间戳
        $inWechatpaySerial =$request->header('wechatpay-serial');// 商户API证书序列号
        $inWechatpayNonce = $request->header('wechatpay-nonce');// 请求随机串
        //2,获取请求body体的JSON纯文本;
        $inBody = file_get_contents('php://input');// 请根据实际情况获取,例如: file_get_contents('php://input');
     
        $apiv3Key = $this->apiv3key;// 在商户平台上设置的APIv3密钥

        // 根据通知的平台证书序列号,查询本地平台证书文件,       
        $platformPublicKeyInstance = Rsa::from('file://'.ROOT_PATH.'wechatpay_4533801129683DE4FD04.pem', Rsa::KEY_TYPE_PUBLIC);

        // 3,检查通知消息头标记的Wechatpay-Timestamp偏移量是否在5分钟之内;
        $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
        //4,调用SDK内置方法,构造验签名串然后经Rsa::verfify 验签;
        $verifiedStatus = Rsa::verify(
            // 构造验签名串
            Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
            $inWechatpaySignature,
            $platformPublicKeyInstance
        );
 
        if ($timeOffsetStatus && $verifiedStatus) {
            // 转换通知的JSON文本消息为PHP Array数组
            $inBodyArray = (array)json_decode($inBody, true);
        
            $ciphertext=$inBodyArray['resource']['ciphertext'];
            $nonce=$inBodyArray['resource']['nonce'];
            $aad=$inBodyArray['resource']['associated_data'];
        
            // 使用PHP7的数据解构语法,从Array中解构并赋值变量
            ['resource' => [
                'ciphertext'      => $ciphertext,
                'nonce'           => $nonce,
                'associated_data' => $aad
            ]] = $inBodyArray;
            // 加密文本消息解密
            $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
      
            // 把解密后的文本转换为PHP Array数组
            $inBodyResourceArray = (array)json_decode($inBodyResource, true);           
           
            header('status:200');
            $code='SUCCESS';
            $message='';
        }else{
            header('status:400');
            $code='FAIL';
            $message='失败';
        }       
        return json_encode(['code'=>$code,'message'=>$message]);
       
    }
    
    //调用微信支付接口订单查询,返回订单详情数组
    public function orderquery($inBodyResourceArray){
       
        try {
            $resp = $this->instance->chain('v3/pay/transactions/out-trade-no/'.$inBodyResourceArray['out_trade_no'])->get([
                // Query 参数
                'query' => ['mchid' => $this->merchantId],               
            ]);          
           
            if($resp->getStatusCode()==200 && $resp->getReasonPhrase()=='OK'){              

                $inBodyArray=json_decode($resp->getBody(),true);               
                return $inBodyArray;
            }           

        } catch (\Exception $e) {
            Log::error('下单返回错误值:');          
            // 进行错误处理          
            Log::error($e->getMessage());         
        }       
    }
    
    /*微信退款
    *接收参数:商户订单号、总金额、退款金额、退款原因
    *返回值:
    */
    public function refund($out_refund_no,$out_trade_no,$total_fee,$refund_fee){  
    
        $total_fee=(int)($total_fee*100);
        $refund_fee=(int)($refund_fee*100);       
        $data=array();       
        if(isset($out_trade_no) && $out_trade_no != ""){           
            $promise = $this->instance
            ->chain('v3/refund/domestic/refunds')
            ->postAsync([
                'json' => [
                    'out_trade_no' => $out_trade_no,
                    'out_refund_no'  => $out_refund_no,
                    'notify_url'=> $this->refundNotify,
                    'amount'         => [
                        'refund'   => $refund_fee,
                        'total'    => $total_fee,
                        'currency' => 'CNY',
                    ],
                ],
            ])->then(static function($response) {
                // 正常逻辑回调处理           
               
                return $response;//这里不是该方法的返回值,注意避坑
            })->otherwise(static function($e) {
               if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
                                                         $r = $e->getResponse();
                    
                                                     }   
             
               return $r;
            });
            // 同步等待
            $res =$promise->wait();  
          if($res->getStatusCode()!=200 && $res->getReasonPhrase()!='OK'){
                // 退款异常            
                
                    $data['code']=$res->getStatusCode();
                    $data['msg']='退款失败';
                    return $data;
                
            }else{
                
                    $data['code']=$res->getStatusCode();
                    $data['msg']='退款成功';
                    return $data;
                
            }
           
        }       
    }
    // 退款回调接口
    public function refundNotify(Request $request){
        $inBody = file_get_contents('php://input');  
        header('status:200');
            $code='SUCCESS';
            $message='';
        return json_encode(['code'=>$code,'message'=>$message]);
    }
    // 关闭订单接口
    // 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
    // 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。

    public function closeOrder($out_trade_no){
        try {
            $resp = $this->instance->chain('v3/pay/transactions/out-trade-no/'.$out_trade_no.'/close')
            ->post([
                // Query 参数
                'json' => ['mchid' => $this->merchantId],
               
            ]);
            if($resp->getStatusCode()==204){    
                return ['code'=>1000,'msg'=>'订单已关闭'];
            }           

        } catch (\Exception $e) {
            Log::error('closeOrder关单返回错误值:');          
            // 进行错误处理          
            Log::error($e->getMessage());
            return ['code'=>1001,'msg'=>$e->getMessage()];
        }
    }
  }

?>
html页面使用ajax查询订单,setintevel()

标签: