微信收付通V3支付

<?php

namespace app\common\lib\built;

use app\platform\model\PaymentConfig;

/**
 * 微信支付
 * Class WxPayment
 * @package app\common\lib\built
 */
class WxPayment
{
    protected $authorization = 'WECHATPAY2-SHA256-RSA2048';  //认证类型
    protected $method = "POST"; //请求方式
    protected $url; //请求链接
    protected $platform_id;   //平台id
    protected $mch_id;        //商户号
    protected $nonce_str;        //随机字符串
    protected $sign;        //签名
    protected $timestamp;   //时间
    protected $serial_no;        //商户Api证书序列号
    protected $apiclient_key;   //私钥地址
    protected $apiclient_cert;   //公钥地址
    protected $secret_key;   //V3密钥
    protected $token;        //Token
    protected $data;         //发送参数
    protected $response;    //返回信息
    protected $image_type;    //图片类型
    protected $boundary;      //边界符
    protected $suffix;      //图片后缀
    protected $openid;      //发放的openid
    protected $file;         //图片信息
    protected $platform_certificate; //平台证书地址
    protected $platform_serial_no; //平台证书序列号


    public function __construct($param)
    {
        if (!empty($param['platform_id'])) {
            $PaymentConfigModel = new PaymentConfig();
            $config = $PaymentConfigModel->getFind([
                'platform_id' => $param['platform_id']
            ]);
            if (empty($config)) {
                echo json_encode(callBack('error', 'param_error', '未配置支付参数'), JSON_UNESCAPED_UNICODE);
                die();
            }
            $this->mch_id = $config['wx_mchid'];
            $this->serial_no = $config['wx_serial_no'];
            $this->apiclient_key = $config['wx_apiclient_key'];
            $this->secret_key = $config['wx_secret_key'];
            $this->platform_certificate = $config['wx_platform_certificate'];
            $this->platform_serial_no = $config['wx_platform_serial_no'];
            $this->apiclient_cert = $config['wx_apiclient_cert'];
        } else {
            $this->mch_id = $param['mch_id'] ?? '';
            $this->serial_no = $param['serial_no'] ?? '';
            $this->apiclient_key = $param['apiclient_key'] ?? '';
            $this->secret_key = $param['secret_key'] ?? '';
            $this->platform_certificate = $param['platform_certificate'] ?? '';
            $this->platform_serial_no = $param['platform_serial_no'] ?? '';
            $this->apiclient_cert = $param['apiclient_cert'] ?? '';
        }
        $this->platform_id = $param['platform_id'] ?? '';
        $this->method = $param['method'] ?? $this->method;
        $this->data = $param['data'] ?? [];
        $this->image_type = $param['image_type'] ?? '';
        $this->boundary = $param['boundary'] ?? '';
        $this->openid = $param['openid'] ?? '';
        $this->file = $param['file'] ?? '';
    }


    //微信小程序发起退款请求
    public function appletLaunchCombineRefund()
    {
        $this->url = 'https://api.mch.weixin.qq.com/v3/ecommerce/refunds/apply';
        $result = $this->requestAction();
        return $result;
    }


    /**
     * 上传图片
     * @return bool|string
     */
    public function upload()
    {
        $this->url = 'https://api.mch.weixin.qq.com/v3/merchant/media/upload';
        $result = $this->uploadRequestAction();
        return $result;
    }


