| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 | 
							- <?php
 
- namespace Yansongda\Pay\Gateways\Alipay;
 
- use Exception;
 
- use Yansongda\Pay\Events;
 
- use Yansongda\Pay\Exceptions\GatewayException;
 
- use Yansongda\Pay\Exceptions\InvalidArgumentException;
 
- use Yansongda\Pay\Exceptions\InvalidConfigException;
 
- use Yansongda\Pay\Exceptions\InvalidSignException;
 
- use Yansongda\Pay\Gateways\Alipay;
 
- use Yansongda\Pay\Log;
 
- use Yansongda\Supports\Arr;
 
- use Yansongda\Supports\Collection;
 
- use Yansongda\Supports\Config;
 
- use Yansongda\Supports\Str;
 
- use Yansongda\Supports\Traits\HasHttpRequest;
 
- /**
 
-  * @author yansongda <me@yansongda.cn>
 
-  *
 
-  * @property string app_id alipay app_id
 
-  * @property string ali_public_key
 
-  * @property string private_key
 
-  * @property array http http options
 
-  * @property string mode current mode
 
-  * @property array log log options
 
-  * @property string pid ali pid
 
-  */
 
- class Support
 
- {
 
-     use HasHttpRequest;
 
-     /**
 
-      * Alipay gateway.
 
-      *
 
-      * @var string
 
-      */
 
-     protected $baseUri;
 
-     /**
 
-      * Config.
 
-      *
 
-      * @var Config
 
-      */
 
-     protected $config;
 
-     /**
 
-      * Instance.
 
-      *
 
-      * @var Support
 
-      */
 
-     private static $instance;
 
-     /**
 
-      * Bootstrap.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      */
 
-     private function __construct(Config $config)
 
-     {
 
-         $this->baseUri = Alipay::URL[$config->get('mode', Alipay::MODE_NORMAL)];
 
-         $this->config = $config;
 
-         $this->setHttpOptions();
 
-     }
 
-     /**
 
-      * __get.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      *
 
-      * @param $key
 
-      *
 
-      * @return mixed|Config|null
 
-      */
 
-     public function __get($key)
 
-     {
 
-         return $this->getConfig($key);
 
-     }
 
-     /**
 
-      * create.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      *
 
-      * @return Support
 
-      */
 
-     public static function create(Config $config)
 
-     {
 
-         if ('cli' === php_sapi_name() || !(self::$instance instanceof self)) {
 
-             self::$instance = new self($config);
 
-         }
 
-         return self::$instance;
 
-     }
 
-     /**
 
-      * getInstance.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      *
 
-      * @throws InvalidArgumentException
 
-      *
 
-      * @return Support
 
-      */
 
-     public static function getInstance()
 
-     {
 
-         if (is_null(self::$instance)) {
 
-             throw new InvalidArgumentException('You Should [Create] First Before Using');
 
-         }
 
-         return self::$instance;
 
-     }
 
-     /**
 
-      * clear.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      */
 
-     public function clear()
 
-     {
 
-         self::$instance = null;
 
-     }
 
-     /**
 
-      * Get Alipay API result.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      *
 
-      * @throws GatewayException
 
-      * @throws InvalidConfigException
 
-      * @throws InvalidSignException
 
-      */
 
-     public static function requestApi(array $data): Collection
 
-     {
 
-         Events::dispatch(new Events\ApiRequesting('Alipay', '', self::$instance->getBaseUri(), $data));
 
-         $data = array_filter($data, function ($value) {
 
-             return ('' == $value || is_null($value)) ? false : true;
 
-         });
 
-         $result = json_decode(self::$instance->post('', $data), true);
 
-         Events::dispatch(new Events\ApiRequested('Alipay', '', self::$instance->getBaseUri(), $result));
 
-         return self::processingApiResult($data, $result);
 
-     }
 
-     /**
 
-      * Generate sign.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      *
 
-      * @throws InvalidConfigException
 
-      */
 
-     public static function generateSign(array $params): string
 
-     {
 
-         $privateKey = self::$instance->private_key;
 
-         if (is_null($privateKey)) {
 
-             throw new InvalidConfigException('Missing Alipay Config -- [private_key]');
 
-         }
 
-         if (Str::endsWith($privateKey, '.pem')) {
 
-             $privateKey = openssl_pkey_get_private(
 
-                 Str::startsWith($privateKey, 'file://') ? $privateKey : 'file://'.$privateKey
 
-             );
 
-         } else {
 
-             $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n".
 
-                 wordwrap($privateKey, 64, "\n", true).
 
-                 "\n-----END RSA PRIVATE KEY-----";
 
-         }
 
-         openssl_sign(self::getSignContent($params), $sign, $privateKey, OPENSSL_ALGO_SHA256);
 
-         $sign = base64_encode($sign);
 
-         Log::debug('Alipay Generate Sign', [$params, $sign]);
 
-         if (is_resource($privateKey)) {
 
-             openssl_free_key($privateKey);
 
-         }
 
-         return $sign;
 
-     }
 
-     /**
 
-      * Verify sign.
 
-      *
 
-      * @author yansongda <me@yansonga.cn>
 
-      *
 
-      * @param bool        $sync
 
-      * @param string|null $sign
 
-      *
 
-      * @throws InvalidConfigException
 
-      */
 
-     public static function verifySign(array $data, $sync = false, $sign = null): bool
 
-     {
 
-         $publicKey = self::$instance->ali_public_key;
 
-         if (is_null($publicKey)) {
 
-             throw new InvalidConfigException('Missing Alipay Config -- [ali_public_key]');
 
-         }
 
-         if (Str::endsWith($publicKey, '.crt')) {
 
-             $publicKey = file_get_contents($publicKey);
 
-         } elseif (Str::endsWith($publicKey, '.pem')) {
 
-             $publicKey = openssl_pkey_get_public(
 
-                 Str::startsWith($publicKey, 'file://') ? $publicKey : 'file://'.$publicKey
 
-             );
 
-         } else {
 
-             $publicKey = "-----BEGIN PUBLIC KEY-----\n".
 
-                 wordwrap($publicKey, 64, "\n", true).
 
-                 "\n-----END PUBLIC KEY-----";
 
-         }
 
-         $sign = $sign ?? $data['sign'];
 
-         $toVerify = $sync ? json_encode($data, JSON_UNESCAPED_UNICODE) : self::getSignContent($data, true);
 
-         $isVerify = 1 === openssl_verify($toVerify, base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256);
 
-         if (is_resource($publicKey)) {
 
-             openssl_free_key($publicKey);
 
-         }
 
-         return $isVerify;
 
-     }
 
-     /**
 
-      * Get signContent that is to be signed.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      *
 
-      * @param bool $verify
 
-      */
 
-     public static function getSignContent(array $data, $verify = false): string
 
-     {
 
-         ksort($data);
 
-         $stringToBeSigned = '';
 
-         foreach ($data as $k => $v) {
 
-             if ($verify && 'sign' != $k && 'sign_type' != $k) {
 
-                 $stringToBeSigned .= $k.'='.$v.'&';
 
-             }
 
-             if (!$verify && '' !== $v && !is_null($v) && 'sign' != $k && '@' != substr($v, 0, 1)) {
 
-                 $stringToBeSigned .= $k.'='.$v.'&';
 
-             }
 
-         }
 
-         Log::debug('Alipay Generate Sign Content Before Trim', [$data, $stringToBeSigned]);
 
-         return trim($stringToBeSigned, '&');
 
-     }
 
-     /**
 
-      * Convert encoding.
 
-      *
 
-      * @author yansongda <me@yansonga.cn>
 
-      *
 
-      * @param string|array $data
 
-      * @param string       $to
 
-      * @param string       $from
 
-      */
 
-     public static function encoding($data, $to = 'utf-8', $from = 'gb2312'): array
 
-     {
 
-         return Arr::encoding((array) $data, $to, $from);
 
-     }
 
-     /**
 
-      * Get service config.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      *
 
-      * @param string|null $key
 
-      * @param mixed|null  $default
 
-      *
 
-      * @return mixed|null
 
-      */
 
-     public function getConfig($key = null, $default = null)
 
-     {
 
-         if (is_null($key)) {
 
-             return $this->config->all();
 
-         }
 
-         if ($this->config->has($key)) {
 
-             return $this->config[$key];
 
-         }
 
-         return $default;
 
-     }
 
-     /**
 
-      * Get Base Uri.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      *
 
-      * @return string
 
-      */
 
-     public function getBaseUri()
 
-     {
 
-         return $this->baseUri;
 
-     }
 
-     /**
 
-      * 生成应用证书SN.
 
-      *
 
-      * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
 
-      *
 
-      * @param $certPath
 
-      *
 
-      * @throws /Exception
 
-      */
 
-     public static function getCertSN($certPath): string
 
-     {
 
-         if (!is_file($certPath)) {
 
-             throw new Exception('unknown certPath -- [getCertSN]');
 
-         }
 
-         $x509data = file_get_contents($certPath);
 
-         if (false === $x509data) {
 
-             throw new Exception('Alipay CertSN Error -- [getCertSN]');
 
-         }
 
-         openssl_x509_read($x509data);
 
-         $certdata = openssl_x509_parse($x509data);
 
-         if (empty($certdata)) {
 
-             throw new Exception('Alipay openssl_x509_parse Error -- [getCertSN]');
 
-         }
 
-         $issuer_arr = [];
 
-         foreach ($certdata['issuer'] as $key => $val) {
 
-             $issuer_arr[] = $key.'='.$val;
 
-         }
 
-         $issuer = implode(',', array_reverse($issuer_arr));
 
-         Log::debug('getCertSN:', [$certPath, $issuer, $certdata['serialNumber']]);
 
-         return md5($issuer.$certdata['serialNumber']);
 
-     }
 
-     /**
 
-      * 生成支付宝根证书SN.
 
-      *
 
-      * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
 
-      *
 
-      * @param $certPath
 
-      *
 
-      * @return string
 
-      *
 
-      * @throws /Exception
 
-      */
 
-     public static function getRootCertSN($certPath)
 
-     {
 
-         if (!is_file($certPath)) {
 
-             throw new Exception('unknown certPath -- [getRootCertSN]');
 
-         }
 
-         $x509data = file_get_contents($certPath);
 
-         if (false === $x509data) {
 
-             throw new Exception('Alipay CertSN Error -- [getRootCertSN]');
 
-         }
 
-         $kCertificateEnd = '-----END CERTIFICATE-----';
 
-         $certStrList = explode($kCertificateEnd, $x509data);
 
-         $md5_arr = [];
 
-         foreach ($certStrList as $one) {
 
-             if (!empty(trim($one))) {
 
-                 $_x509data = $one.$kCertificateEnd;
 
-                 openssl_x509_read($_x509data);
 
-                 $_certdata = openssl_x509_parse($_x509data);
 
-                 if (in_array($_certdata['signatureTypeSN'], ['RSA-SHA256', 'RSA-SHA1'])) {
 
-                     $issuer_arr = [];
 
-                     foreach ($_certdata['issuer'] as $key => $val) {
 
-                         $issuer_arr[] = $key.'='.$val;
 
-                     }
 
-                     $_issuer = implode(',', array_reverse($issuer_arr));
 
-                     if (0 === strpos($_certdata['serialNumber'], '0x')) {
 
-                         $serialNumber = self::bchexdec($_certdata['serialNumber']);
 
-                     } else {
 
-                         $serialNumber = $_certdata['serialNumber'];
 
-                     }
 
-                     $md5_arr[] = md5($_issuer.$serialNumber);
 
-                     Log::debug('getRootCertSN Sub:', [$certPath, $_issuer, $serialNumber]);
 
-                 }
 
-             }
 
-         }
 
-         return implode('_', $md5_arr);
 
-     }
 
-     /**
 
-      * processingApiResult.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      *
 
-      * @param $data
 
-      * @param $result
 
-      *
 
-      * @throws GatewayException
 
-      * @throws InvalidConfigException
 
-      * @throws InvalidSignException
 
-      */
 
-     protected static function processingApiResult($data, $result): Collection
 
-     {
 
-         $method = str_replace('.', '_', $data['method']).'_response';
 
-         if (!isset($result['sign']) || '10000' != $result[$method]['code']) {
 
-             throw new GatewayException('Get Alipay API Error:'.$result[$method]['msg'].(isset($result[$method]['sub_code']) ? (' - '.$result[$method]['sub_code']) : ''), $result);
 
-         }
 
-         if (self::verifySign($result[$method], true, $result['sign'])) {
 
-             return new Collection($result[$method]);
 
-         }
 
-         Events::dispatch(new Events\SignFailed('Alipay', '', $result));
 
-         throw new InvalidSignException('Alipay Sign Verify FAILED', $result);
 
-     }
 
-     /**
 
-      * Set Http options.
 
-      *
 
-      * @author yansongda <me@yansongda.cn>
 
-      */
 
-     protected function setHttpOptions(): self
 
-     {
 
-         if ($this->config->has('http') && is_array($this->config->get('http'))) {
 
-             $this->config->forget('http.base_uri');
 
-             $this->httpOptions = $this->config->get('http');
 
-         }
 
-         return $this;
 
-     }
 
-     /**
 
-      * 0x转高精度数字.
 
-      *
 
-      * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html
 
-      *
 
-      * @param $hex
 
-      *
 
-      * @return int|string
 
-      */
 
-     private static function bchexdec($hex)
 
-     {
 
-         $dec = 0;
 
-         $len = strlen($hex);
 
-         for ($i = 1; $i <= $len; ++$i) {
 
-             if (ctype_xdigit($hex[$i - 1])) {
 
-                 $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
 
-             }
 
-         }
 
-         return str_replace('.00', '', $dec);
 
-     }
 
- }
 
 
  |