| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- <?php
- declare (strict_types = 1);
- namespace library\utils;
- // +----------------------------------------------------------------------
- // | [ WE CAN DO IT MORE SIMPLE ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2018-2020 rights reserved.
- // +----------------------------------------------------------------------
- // | Author: TABLE ME
- // +----------------------------------------------------------------------
- // | Date: 2020-10-04 18:31
- // +----------------------------------------------------------------------
- use GuzzleHttp\Exception\RequestException;
- use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
- use WechatPay\GuzzleMiddleware\Util\PemUtil;
- use GuzzleHttp\HandlerStack as HandlerStackWx;
- use GuzzleHttp\Client as GuzzleHttpClient;
- class wxpay {
-
- const KEY_LENGTH_BYTE = 32;
- const AUTH_TAG_LENGTH_BYTE = 16;
-
- private $config;
- private $client;
- public $errorMsg = "操作失败";
-
-
-
- /**
- * 构造函数
- * @param type $config
- */
- public function __construct($config = [])
- {
- if(empty($config)) $config = config('wxpay');
- $this->config = $config;
- $merchantPrivateKey = PemUtil::loadPrivateKey($this->config["PrivateKey"]); // 商户私钥文件路径
- // 微信支付平台配置
- $wechatpayCertificate = PemUtil::loadCertificate($this->config["Certificate"]); // 微信支付平台证书文件路径
- // 构造一个WechatPayMiddleware
- $wechatpayMiddleware = WechatPayMiddleware::builder()
- ->withMerchant($this->config["MCHID"],$this->config["merchantSerialNumber"], $merchantPrivateKey) // 传入商户相关配置
- ->withWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array
- ->build();
- // 将WechatPayMiddleware添加到Guzzle的HandlerStack中
- $stack = HandlerStackWx::create();
- $stack->push($wechatpayMiddleware, 'wechatpay');
- // 创建Guzzle HTTP Client时,将HandlerStack传入,接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签
- $this->client = new GuzzleHttpClient(['handler' => $stack]);
- }
- /**
- * 微信小程序支付
- * @param type $post
- */
- public function wxmpPay($post=[]){
- $apiUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
- $jsonData = [];
- //商户信息
- $jsonData["appid"] = $this->config["APPID"];
- $jsonData["mchid"] = $this->config["MCHID"];
- $jsonData["notify_url"] = $this->config["NOTIFY_URL"];
- //必填参数
- $jsonData["description"] = $post["description"];//描述
- $jsonData["out_trade_no"] = $post["out_trade_no"];//商户订单号
- $jsonData["amount"] = [
- "total"=> floatval($post["total"])*100,//订单金额,分
- ];
- // $jsonData["scene_info"] = [//支付场景描述
- // "payer_client_ip"=>$post["payer_client_ip"],//客户端IP
- // ];
- $jsonData["time_expire"] = date("Y-m-d\TH:i:s+08:00",time()+30*60);//交易结束时间2018-06-08T10:34:56+08:00
- $jsonData["payer"]=[
- "openid"=>$post["openid"]
- ];
- $result = $this->clientHttp("POST", $apiUrl, $jsonData);
- if(empty($result)){
- $this->errorMsg = "支付错误001";
- return false;
- }
- $resuleAr = json_decode($result,true);
- if(empty($resuleAr)){
- $this->errorMsg = "支付错误002";
- return false;
- }
- if(empty($resuleAr["prepay_id"])){
- $this->errorMsg = "支付错误003";
- return false;
- }
- //组装支付参数
- $payInfo=array();
- $info['appId'] = $this->config["APPID"];
- $info['timeStamp'] = time();
- $info['nonceStr'] = $this->random_number(); //生成随机数,下面有生成实例,统一下单接口需要
- $info["package"] = "prepay_id=".$result['prepay_id'];
- $info['signType'] = 'MD5';
- $info['paySign'] = $this->MakeSign($info);
- return $info;
- }
- /**
- * h5支付
- */
- public function wapPay($post=[]){
- $apiUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/h5";
- $jsonData = [];
- //商户信息
- $jsonData["appid"] = $this->config["APPID"];
- $jsonData["mchid"] = $this->config["MCHID"];
- $jsonData["notify_url"] = $this->config["NOTIFY_URL"];
- //必填参数
- $jsonData["description"] = $post["description"];//描述
- $jsonData["out_trade_no"] = $post["out_trade_no"];//商户订单号
- $jsonData["amount"] = [
- "total"=> floatval($post["total"])*100,//订单金额,分
- ];
- $jsonData["scene_info"] = [//支付场景描述
- "payer_client_ip"=>$post["payer_client_ip"],//客户端IP
- "h5_info"=>[//h5场景信息
- "type"=>"Wap",//iOS, Android, Wap
- ]
- ];
- $jsonData["time_expire"] = date("Y-m-d\TH:i:s+08:00",time()+30*60);//交易结束时间2018-06-08T10:34:56+08:00
- $result = $this->clientHttp("POST", $apiUrl, $jsonData);
- if($result){
- $resuleAr = json_decode($result,true);
- if(empty($resuleAr) || empty($resuleAr["h5_url"])){
- $this->errorMsg = "h5_url不存在";
- return false;
- }
- return $resuleAr["h5_url"];
- }
- return false;
- }
-
-
- /**
- * app支付
- */
- public function appPay($post=[]){
- $apiUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
- $jsonData = [];
- //商户信息
- $jsonData["appid"] = $this->config["APPID"];
- $jsonData["mchid"] = $this->config["MCHID"];
- $jsonData["notify_url"] = $this->config["NOTIFY_URL"];
- //必填参数
- $jsonData["description"] = $post["description"];//描述
- $jsonData["out_trade_no"] = $post["out_trade_no"];//商户订单号
- $jsonData["amount"] = [
- "total"=> floatval($post["total"])*100,//订单金额,分
- ];
- $jsonData["scene_info"] = [//支付场景描述
- "payer_client_ip"=>$post["payer_client_ip"],//客户端IP
- ];
- $jsonData["time_expire"] = date("Y-m-d\TH:i:s+08:00",time()+30*60);//交易结束时间2018-06-08T10:34:56+08:00
- $this->clientHttp("POST", $apiUrl, $jsonData);
- }
-
-
-
-
- /**
- * 商户订单号
- * @param type $out_trade_no
- */
- public function searchNotifyOrder($out_trade_no){
- $apiUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{$out_trade_no}?mchid={$this->config["MCHID"]}";
- return $this->clientHttp("GET", $apiUrl);
- }
- /**
- * 关闭支付订单
- * @param type $out_trade_no
- */
- public function closePayOrder($out_trade_no){
- $apiUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{$out_trade_no}/close";
- $postData=array(
- "out_trade_no"=>$out_trade_no,
- "mchid"=>$this->config["MCHID"],
- );
- return $this->clientHttp("POST", $apiUrl,$postData);
- }
-
- /**
- * 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 function decryptToString($associatedData, $nonceStr, $ciphertext)
- {
- if (strlen($this->config['aesKey']) != self::KEY_LENGTH_BYTE) {
- $this->errorMsg = "无效的ApiV3Key,长度应为32个字节";
- return false;
- }
-
- $ciphertext = \base64_decode($ciphertext);
- if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
- $this->errorMsg = "字符长度错误";
- 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, $this->config['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, $this->config['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', $this->config['aesKey'], \OPENSSL_RAW_DATA, $nonceStr,$authTag, $associatedData);
- }
- $this->errorMsg = "AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php";
- return false;
- // throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
- }
-
- /**
- * http提交
- */
- public function clientHttp($type='POST',$url='',$json=[]){
- $httpData = [];
- if($type == 'POST'){
- $httpData["json"] = $json;
- }
- $httpData["headers"] = ['Accept' => 'application/json'];
- $resp = $this->client->request($type,$url,$httpData);
- $statusCode = $resp->getStatusCode();
- if ($statusCode == 200) { //处理成功
- return $resp->getBody()->getContents();
- } else if ($statusCode == 204) { //处理成功,无返回Body
- $this->errorMsg = "处理成功,无返回Body";
- return false;
- }else{
- $this->errorMsg = "未知错误";
- return false;
- }
- // try{
- // $resp = $this->client->request($type,$url,$httpData);
- // $statusCode = $resp->getStatusCode();
- // var_dump($statusCode);
- // if ($statusCode == 200) { //处理成功
- // return $resp->getBody()->getContents();
- // } else if ($statusCode == 204) { //处理成功,无返回Body
- // $this->errorMsg = "处理成功,无返回Body";
- // return false;
- // }else{
- // $this->errorMsg = "未知错误";
- // return false;
- // }
- // } catch (RequestException $e){
- // var_dump($e);
- // // 进行错误处理
- // $this->errorMsg = $e->getMessage();
- // if ($e->hasResponse()) {
- // $this->errorMsg = $e->getResponse()->getBody()->getContents();
- //// $this->errorMsg = "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody();
- // }
- // return false;
- // }
- }
- /**
- * 生成签名
- * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
- */
- public function MakeSign($values) {
- //签名步骤一:按字典序排序参数
- ksort($values);
- $string = $this->ToUrlParams($values);
- //签名步骤二:在string后加入KEY
- $string = $string . "&key=" . \WxPayConfig::KEY;
- //签名步骤三:MD5加密
- $string = md5($string);
- //签名步骤四:所有字符转为大写
- $result = strtoupper($string);
- return $result;
- }
- /**
- * 参数数组转换为url参数
- * @param array $urlObj
- */
- private function ToUrlParams($urlObj) {
- $buff = "";
- foreach ($urlObj as $k => $v) {
- $buff .= $k . "=" . $v . "&";
- }
- $buff = trim($buff, "&");
- return $buff;
- }
- /**
- * 生成随机字符串
- * @param type $len
- * @param type $format
- * @return type
- */
- public function random_number($len = 21, $format = 'ALL') {
- $is_abc = $is_numer = 0;
- $password = $tmp = '';
- switch ($format) {
- case 'ALL':
- $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- break;
- case 'CHAR':
- $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
- break;
- case 'NUMBER':
- $chars = '0123456789';
- break;
- default :
- $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- break;
- }
- mt_srand((double) microtime() * 1000000 * getmypid());
- while (strlen($password) < $len) {
- $tmp = substr($chars, (mt_rand() % strlen($chars)), 1);
- if (($is_numer <> 1 && is_numeric($tmp) && $tmp > 0 ) || $format == 'CHAR') {
- $is_numer = 1;
- }
- if (($is_abc <> 1 && preg_match('/[a-zA-Z]/', $tmp)) || $format == 'NUMBER') {
- $is_abc = 1;
- }
- $password .= $tmp;
- }
- if ($is_numer <> 1 || $is_abc <> 1 || empty($password)) {
- $password = $this->random_number($len, $format);
- }
- return $password;
- }
- }
|