    /**
     * 图片上传请求
     * @return bool|string
     */
    public function uploadRequestAction()
    {
        if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
            throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
        }
        $headerParam = $this->uploadHeaderParam(); //获取头部信息
        $boundarystr = "--{$this->boundary}\r\n";// $out是post的内容
        $str = $boundarystr;
        $str .= 'Content-Disposition: form-data; name="meta"' . "\r\n";
        $str .= 'Content-Type: application/json' . "\r\n";
        $str .= "\r\n";
        $str .= json_encode($this->data['meta']) . "\r\n";
        $str .= $boundarystr;
        $str .= 'Content-Disposition: form-data; name="file"; filename="' . $this->data['meta']['filename'] . '"' . "\r\n";
        $str .= 'Content-Type: ' . $this->image_type . ";\r\n";
        $str .= "\r\n";
        $str .= $this->data['file'] . "\r\n";
        $str .= $boundarystr . "--\r\n";
        $ThirdClass = new ThirdRequest();
        $this->response = $ThirdClass->curlPost($this->url, $str, $headerParam);
        return $this->response;
    }


    /**
     * 图片上传头部参数
     * @return array
     */
    public function uploadHeaderParam()
    {
        $this->getUploadSign();        //生成签名
        $this->getToken();        //生成Token
        $header = [
            "Content-Type: multipart/form-data;name='meta'",
            "Content-Type: application/json",
            "User-Agent:" . $_SERVER['HTTP_USER_AGENT'],
            'Authorization: ' . $this->authorization . ' ' . $this->token,
            "Content-Type: multipart/form-data;boundary=" . $this->boundary
        ];
        return $header;
    }

    /**
     * 图片生成签名
     */
    protected function getUploadSign()
    {
        $url_parts = parse_url($this->url);  //链接
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $this->timestamp = time();
        $this->nonce_str = randomStr(32); //随机字符串
        $message = $this->method . "\n" .
            $canonical_url . "\n" .
            $this->timestamp . "\n" .
            $this->nonce_str . "\n" .
            json_encode($this->data['meta']) . "\n";
        openssl_sign($message, $raw_sign, openssl_get_privatekey(file_get_contents($this->apiclient_key)), 'sha256WithRSAEncryption');
        $this->sign = base64_encode($raw_sign);
    }


    //微信小程序发起合单支付请求
    public function appletLaunchCombinePayment()
    {
        $this->url = 'https://api.mch.weixin.qq.com/v3/combine-transactions/jsapi';
        $result = $this->requestAction();
        return $result;
    }


    /**
     * 进件状态
     * @param $out_request_no 业务申请编号
     * @return bool|string
     */
    public function applymentsStatus($out_request_no)
    {
        $this->url = 'https://api.mch.weixin.qq.com/v3/ecommerce/applyments/out-request-no/' . $out_request_no;
        $this->data = [];
        $this->getSign();        //生成签名
        $this->getToken();        //生成Token
        $headerParam = [
            "Content-type: application/json;charset='utf-8'",
            "Accept:application/json",
            "User-Agent:" . $this->mch_id,
            'Authorization: ' . $this->authorization . ' ' . $this->token,
            'Wechatpay-Serial:' . $this->platform_serial_no
        ];
        $ThirdClass = new ThirdRequest();
        $this->response = $ThirdClass->curlGet($this->url, $headerParam);
        return $this->response;
    }


    //供应商进件
    public function applyments()
    {
        $this->url = 'https://api.mch.weixin.qq.com/v3/ecommerce/applyments/';
        $this->getSign();        //生成签名
        $this->getToken();        //生成Token
        $headerParam = [
            "Content-type: application/json;charset='utf-8'",
            "Accept:application/json",
            "User-Agent:*/*",
            'Authorization: ' . $this->authorization . ' ' . $this->token,
            'Wechatpay-Serial:' . $this->platform_serial_no
        ];
        $ThirdClass = new ThirdRequest();
        $this->response = $ThirdClass->curlPost($this->url, json_encode($this->data), $headerParam);
        return $this->response;
    }


    //对私银行列表
    public function personalBanking()
    {
        $this->url = 'https://api.mch.weixin.qq.com/v3/capital/capitallhh/banks/personal-banking?offset=' . $this->data['offset'] . '&limit=' . $this->data['limit'];
        $this->data = [];
        $headerParam = $this->getHeaderParam(); //获取头部信息
        $ThirdClass = new ThirdRequest();
        $this->response = $ThirdClass->curlGet($this->url, $headerParam);
        return $this->response;
    }


    //对公银行列表
    public function corporateBanking()
    {
        $this->url = 'https://api.mch.weixin.qq.com/v3/capital/capitallhh/banks/corporate-banking?offset=' . $this->data['offset'] . '&limit=' . $this->data['limit'];
        $this->data = [];
        $headerParam = $this->getHeaderParam(); //获取头部信息
        $ThirdClass = new ThirdRequest();
        $this->response = $ThirdClass->curlGet($this->url, $headerParam);
        return $this->response;
    }


    /**
     * 发送post请求
     * @return bool|string
     */
    protected function requestAction()
    {
        $headerParam = $this->getHeaderParam(); //获取头部信息
        $ThirdClass = new ThirdRequest();
        $this->response = $ThirdClass->curlPost($this->url, json_encode($this->data), $headerParam);
        return $this->response;
    }


    /**
     * 获取请求头部参数
     * @return array
     */
    protected function getHeaderParam()
    {
        $this->getSign();        //生成签名
        $this->getToken();        //生成Token
        $header = [
            "Content-type: application/json;charset='utf-8'",
            "Accept:application/json",
            "User-Agent:*/*",
            'Authorization: ' . $this->authorization . ' ' . $this->token,
        ];
        return $header;
    }


    /**
     * 生成签名
     */
    protected function getSign()
    {
        $url_parts = parse_url($this->url);  //链接
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $this->timestamp = time();
        $this->nonce_str = randomStr(32); //随机字符串
        if (!empty($this->data)) {
            $message = $this->method . "\n" .
                $canonical_url . "\n" .
                $this->timestamp . "\n" .
                $this->nonce_str . "\n" .
                json_encode($this->data) . "\n";
        } else {
            $message = $this->method . "\n" .
                $canonical_url . "\n" .
                $this->timestamp . "\n" .
                $this->nonce_str . "\n" .
                '' . "\n";
        }
        openssl_sign($message, $raw_sign, openssl_get_privatekey(file_get_contents($this->apiclient_key)), 'sha256WithRSAEncryption');
        $this->sign = base64_encode($raw_sign);
    }


    /**
     * 生成Token
     * @return string
     */
    protected function getToken()
    {
        $this->token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $this->mch_id, $this->nonce_str, $this->timestamp, $this->serial_no, $this->sign);
    }


    /**
     * 回调验签
     * @param $platform_id 平台id
     * @param $timeStamp 时间戳
     * @param $nonce 随机字符串
     * @param $body 内同
     * @param $wx_sign 签名
     * @param $serial_no 回调返回的平台证书序列号
     * @return bool
     */
    public function checkSign($timeStamp, $nonce, $body, $wx_sign, $serial_no)
    {
        if (!$serial_no) {
            return false;
        }
        //证书序列号不一致获取新的证书
        if ($serial_no != $this->platform_serial_no) {
            $this->getCertificates();
        }
        $message = $timeStamp . "\n" .
            $nonce . "\n" .
            $body . "\n";
        $wx_sign = base64_decode($wx_sign);
        $platform_certificate = file_get_contents($this->platform_certificate);//平台证书
        $res = openssl_verify($message, $wx_sign, $platform_certificate, OPENSSL_ALGO_SHA256);
        if ($res == 1) {
            return true;
        }
        return false;
    }


    //获取平台证书
    public function getCertificates()
    {
        $this->method = 'GET';
        $this->url = 'https://api.mch.weixin.qq.com/v3/certificates';
        $this->getSign();        //生成签名
        $this->getToken();        //生成Token
        $headerParam = [
            "Content-type: application/json;charset='utf-8'",
            "Accept:application/json",
            "User-Agent:" . $this->mch_id,
            'Authorization: ' . $this->authorization . ' ' . $this->token,
        ];
        $ThirdClass = new ThirdRequest();
        $result = $ThirdClass->curlGet($this->url, $headerParam);
        $list = json_decode($result, true)['data'];
        //查询本地证书序列号
        $str = md5(randomStr(32));
        $file_path = '../certificate/payments/platform_certificate/' . $this->platform_id;
        $FileHandleClass = new FileHandle();
        $FileHandleClass->createDir($file_path, 2);
        $file_path = $file_path . '/' . $str . '.pem';
        $PaymentConfigModel = new PaymentConfig();
        foreach ($list as $val) {
            if ($this->platform_serial_no != $val['serial_no']) {
                $serial_no = $val['serial_no'];//获取的平台证书序列号
                $encrypt_certificate = $val['encrypt_certificate'];
                $result = $this->decryptResource($encrypt_certificate['ciphertext'], $encrypt_certificate['associated_data'], $encrypt_certificate['nonce']);
                if (empty($result)) {
                    return callBack('error', 'param_error', '解密失败');
                }
                $PaymentConfigModel->updateInfo([
                    ['id', '=', $this->platform_id]
                ], [
                    'wx_platform_serial_no' => $serial_no,
                    'wx_platform_certificate' => $file_path
                ]);
                file_put_contents($file_path, $result);//获取的文件临时保存到服务器
            }
        }
        return callBack('success', 'success', '获取平台证书成功');
    }


    //加密敏感字符串
    public function getEncrypts($str = "张三")
    {
        $public_key = file_get_contents($this->platform_certificate);
        $encrypted = '';
        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
            $sign = base64_encode($encrypted);
        } else {
            return false;
        }
        return $sign;//返回加密的敏感字符串
    }


    //解密敏感字符串
    public function decryptToString($str)
    {
        $encrypted = '';
        $public_key = file_get_contents($this->apiclient_key);
        openssl_private_decrypt(base64_decode($str), $encrypted, $public_key);
        return $encrypted;//返回解密的字符串
    }


    /**
     * 解密resource对象
     * @param $ciphertext 数据密文
     * @param $associated_data 附加数据
     * @param $nonce 随机串
     * @return false|mixed|string
     */
    public function decryptResource($ciphertext, $associated_data, $nonce)
    {
        if (strlen($this->secret_key) != 32) {
            return false;
        }
        $ciphertext = \base64_decode($ciphertext);
        if (strlen($ciphertext) <= 16) {
            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, $associated_data, $nonce, $this->secret_key);
        }

        // 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, $associated_data, $nonce, $this->secret_key);
        }

        // openssl (PHP >= 7.1 support AEAD)
        if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
            $ctext = substr($ciphertext, 0, -16);
            $authTag = substr($ciphertext, -16);
            return \openssl_decrypt($ctext,
                'aes-256-gcm',
                $this->secret_key,
                \OPENSSL_RAW_DATA,
                $nonce,
                $authTag,
                $associated_data);
        }
        throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值