From 43ff1b3fed64e2b877bce8ea28456be6c1b54331 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 9 Sep 2020 21:16:25 +0400 Subject: [PATCH 1/5] Refactoring, psr-4 compliance, unit testing, namespaces, update examples --- .gitignore | 2 + .phpunit.result.cache | 1 + README.md | 188 +-- UnitPay.php | 444 ------ composer.json | 12 +- composer.lock | 1978 ++++++++++++++++++++++++++ phpunit.xml | 11 + sample/handler.php | 8 +- sample/initPaymentApi.php | 22 +- sample/initPaymentForm.php | 6 +- sample/orderInfo.php | 16 +- sample/paymentInfo.php | 6 +- src/API/API.php | 148 ++ src/CashItem.php | 161 +++ src/Exceptions/UnsupportedDomain.php | 18 + src/Exceptions/WrongIpAddress.php | 19 + src/UnitPay.php | 350 +++++ tests/UnitPayAPITest.php | 60 + tests/UnitPayTest.php | 200 +++ 19 files changed, 2989 insertions(+), 661 deletions(-) create mode 100644 .gitignore create mode 100644 .phpunit.result.cache delete mode 100644 UnitPay.php create mode 100644 composer.lock create mode 100644 phpunit.xml create mode 100644 src/API/API.php create mode 100644 src/CashItem.php create mode 100644 src/Exceptions/UnsupportedDomain.php create mode 100644 src/Exceptions/WrongIpAddress.php create mode 100644 src/UnitPay.php create mode 100644 tests/UnitPayAPITest.php create mode 100644 tests/UnitPayTest.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0caea8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +vendor/ \ No newline at end of file diff --git a/.phpunit.result.cache b/.phpunit.result.cache new file mode 100644 index 0000000..d9dc36f --- /dev/null +++ b/.phpunit.result.cache @@ -0,0 +1 @@ +C:37:"PHPUnit\Runner\DefaultTestResultCache":1044:{a:2:{s:7:"defects";a:2:{s:23:"APITest::testApiSuccess";i:4;s:30:"UnitPayAPITest::testApiSuccess";i:4;}s:5:"times";a:18:{s:38:"UnitPayTest::testSuccessCreateInstance";d:0.037;s:35:"UnitPayTest::testFailCreateInstance";d:0.009;s:42:"UnitPayTest::testSuccessGetSignatureEquals";d:0.003;s:44:"UnitPayTest::testSuccessFormEqualsWithSecret";d:0;s:47:"UnitPayTest::testSuccessFormEqualsWithoutSecret";d:0;s:37:"UnitPayTest::testApiUnsupportedMethod";d:0.002;s:27:"UnitPayTest::testApiSuccess";d:0.87;s:29:"UnitPayTest::testRealIPGetter";d:0;s:39:"UnitPayTest::testHandlerFailEmptyMethod";d:0.003;s:39:"UnitPayTest::testHandlerFailEmptyParams";d:0;s:45:"UnitPayTest::testHandlerFailUnsupportedMethod";d:0;s:42:"UnitPayTest::testHandlerFailWrongSignature";d:0;s:42:"UnitPayTest::testHandlerFailWrongIpAddress";d:0.002;s:31:"UnitPayTest::testHandlerSuccess";d:0;s:33:"APITest::testApiUnsupportedMethod";d:0.044;s:23:"APITest::testApiSuccess";d:0.002;s:40:"UnitPayAPITest::testApiUnsupportedMethod";d:0.043;s:30:"UnitPayAPITest::testApiSuccess";d:0.883;}}} \ No newline at end of file diff --git a/README.md b/README.md index a566ecf..ae5edd3 100644 --- a/README.md +++ b/README.md @@ -1,197 +1,11 @@ # UnitPay PHP-SDK -Php sdk for [UnitPay.ru ](https://unitpay.ru) and [UnitPay.money ](https://unitpay.money) +Php sdk for [UnitPay.ru](https://unitpay.ru) and [UnitPay.money](https://unitpay.money) Documentation https://help.unitpay.ru and https://help.unitpay.money ## Examples ## These are just some quick examples. Check out the samples in [`/sample`](https://github.com/unitpay/php-sdk/blob/master/sample). -### Payment integration using UnitPay Form -```php -setBackUrl('http://domain.com') - ->setCustomerEmail('customer@domain.com') - ->setCustomerPhone('79001235555') - ->setCashItems(array( - new CashItem($itemName, 1, $orderSum) - )); - -$redirectUrl = $unitPay->form( - $publicId, - $orderSum, - $orderId, - $orderDesc, - $orderCurrency -); - -header("Location: " . $redirectUrl); -``` - -### Payment integration using UnitPay Api - -```php -api('initPayment', [ - 'account' => $orderId, - 'desc' => $orderDesc, - 'sum' => $orderSum, - 'paymentType' => 'yandex', - 'currency' => $orderCurrency, - 'projectId' => $projectId -]); - -// If need user redirect on Payment Gate -if (isset($response->result->type) - && $response->result->type == 'redirect') { - // Url on PaymentGate - $redirectUrl = $response->result->redirectUrl; - // Payment ID in Unitpay (you can save it) - $paymentId = $response->result->paymentId; - // User redirect - header("Location: " . $redirectUrl); - -// If without redirect (invoice) -} elseif (isset($response->result->type) - && $response->result->type == 'invoice') { - // Url on receipt page in Unitpay - $receiptUrl = $response->result->receiptUrl; - // Payment ID in Unitpay (you can save it) - $paymentId = $response->result->paymentId; - // Invoice Id in Payment Gate (you can save it) - $invoiceId = $response->result->invoiceId; - // User redirect - header("Location: " . $receiptUrl); - -// If error during api request -} elseif (isset($response->error->message)) { - $error = $response->error->message; - print 'Error: '.$error; -} -``` - -### Handler sample - -```php -checkHandlerRequest(); - - list($method, $params) = array($_GET['method'], $_GET['params']); - - // Very important! Validate request with your order data, before complete order - if ( - $params['orderSum'] != $orderSum || - $params['orderCurrency'] != $orderCurrency || - $params['account'] != $orderId || - $params['projectId'] != $projectId - ) { - // logging data and throw exception - throw new InvalidArgumentException('Order validation Error!'); - } - switch ($method) { - // Just check order (check server status, check order in DB and etc) - case 'check': - echo $unitPay->getSuccessHandlerResponse('Check Success. Ready to pay.'); - break; - // Method Pay means that the money received - case 'pay': - // Please complete order - echo $unitPay->getSuccessHandlerResponse('Pay Success'); - break; - // Method Error means that an error has occurred. - case 'error': - // Please log error text. - echo $unitPay->getSuccessHandlerResponse('Error logged'); - break; - } -// Oops! Something went wrong. -} catch (Exception $e) { - echo $unitPay->getErrorHandlerResponse($e->getMessage()); -} -``` - ## Installation ### Install composer package diff --git a/UnitPay.php b/UnitPay.php deleted file mode 100644 index ad0e143..0000000 --- a/UnitPay.php +++ /dev/null @@ -1,444 +0,0 @@ -name = $name; - $this->count = $count; - $this->price = $price; - $this->nds = $nds; - $this->type = $type; - $this->paymentMethod = $paymentMethod; - } - - /** - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * @return int - */ - public function getCount() - { - return $this->count; - } - - /** - * @return float - */ - public function getPrice() - { - return $this->price; - } - - /** - * @return string - */ - public function getNds() - { - return $this->nds; - } - - /** - * @return string - */ - public function getType() - { - return $this->type; - } - - /** - * @return string - */ - public function getPaymentMethod() - { - return $this->paymentMethod; - } -} - -/** - * Payment method UnitPay process - * - * @author UnitPay - */ -class UnitPay -{ - private $supportedCurrencies = array('EUR','UAH', 'BYR', 'USD','RUB'); - private $supportedUnitpayMethods = array('initPayment', 'getPayment'); - private $requiredUnitpayMethodsParams = array( - 'initPayment' => array('desc', 'account', 'sum'), - 'getPayment' => array('paymentId') - ); - private $supportedPartnerMethods = array('check', 'pay', 'error'); - private $supportedUnitpayIp = array( - '31.186.100.49', - '178.132.203.105', - '52.29.152.23', - '52.19.56.234', - '127.0.0.1' // for debug - ); - - private $secretKey; - - private $params = array(); - - private $apiUrl; - private $formUrl; - - public function __construct($domain, $secretKey = null) - { - $this->secretKey = $secretKey; - $this->apiUrl = "https://$domain/api"; - $this->formUrl = "https://$domain/pay/"; - } - - /** - * Create SHA-256 digital signature - * - * @param array $params - * @param $method - * - * @return string - */ - function getSignature(array $params, $method = null) - { - ksort($params); - unset($params['sign']); - unset($params['signature']); - array_push($params, $this->secretKey); - - if ($method) { - array_unshift($params, $method); - } - - return hash('sha256', join('{up}', $params)); - } - - /** - * Return IP address - * - * @return string - */ - protected function getIp() - { - return $_SERVER['REMOTE_ADDR']; - } - - /** - * Get URL for pay through the form - * - * @param string $publicKey - * @param string|float|int $sum - * @param string $account - * @param string $desc - * @param string $currency - * @param string $locale - * - * @return string - */ - public function form($publicKey, $sum, $account, $desc, $currency = 'RUB', $locale = 'ru') - { - $vitalParams = array( - 'account' => $account, - 'currency' => $currency, - 'desc' => $desc, - 'sum' => $sum - ); - - $this->params = array_merge($this->params, $vitalParams); - - if ($this->secretKey) { - $this->params['signature'] = $this->getSignature($vitalParams); - } - - $this->params['locale'] = $locale; - - return $this->formUrl . $publicKey . '?' . http_build_query($this->params); - } - - /** - * Set customer email - * - * @param string $email - * - * @return UnitPay - */ - public function setCustomerEmail($email) - { - $this->params['customerEmail'] = $email; - return $this; - } - - /** - * Set customer phone number - * - * @param string $phone - * - * @return UnitPay - */ - public function setCustomerPhone($phone) - { - $this->params['customerPhone'] = $phone; - return $this; - } - - /** - * Set list of paid goods - * - * @param CashItem[] $items - * - * @return UnitPay - */ - public function setCashItems($items) - { - $this->params['cashItems'] = base64_encode( - json_encode( - array_map(function ($item) { - /** @var CashItem $item */ - return array( - 'name' => $item->getName(), - 'count' => $item->getCount(), - 'price' => $item->getPrice(), - 'nds' => $item->getNds(), - 'type' => $item->getType(), - 'paymentMethod' => $item->getPaymentMethod(), - ); - }, $items))); - - return $this; - } - - /** - * Set callback URL - * - * @param string $backUrl - * @return UnitPay - */ - public function setBackUrl($backUrl) - { - $this->params['backUrl'] = $backUrl; - return $this; - } - - /** - * Call API - * - * @param $method - * @param array $params - * - * @return object - * - * @throws InvalidArgumentException - * @throws UnexpectedValueException - */ - public function api($method, $params = array()) - { - if (!in_array($method, $this->supportedUnitpayMethods)) { - throw new UnexpectedValueException('Method is not supported'); - } - - if (isset($this->requiredUnitpayMethodsParams[$method])) { - foreach ($this->requiredUnitpayMethodsParams[$method] as $rParam) { - if (!isset($params[$rParam])) { - throw new InvalidArgumentException('Param '.$rParam.' is null'); - } - } - } - - $params['secretKey'] = $this->secretKey; - if (empty($params['secretKey'])) { - throw new InvalidArgumentException('SecretKey is null'); - } - - $requestUrl = $this->apiUrl . '?' . http_build_query([ - 'method' => $method, - 'params' => $params - ], null, '&', PHP_QUERY_RFC3986); - - $response = json_decode(file_get_contents($requestUrl)); - if (!is_object($response)) { - throw new InvalidArgumentException('Temporary server error. Please try again later.'); - } - - return $response; - } - - /** - * Check request on handler from UnitPay - * - * @return bool - * - * @throws InvalidArgumentException - * @throws UnexpectedValueException - */ - public function checkHandlerRequest() - { - $ip = $this->getIp(); - if (!isset($_GET['method'])) { - throw new InvalidArgumentException('Method is null'); - } - - if (!isset($_GET['params'])) { - throw new InvalidArgumentException('Params is null'); - } - - list($method, $params) = array($_GET['method'], $_GET['params']); - - if (!in_array($method, $this->supportedPartnerMethods)) { - throw new UnexpectedValueException('Method is not supported'); - } - - if (!isset($params['signature']) || $params['signature'] != $this->getSignature($params, $method)) { - throw new InvalidArgumentException('Wrong signature'); - } - - /** - * IP address check - * @link http://help.unitpay.ru/article/67-ip-addresses - * @link http://help.unitpay.money/article/67-ip-addresses - */ - if (!in_array($ip, $this->supportedUnitpayIp)) { - throw new InvalidArgumentException('IP address Error'); - } - - return true; - } - - /** - * Response for UnitPay if handle success - * - * @param $message - * - * @return string - */ - public function getSuccessHandlerResponse($message) - { - return json_encode(array( - "result" => array( - "message" => $message - ) - )); - } - - /** - * Response for UnitPay if handle error - * - * @param $message - * - * @return string - */ - public function getErrorHandlerResponse($message) - { - return json_encode(array( - "error" => array( - "message" => $message - ) - )); - } -} \ No newline at end of file diff --git a/composer.json b/composer.json index 6a27766..ac0f23b 100644 --- a/composer.json +++ b/composer.json @@ -14,11 +14,15 @@ } ], "require":{ - "php":">=5.3.0" + "php":">=7.1.0", + "ext-json": "*" }, "autoload":{ - "classmap":[ - "./UnitPay.php" - ] + "psr-4": { + "UnitPay\\": "src/" + } + }, + "require-dev": { + "phpunit/phpunit": "^9" } } diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..892585d --- /dev/null +++ b/composer.lock @@ -0,0 +1,1978 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "8234ae77ad97ea1ef7e322cea3e63769", + "packages": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "f350df0268e904597e3bd9c4685c53e0e333feea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea", + "reference": "f350df0268e904597e3bd9c4685c53e0e333feea", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-05-29T17:27:14+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.10.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", + "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-06-29T13:22:24+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.9.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "88e519766fc58bd46b8265561fb79b54e2e00b28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/88e519766fc58bd46b8265561fb79b54e2e00b28", + "reference": "88e519766fc58bd46b8265561fb79b54e2e00b28", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2020-08-30T16:15:20+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "reference": "85265efd3af7ba3ca4b2a2c34dbfc5788dd29133", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2020-06-27T14:33:11+00:00" + }, + { + "name": "phar-io/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/c6bb6825def89e0a32220f88337f8ceaf1975fa0", + "reference": "c6bb6825def89e0a32220f88337f8ceaf1975fa0", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2020-06-27T14:39:04+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.2.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "d870572532cd70bc3fab58f2e23ad423c8404c44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d870572532cd70bc3fab58f2e23ad423c8404c44", + "reference": "d870572532cd70bc3fab58f2e23ad423c8404c44", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2020-08-15T11:14:08+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "e878a14a65245fbe78f8080eba03b47c3b705651" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e878a14a65245fbe78f8080eba03b47c3b705651", + "reference": "e878a14a65245fbe78f8080eba03b47c3b705651", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2020-06-27T10:12:23+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b20034be5efcdab4fb60ca3a29cba2949aead160", + "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2", + "php": "^7.2", + "phpdocumentor/reflection-docblock": "^5.0", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2020-07-08T12:44:21+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.1.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "f98f8466126d83b55b924a94d2244c53c216b8fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f98f8466126d83b55b924a94d2244c53c216b8fb", + "reference": "f98f8466126d83b55b924a94d2244c53c216b8fb", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.8", + "php": "^7.3 || ^8.0", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-07T08:07:10+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "25fefc5b19835ca653877fe081644a3f8c1d915e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/25fefc5b19835ca653877fe081644a3f8c1d915e", + "reference": "25fefc5b19835ca653877fe081644a3f8c1d915e", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-07-11T05:18:21+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "7a85b66acc48cacffdf87dadd3694e7123674298" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/7a85b66acc48cacffdf87dadd3694e7123674298", + "reference": "7a85b66acc48cacffdf87dadd3694e7123674298", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-08-06T07:04:15+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "6ff9c8ea4d3212b88fcf74e25e516e2c51c99324" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/6ff9c8ea4d3212b88fcf74e25e516e2c51c99324", + "reference": "6ff9c8ea4d3212b88fcf74e25e516e2c51c99324", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T11:55:37+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "cc49734779cbb302bf51a44297dab8c4bbf941e7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/cc49734779cbb302bf51a44297dab8c4bbf941e7", + "reference": "cc49734779cbb302bf51a44297dab8c4bbf941e7", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T11:58:13+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.3.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "93d78d8e2a06393a0d0c1ead6fe9984f1af1f88c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/93d78d8e2a06393a0d0c1ead6fe9984f1af1f88c", + "reference": "93d78d8e2a06393a0d0c1ead6fe9984f1af1f88c", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.1", + "phar-io/version": "^3.0.2", + "php": "^7.3 || ^8.0", + "phpspec/prophecy": "^1.11.1", + "phpunit/php-code-coverage": "^9.1.5", + "phpunit/php-file-iterator": "^3.0.4", + "phpunit/php-invoker": "^3.1", + "phpunit/php-text-template": "^2.0.2", + "phpunit/php-timer": "^5.0.1", + "sebastian/cli-parser": "^1.0", + "sebastian/code-unit": "^1.0.5", + "sebastian/comparator": "^4.0.3", + "sebastian/diff": "^4.0.2", + "sebastian/environment": "^5.1.2", + "sebastian/exporter": "^4.0.2", + "sebastian/global-state": "^5.0", + "sebastian/object-enumerator": "^4.0.2", + "sebastian/resource-operations": "^3.0.2", + "sebastian/type": "^2.2.1", + "sebastian/version": "^3.0.1" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-08-27T06:30:58+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2a4a38c56e62f7295bedb8b1b7439ad523d4ea82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2a4a38c56e62f7295bedb8b1b7439ad523d4ea82", + "reference": "2a4a38c56e62f7295bedb8b1b7439ad523d4ea82", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-08-12T10:49:21+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "c1e2df332c905079980b119c4db103117e5e5c90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/c1e2df332c905079980b119c4db103117e5e5c90", + "reference": "c1e2df332c905079980b119c4db103117e5e5c90", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:50:45+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ee51f9bb0c6d8a43337055db3120829fa14da819" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ee51f9bb0c6d8a43337055db3120829fa14da819", + "reference": "ee51f9bb0c6d8a43337055db3120829fa14da819", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:04:00+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "dcc580eadfaa4e7f9d2cf9ae1922134ea962e14f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dcc580eadfaa4e7f9d2cf9ae1922134ea962e14f", + "reference": "dcc580eadfaa4e7f9d2cf9ae1922134ea962e14f", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:05:46+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "33fcd6a26656c6546f70871244ecba4b4dced097" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/33fcd6a26656c6546f70871244ecba4b4dced097", + "reference": "33fcd6a26656c6546f70871244ecba4b4dced097", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-07-25T14:01:34+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "1e90b4cf905a7d06c420b1d2e9d11a4dc8a13113" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1e90b4cf905a7d06c420b1d2e9d11a4dc8a13113", + "reference": "1e90b4cf905a7d06c420b1d2e9d11a4dc8a13113", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-30T04:46:02+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "0a757cab9d5b7ef49a619f1143e6c9c1bc0fe9d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/0a757cab9d5b7ef49a619f1143e6c9c1bc0fe9d2", + "reference": "0a757cab9d5b7ef49a619f1143e6c9c1bc0fe9d2", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:07:24+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "571d721db4aec847a0e59690b954af33ebf9f023" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/571d721db4aec847a0e59690b954af33ebf9f023", + "reference": "571d721db4aec847a0e59690b954af33ebf9f023", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:08:55+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "22ae663c951bdc39da96603edc3239ed3a299097" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/22ae663c951bdc39da96603edc3239ed3a299097", + "reference": "22ae663c951bdc39da96603edc3239ed3a299097", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-08-07T04:09:03+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e02bf626f404b5daec382a7b8a6a4456e49017e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e02bf626f404b5daec382a7b8a6a4456e49017e5", + "reference": "e02bf626f404b5daec382a7b8a6a4456e49017e5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-07-22T18:33:42+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "074fed2d0a6d08e1677dd8ce9d32aecb384917b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/074fed2d0a6d08e1677dd8ce9d32aecb384917b8", + "reference": "074fed2d0a6d08e1677dd8ce9d32aecb384917b8", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:11:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "127a46f6b057441b201253526f81d5406d6c7840" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/127a46f6b057441b201253526f81d5406d6c7840", + "reference": "127a46f6b057441b201253526f81d5406d6c7840", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:12:55+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "062231bf61d2b9448c4fa5a7643b5e1829c11d63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/062231bf61d2b9448c4fa5a7643b5e1829c11d63", + "reference": "062231bf61d2b9448c4fa5a7643b5e1829c11d63", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:14:17+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0653718a5a629b065e91f774595267f8dc32e213" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0653718a5a629b065e91f774595267f8dc32e213", + "reference": "0653718a5a629b065e91f774595267f8dc32e213", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:16:22+00:00" + }, + { + "name": "sebastian/type", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "86991e2b33446cd96e648c18bcdb1e95afb2c05a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/86991e2b33446cd96e648c18bcdb1e95afb2c05a", + "reference": "86991e2b33446cd96e648c18bcdb1e95afb2c05a", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-07-05T08:31:53+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "626586115d0ed31cb71483be55beb759b5af5a3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/626586115d0ed31cb71483be55beb759b5af5a3c", + "reference": "626586115d0ed31cb71483be55beb759b5af5a3c", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-06-26T12:18:43+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.18.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "1c302646f6efc070cd46856e600e5e0684d6b454" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454", + "reference": "1c302646f6efc070cd46856e600e5e0684d6b454", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.18-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-07-14T12:35:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "75a63c33a8577608444246075ea0af0d052e452a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2020-07-12T23:59:07+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.9.1", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0 || ^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<3.9.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36 || ^7.5.13" + }, + "type": "library", + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2020-07-08T17:02:28+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.1.0", + "ext-json": "*" + }, + "platform-dev": [], + "plugin-api-version": "1.1.0" +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..1e84624 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,11 @@ + + + + + ./tests + + + diff --git a/sample/handler.php b/sample/handler.php index 9673ffd..55d3713 100644 --- a/sample/handler.php +++ b/sample/handler.php @@ -7,16 +7,18 @@ * @link http://help.unitpay.money/article/35-confirmation-payment */ +use UnitPay\UnitPay; + require_once('./orderInfo.php'); -require_once('../UnitPay.php'); +require_once('../vendor/autoload.php'); $unitPay = new UnitPay($domain, $secretKey); try { // Validate request (check ip address, signature and etc) - $unitPay->checkHandlerRequest(); + $unitPay->handleRequest(); - list($method, $params) = array($_GET['method'], $_GET['params']); + [$method, $params] = [$_GET['method'], $_GET['params']]; // Very important! Validate request with your order data, before complete order if ( diff --git a/sample/initPaymentApi.php b/sample/initPaymentApi.php index 2aa0484..0ac8470 100644 --- a/sample/initPaymentApi.php +++ b/sample/initPaymentApi.php @@ -1,5 +1,7 @@ api('initPayment', [ - 'account' => $orderId, - 'desc' => $orderDesc, - 'sum' => $orderSum, + 'account' => $orderId, + 'desc' => $orderDesc, + 'sum' => $orderSum, 'paymentType' => 'yandex', - 'currency' => $orderCurrency, - 'projectId' => $projectId, + 'currency' => $orderCurrency, + 'projectId' => $projectId, ]); // If need user redirect on Payment Gate if (isset($response->result->type) - && $response->result->type == 'redirect') { + && $response->result->type === 'redirect') { // Url on PaymentGate $redirectUrl = $response->result->redirectUrl; // Payment ID in Unitpay (you can save it) $paymentId = $response->result->paymentId; // User redirect - header("Location: " . $redirectUrl); + header("Location: ".$redirectUrl); // If without redirect (invoice) } elseif (isset($response->result->type) - && $response->result->type == 'invoice') { + && $response->result->type === 'invoice') { // Url on receipt page in Unitpay $receiptUrl = $response->result->receiptUrl; // Payment ID in Unitpay (you can save it) @@ -57,7 +59,7 @@ // Invoice Id in Payment Gate (you can save it) $invoiceId = $response->result->invoiceId; // User redirect - header("Location: " . $receiptUrl); + header("Location: ".$receiptUrl); // If error during api request } elseif (isset($response->error->message)) { diff --git a/sample/initPaymentForm.php b/sample/initPaymentForm.php index d582c84..b6a7e52 100644 --- a/sample/initPaymentForm.php +++ b/sample/initPaymentForm.php @@ -7,8 +7,10 @@ * @link http://help.unitpay.money/article/31-creating-payment-from-payment-form */ +use UnitPay\UnitPay; + require_once('./orderInfo.php'); -require_once('../UnitPay.php'); +require_once('../vendor/autoload.php'); $unitPay = new UnitPay($domain, $secretKey); @@ -20,4 +22,4 @@ $orderCurrency ); -header("Location: " . $redirectUrl); +header("Location: ".$redirectUrl); diff --git a/sample/orderInfo.php b/sample/orderInfo.php index 26f517e..e097fcd 100644 --- a/sample/orderInfo.php +++ b/sample/orderInfo.php @@ -5,16 +5,16 @@ */ // Project Data -$domain = 'unitpay.money'; -$projectId = 1; -$secretKey = '2907b9e4a48d9450b6f125b8f184be8a'; -$publicId = '15155-ae12d'; +$domain = 'unitpay.money'; +$projectId = 1; +$secretKey = '2907b9e4a48d9450b6f125b8f184be8a'; +$publicId = '15155-ae12d'; // My item Info $itemName = 'Iphone 6 Skin Cover'; // My Order Data -$orderId = 'a183f94-1434-1e44'; -$orderSum = 900; -$orderDesc = 'Payment for item "'.$itemName.'"'; -$orderCurrency = 'RUB'; \ No newline at end of file +$orderId = 'a183f94-1434-1e44'; +$orderSum = 900; +$orderDesc = 'Payment for item "'.$itemName.'"'; +$orderCurrency = 'RUB'; \ No newline at end of file diff --git a/sample/paymentInfo.php b/sample/paymentInfo.php index 4dcfe58..1f0dbd3 100644 --- a/sample/paymentInfo.php +++ b/sample/paymentInfo.php @@ -1,6 +1,6 @@ api('getPayment', [ - 'paymentId' => 3403575 + 'paymentId' => 3403575, ]); // If need user redirect on Payment Gate diff --git a/src/API/API.php b/src/API/API.php new file mode 100644 index 0000000..efe5097 --- /dev/null +++ b/src/API/API.php @@ -0,0 +1,148 @@ + + * @version 1.0.0 + * @since 3.0.0 + */ +class API +{ + /** + * @var API + */ + private static $instance; + + /** + * @var string + */ + private $apiUrl; + + /** + * @var string + */ + private $secretKey; + + /** + * @var string|null + */ + private $personalSecret; + + private static $supportedMethod = [ + 'initPayment', 'getPayment', // payments + + 'getPartner', // cabinet + ]; + + private $requiredUnitpayMethodsParams = [ + 'initPayment' => ['desc', 'account', 'sum'], + 'getPayment' => ['paymentId'], + ]; + + public function __construct(string $apiUrl, string $secretKey, ?string $personalSecret = null) + { + + $this->apiUrl = $apiUrl; + $this->secretKey = $secretKey; + $this->personalSecret = $personalSecret; + + self::$instance = $this; + } + + /** + * @param string $newSupportedMethod + * + * @return $this + * @since 3.0.0 + * + */ + public function addSupportedMethod(string $newSupportedMethod): self + { + self::$supportedMethod[] = $newSupportedMethod; + + return $this; + } + + /** + * Call API + * + * @param string $method + * @param array $params + * + * @return object + * + * @throws InvalidArgumentException + * @throws UnexpectedValueException + * + * @see https://help.unitpay.money/category/180-api + * @see https://help.unitpay.money/category/181-api + * @see https://help.unitpay.money/category/182-api + * @see https://help.unitpay.money/category/183-api + * + * @since 1.0.0 + */ + public function request(string $method, $params = []) + { + if (!in_array($method, self::$supportedMethod, true)) { + throw new UnexpectedValueException('Method is not supported'); + } + + if (isset($this->requiredUnitpayMethodsParams[$method])) { + foreach ($this->requiredUnitpayMethodsParams[$method] as $rParam) { + if (!isset($params[$rParam])) { + throw new InvalidArgumentException(sprintf('Param `%s` is empty', $rParam)); + } + } + } + + if (!isset($params['secretKey'])) { + $params['secretKey'] = $this->secretKey; + } + + if (empty($params['secretKey'])) { + throw new InvalidArgumentException('SecretKey is null'); + } + + $requestUrl = sprintf("%s?%s", $this->apiUrl, http_build_query([ + 'method' => $method, + 'params' => $params, + ], "", '&', PHP_QUERY_RFC3986) + ); + + $response = json_decode(file_get_contents($requestUrl), false); + if (!is_object($response)) { + throw new InvalidArgumentException('Temporary server error. Please try again later.'); + } + + return $response; + } + + /** + * @return string + */ + public function getSecretKey(): string + { + return $this->secretKey; + } + + /** + * @return string|null + */ + public function getPersonalSecret(): ?string + { + return $this->personalSecret; + } + + public static function getInstance(): API + { + return self::$instance; + } +} \ No newline at end of file diff --git a/src/CashItem.php b/src/CashItem.php new file mode 100644 index 0000000..53a755d --- /dev/null +++ b/src/CashItem.php @@ -0,0 +1,161 @@ + + * @version 3.0.0 + * @since 1.0.0 + */ +class CashItem +{ + public const NDS_NONE = 'none'; + public const NDS_0 = 'vat0'; + public const NDS_10 = 'vat10'; + public const NDS_20 = 'vat20'; + + /** Товар */ + public const TYPE_COMMODITY = 'commodity'; + /** Подакцизный товар */ + public const TYPE_EXCISE = 'excise'; + /** Работа */ + public const TYPE_JOB = 'job'; + /** Услуга */ + public const TYPE_SERVICE = 'service'; + /** Ставка */ + public const TYPE_GAMBLING_BET = 'gambling_bet'; + /** Выигрыш */ + public const TYPE_GAMBLING_PRIZE = 'gambling_prize'; + /** Лотерейный билет */ + public const TYPE_LOTTERY = 'lottery'; + /** Выигрыш лотереи */ + public const TYPE_LOTTERY_PRIZE = 'lottery_prize'; + /** Результаты интеллектуальной деятельности */ + public const TYPE_INTELLECTUAL_ACTIVITY = 'intellectual_activity'; + /** Платёж */ + public const TYPE_PAYMENT = 'payment'; + /** Агентское вознаграждение */ + public const TYPE_AGENT_COMMISSION = 'agent_commission'; + /** Составной предмет расчёта */ + public const TYPE_COMPOSITE = 'composite'; + /** Иной предмет расчёта */ + public const TYPE_ANOTHER = 'another'; + /** */ + public const TYPE_PROPERTY_RIGHT = 'property_right'; + /** */ + public const TYPE_NON_OPERATING_GAIN = 'non-operating_gain'; + /** */ + public const TYPE_INSURANCE_PREMIUM = 'insurance_premium'; + /** Налог с продажи */ + public const TYPE_SALES_TAX = 'sales_tax'; + /** Курортный сбор */ + public const TYPE_RESORT_FEE = 'resort_fee'; + + /** 100% предоплата */ + public const PAYMENT_METHOD_PREPAYMENT_FULL = 'full_prepayment'; + /** Частичная предоплата */ + public const PAYMENT_METHOD_PREPAYMENT = 'prepayment'; + /** Аванс */ + public const PAYMENT_METHOD_ADVANCE = 'advance'; + /** Полный расчёт */ + public const PAYMENT_METHOD_PAYMENT_FULL = 'full_payment'; + + public const ALLOWED_PAYMENT_METHODS = [ + self::PAYMENT_METHOD_PREPAYMENT_FULL, + self::PAYMENT_METHOD_PREPAYMENT, + self::PAYMENT_METHOD_ADVANCE, + self::PAYMENT_METHOD_PAYMENT_FULL, + ]; + + /** + * @var string + */ + private $name; + + /** + * @var int + */ + private $count; + + /** + * @var float + */ + private $price; + + /** + * @var string + */ + private $nds; + + /** + * @var string + */ + private $type; + + /** + * @var string + */ + private $paymentMethod; + + public function __construct( + $name, + $count, + $price, + $nds = self::NDS_NONE, + $type = self::TYPE_COMMODITY, + $paymentMethod = self::PAYMENT_METHOD_PREPAYMENT_FULL + ) { + $this->name = $name; + $this->count = $count; + $this->price = $price; + $this->nds = $nds; + $this->type = $type; + $this->paymentMethod = $paymentMethod; + } + + /** + * @return string + */ + public function getName(): string { + return $this->name; + } + + /** + * @return int + */ + public function getCount(): int { + return $this->count; + } + + /** + * @return string + */ + public function getNds(): string { + return $this->nds; + } + + /** + * @return string + */ + public function getPaymentMethod(): string { + return $this->paymentMethod; + } + + /** + * @return float + */ + public function getPrice(): float { + return $this->price; + } + + /** + * @return string + */ + public function getType(): string { + return $this->type; + } +} \ No newline at end of file diff --git a/src/Exceptions/UnsupportedDomain.php b/src/Exceptions/UnsupportedDomain.php new file mode 100644 index 0000000..c333b62 --- /dev/null +++ b/src/Exceptions/UnsupportedDomain.php @@ -0,0 +1,18 @@ + + * @version 1.0.0 + * @since 3.0.0 + */ +class UnsupportedDomain extends Exception +{ +} \ No newline at end of file diff --git a/src/Exceptions/WrongIpAddress.php b/src/Exceptions/WrongIpAddress.php new file mode 100644 index 0000000..bcc1b6b --- /dev/null +++ b/src/Exceptions/WrongIpAddress.php @@ -0,0 +1,19 @@ + + * @version 1.0.0 + * @since 3.0.0 + */ +class WrongIpAddress extends Exception +{ + +} \ No newline at end of file diff --git a/src/UnitPay.php b/src/UnitPay.php new file mode 100644 index 0000000..8855bbc --- /dev/null +++ b/src/UnitPay.php @@ -0,0 +1,350 @@ + + * @author Alexander Gorenkov + * @version 3.0.0 + * @since 1.0.0 + */ +class UnitPay +{ + #region Constants + + public const SUPPORTED_CURRENCIES = ['EUR', 'UAH', 'BYR', 'USD', 'RUB']; + public const SUPPORTED_PARTNER_METHODS = ['check', 'pay', 'error']; + + public const DOMAIN_RU = 'unitpay.ru'; + public const DOMAIN_MONEY = 'unitpay.money'; + public const ALLOWED_DOMAINS = [ + self::DOMAIN_RU, self::DOMAIN_MONEY, + ]; + + public const UNITPAY_IPS = [ + '31.186.100.49', + '178.132.203.105', + '52.29.152.23', + '52.19.56.234', + '127.0.0.1' // for debug + ]; + + #endregion Constants + + #region Properties + + /** + * @var string + */ + private $publicKey; + + /** + * @var string|null + */ + private $secretKey; + + /** + * @var string|null + */ + private $secretPersonalKey; + + /** + * @var string + */ + private $apiUrl; + + /** + * @var string + */ + private $formUrl; + + /** + * @var mixed[] + */ + private $params = []; + + #endregion Properties + + /** + * UnitPay constructor. + * + * @param string $publicKey + * @param string|null $secretKey + * @param string|null $secretPersonalKey + * @param string $domain + * + * @throws UnsupportedDomain + */ + public function __construct(string $publicKey, ?string $secretKey = null, ?string $secretPersonalKey = null, string $domain = self::DOMAIN_MONEY) + { + if (!in_array($domain, self::ALLOWED_DOMAINS)) { + throw new UnsupportedDomain(sprintf("`%s` is unsupported domain. Allowed: %s", $domain, implode(', ', self::ALLOWED_DOMAINS))); + } + + $this->publicKey = $publicKey; + $this->secretKey = $secretKey; + $this->secretPersonalKey = $secretPersonalKey; + $this->apiUrl = sprintf("https://%s/api", $domain); + $this->formUrl = sprintf("https://%s/pay", $domain); + } + + public function getAPI(): API + { + return new API($this->apiUrl, $this->secretKey, $this->secretPersonalKey); + } + + /** + * @param array $params + * @param string|null $method + * + * @return string + * + * @since 1.0.0 + */ + public function getSignature(array $params, ?string $method = null): string + { + unset($params['sign'], $params['signature']); + + ksort($params); + + $params[] = $this->secretKey; + + if ($method) { + array_unshift($params, $method); + } + + return hash('sha256', implode('{up}', $params)); + } + + /** + * @param float $sum + * @param string|int $account + * @param string $desc + * @param string $currency + * @param string $locale + * + * @return string + * + * @since 1.0.0 + */ + public function form(float $sum, $account, string $desc, string $currency = 'RUB', string $locale = 'ru'): string + { + $vitalParams = [ + 'account' => $account, + 'currency' => $currency, + 'desc' => $desc, + 'sum' => $sum, + ]; + + $params = array_merge($this->params, $vitalParams); + + if (!empty($this->secretKey)) { + $params['signature'] = $this->getSignature($vitalParams); + } + + $params['locale'] = $locale; + + return sprintf("%s/%s?%s", $this->formUrl, $this->publicKey, http_build_query($params)); + } + + /** + * Check request on handler from UnitPay + * + * @return bool + * + * @throws InvalidArgumentException + * @throws WrongIpAddress + */ + public function handleRequest(): bool + { + if (!isset($_GET['method'])) { + throw new InvalidArgumentException('Empty method'); + } + + if (!isset($_GET['params']) || empty($_GET['params'])) { + throw new InvalidArgumentException('Empty params'); + } + + [$method, $params] = [$_GET['method'], $_GET['params']]; + + if (!in_array($method, self::SUPPORTED_PARTNER_METHODS, true)) { + throw new UnexpectedValueException('Method is not supported'); + } + + if (!isset($params['signature']) || $params['signature'] !== $this->getSignature($params, $method)) { + throw new InvalidArgumentException('Wrong signature'); + } + + /** + * IP address check + * + * @link http://help.unitpay.ru/article/67-ip-addresses + * @link http://help.unitpay.money/article/67-ip-addresses + */ + if (!in_array($this->getRealIP(), self::UNITPAY_IPS, true)) { + throw new WrongIpAddress('IP address Error'); + } + + return true; + } + + /** + * @return string + * + * @since 1.0.0 + * @since 3.0.0 Returns the real ip when using cloudflare/etc. + */ + public function getRealIP(): string + { + if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { + $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP']; + } + + $forward = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? null; + $ip = $_SERVER['REMOTE_ADDR']; + + if (filter_var($forward, FILTER_VALIDATE_IP)) { + $ip = $forward; + } + + return $ip; + } + + #region UnitPay response + + /** + * Response for UnitPay if handle success + * + * @param string $message + * + * @return string + */ + public function getSuccessHandlerResponse(string $message): string + { + return json_encode([ + "result" => [ + "message" => $message, + ], + ]); + } + + /** + * Response for UnitPay if handle error + * + * @param string $message + * + * @return string + */ + public function getErrorHandlerResponse(string $message): string + { + return json_encode([ + "error" => [ + "message" => $message, + ], + ]); + } + + #endregion UnitPay response + + #region Getters and setters + + /** + * Set list of paid goods + * + * @param CashItem[] $items + * + * @return UnitPay + */ + public function setCashItems(array $items): self + { + $this->params['cashItems'] = base64_encode( + json_encode( + array_map(static function ($item) { + /** @var CashItem $item */ + return [ + 'name' => $item->getName(), + 'count' => $item->getCount(), + 'price' => $item->getPrice(), + 'nds' => $item->getNds(), + 'type' => $item->getType(), + 'paymentMethod' => $item->getPaymentMethod(), + ]; + }, $items))); + + return $this; + } + + /** + * Set callback URL + * + * @param string $backUrl + * + * @return UnitPay + */ + public function setBackUrl(string $backUrl): self + { + $this->params['backUrl'] = $backUrl; + + return $this; + } + + public function setCustomerPhone(string $phone): self + { + $this->params['customerPhone'] = $phone; + + return $this; + } + + public function setCustomerEmail(string $email): self + { + $this->params['customerEmail'] = $email; + + return $this; + } + + public function setPublicKey(string $publicKey): void + { + $this->publicKey = $publicKey; + } + + public function getPublicKey(): string + { + return $this->publicKey; + } + + #endregion Getters and setters +} \ No newline at end of file diff --git a/tests/UnitPayAPITest.php b/tests/UnitPayAPITest.php new file mode 100644 index 0000000..d542448 --- /dev/null +++ b/tests/UnitPayAPITest.php @@ -0,0 +1,60 @@ + + * @version 1.0.0 + * @since 3.0.0 + */ +class UnitPayAPITest extends TestCase +{ + public function testApiUnsupportedMethod(): void + { + $sdk = new UnitPay($this->getPublicKey(), $this->getSecretKey()); + $this->expectException(UnexpectedValueException::class); + $sdk->getAPI()->request('unsupported'); + } + + public function testApiSuccess(): void + { + $sdk = new UnitPay($this->getPublicKey(), $this->getSecretKey()); + + $sdk->getAPI()->addSupportedMethod('getPartner'); + $response = $sdk->getAPI()->request('getPartner', [ + 'login' => $this->getPartnerLogin(), + 'secretKey' => $this->getPersonalKey(), + ]); + + self::assertIsObject($response->result); + self::assertIsString($response->result->email); + self::assertEquals( + $this->getPartnerLogin(), + $response->result->email + ); + } + + private function getPublicKey(): string + { + return $_ENV['PUBLIC_KEY'] ?? $_SERVER['PUBLIC_KEY'] ?? ""; + } + + private function getSecretKey(): string + { + return $_ENV['SECRET_KEY'] ?? $_SERVER['SECRET_KEY'] ?? ""; + } + + public function getPersonalKey(): string + { + return $_ENV['PERSONAL_KEY'] ?? $_SERVER['PERSONAL_KEY'] ?? ""; + } + + public function getPartnerLogin(): string + { + return $_ENV['PARTNER_LOGIN'] ?? $_SERVER['PARTNER_LOGIN'] ?? ""; + } +} \ No newline at end of file diff --git a/tests/UnitPayTest.php b/tests/UnitPayTest.php new file mode 100644 index 0000000..7845ece --- /dev/null +++ b/tests/UnitPayTest.php @@ -0,0 +1,200 @@ + + * @version 1.0.0 + * @since 3.0.0 + */ +class UnitPayTest extends TestCase +{ + private const PRIVATE_KEY = 'private'; + private const PUBLIC_KEY = 'public'; + + public function testSuccessCreateInstance(): void + { + new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY, UnitPay::DOMAIN_MONEY); + self::assertTrue(true); + } + + public function testFailCreateInstance(): void + { + $this->expectException(UnsupportedDomain::class); + new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY, null, "unsupported"); + } + + public function testSuccessGetSignatureEquals(): void + { + $sdk = new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY); + + $vitalParams = []; + + $signature = $sdk->getSignature($vitalParams); + $signature2 = hash('sha256', implode('{up}', [ + self::PRIVATE_KEY, + ])); + self::assertEquals($signature2, $signature); + + $signature = $sdk->getSignature($vitalParams, 'pay'); + $signature2 = hash('sha256', implode('{up}', [ + 'pay', + self::PRIVATE_KEY, + ])); + self::assertEquals($signature2, $signature); + } + + public function testSuccessFormEqualsWithSecret(): void + { + $sdk = new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY); + + $url = $sdk->form(100.0, "custom-id", "description"); + + $vitalParams = [ + 'account' => 'custom-id', + 'currency' => 'RUB', + 'desc' => 'description', + 'sum' => 100, + ]; + + $vitalParams['signature'] = $sdk->getSignature($vitalParams); + $vitalParams['locale'] = 'ru'; + + self::assertEquals( + sprintf("https://unitpay.money/pay/public?%s", http_build_query($vitalParams)), + $url + ); + } + + public function testSuccessFormEqualsWithoutSecret(): void + { + $account = 'custom-id'; + $desc = 'description'; + $sum = 100; + $vitalParams = [ + 'account' => $account, + 'currency' => 'RUB', + 'desc' => $desc, + 'sum' => $sum, + 'locale' => 'ru', + ]; + + $url = sprintf("https://unitpay.money/pay/public?".http_build_query($vitalParams)); + + self::assertEquals($url, (new UnitPay(self::PUBLIC_KEY, ""))->form($sum, $account, $desc)); + self::assertEquals($url, (new UnitPay(self::PUBLIC_KEY, "0"))->form($sum, $account, $desc)); + self::assertEquals($url, (new UnitPay(self::PUBLIC_KEY, null))->form($sum, $account, $desc)); + } + + public function testRealIPGetter(): void + { + $sdk = new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY); + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + + + self::assertEquals('127.0.0.1', $sdk->getRealIP()); + + $_SERVER['HTTP_X_FORWARDED_FOR'] = '127.0.0.2'; + self::assertEquals('127.0.0.2', $sdk->getRealIP()); + } + + public function testHandlerFailEmptyMethod(): void + { + $sdk = new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Empty method"); + /** @noinspection PhpUnhandledExceptionInspection */ + $sdk->handleRequest(); + } + + public function testHandlerFailEmptyParams(): void + { + $sdk = new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY); + + $_GET['method'] = 'pay'; + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Empty params"); + /** @noinspection PhpUnhandledExceptionInspection */ + $sdk->handleRequest(); + } + + public function testHandlerFailUnsupportedMethod(): void + { + $sdk = new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY); + + $_GET['method'] = 'unsupported'; + $_GET['params'] = [ + 'sum' => 100, + ]; + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage("Method is not supported"); + /** @noinspection PhpUnhandledExceptionInspection */ + $sdk->handleRequest(); + } + + public function testHandlerFailWrongSignature(): void + { + $sdk = new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY); + + $_GET['method'] = 'pay'; + $_GET['params'] = [ + 'sum' => 100, + ]; + + $_GET['params']['signature'] = "wrong"; +// $_GET['params']['signature'] = $sdk->getSignature($_GET['params'], $_GET['method']); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage("Wrong signature"); + /** @noinspection PhpUnhandledExceptionInspection */ + $sdk->handleRequest(); + } + + public function testHandlerFailWrongIpAddress(): void + { + $sdk = new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY); + + $_SERVER['REMOTE_ADDR'] = '127.0.0.2'; + + $_GET['method'] = 'pay'; + $_GET['params'] = [ + 'sum' => 100, + ]; + + $_GET['params']['signature'] = $sdk->getSignature($_GET['params'], $_GET['method']); + + $this->expectException(WrongIpAddress::class); + $this->expectExceptionMessage("IP address Error"); + $sdk->handleRequest(); + } + + public function testHandlerSuccess(): void + { + $sdk = new UnitPay(self::PUBLIC_KEY, self::PRIVATE_KEY); + + $_SERVER['HTTP_X_FORWARDED_FOR'] = '127.0.0.1'; + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + + $_GET['method'] = 'pay'; + $_GET['params'] = [ + 'sum' => 100, + ]; + + $_GET['params']['signature'] = $sdk->getSignature($_GET['params'], $_GET['method']); + + /** @noinspection PhpUnhandledExceptionInspection */ + self::assertTrue($sdk->handleRequest()); + } +} \ No newline at end of file From ac7c50125bb7941ea7a549b4880fb6587201b771 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 9 Sep 2020 21:28:33 +0400 Subject: [PATCH 2/5] Small example fix --- sample/initPaymentApi.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/initPaymentApi.php b/sample/initPaymentApi.php index 0ac8470..1dd0f2d 100644 --- a/sample/initPaymentApi.php +++ b/sample/initPaymentApi.php @@ -30,7 +30,7 @@ * @link http://help.unitpay.money/article/32-creating-payment-via-api * @link http://help.unitpay.money/article/36-codes-payment-systems */ -$response = $unitPay->api('initPayment', [ +$response = $unitPay->getAPI()->request('initPayment', [ 'account' => $orderId, 'desc' => $orderDesc, 'sum' => $orderSum, From c4f6dadcbf51a9dca02d36f5963f40a03f91480e Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 9 Sep 2020 21:30:53 +0400 Subject: [PATCH 3/5] Added all API methods and their parameters --- src/API/API.php | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/API/API.php b/src/API/API.php index efe5097..b714d84 100644 --- a/src/API/API.php +++ b/src/API/API.php @@ -37,14 +37,24 @@ class API private $personalSecret; private static $supportedMethod = [ - 'initPayment', 'getPayment', // payments - - 'getPartner', // cabinet + 'initPayment', 'getPayment', 'refundPayment', + 'listSubscriptions', 'getSubscription', 'closeSubscription', + 'massPayment', 'massPaymentStatus', 'getBinInfo', + 'getPartner', 'getCommissions', ]; private $requiredUnitpayMethodsParams = [ - 'initPayment' => ['desc', 'account', 'sum'], - 'getPayment' => ['paymentId'], + 'initPayment' => ['account', 'sum', 'desc', 'paymentType', 'projectId'], + 'getPayment' => ['paymentId'], + 'refundPayment' => ['paymentId'], + 'massPayment' => ['login', 'purse', 'transactionId', 'sum', 'paymentType'], + 'massPaymentStatus' => ['login', 'transactionId'], + 'getBinInfo' => ['login', 'bin'], + 'listSubscriptions ' => ['projectId'], + 'getSubscription' => ['subscriptionId'], + 'closeSubscription' => ['subscriptionId'], + 'getPartner' => ['login'], + 'getCommissions' => ['projectId', 'login'], ]; public function __construct(string $apiUrl, string $secretKey, ?string $personalSecret = null) From 5c3e2014cb04851e954070696ad439986061ab9c Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 9 Sep 2020 21:31:12 +0400 Subject: [PATCH 4/5] Using cURL instead of `file_get_contents` --- .phpunit.result.cache | 2 +- composer.json | 3 ++- src/API/API.php | 17 ++++++++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.phpunit.result.cache b/.phpunit.result.cache index d9dc36f..ada7ae3 100644 --- a/.phpunit.result.cache +++ b/.phpunit.result.cache @@ -1 +1 @@ -C:37:"PHPUnit\Runner\DefaultTestResultCache":1044:{a:2:{s:7:"defects";a:2:{s:23:"APITest::testApiSuccess";i:4;s:30:"UnitPayAPITest::testApiSuccess";i:4;}s:5:"times";a:18:{s:38:"UnitPayTest::testSuccessCreateInstance";d:0.037;s:35:"UnitPayTest::testFailCreateInstance";d:0.009;s:42:"UnitPayTest::testSuccessGetSignatureEquals";d:0.003;s:44:"UnitPayTest::testSuccessFormEqualsWithSecret";d:0;s:47:"UnitPayTest::testSuccessFormEqualsWithoutSecret";d:0;s:37:"UnitPayTest::testApiUnsupportedMethod";d:0.002;s:27:"UnitPayTest::testApiSuccess";d:0.87;s:29:"UnitPayTest::testRealIPGetter";d:0;s:39:"UnitPayTest::testHandlerFailEmptyMethod";d:0.003;s:39:"UnitPayTest::testHandlerFailEmptyParams";d:0;s:45:"UnitPayTest::testHandlerFailUnsupportedMethod";d:0;s:42:"UnitPayTest::testHandlerFailWrongSignature";d:0;s:42:"UnitPayTest::testHandlerFailWrongIpAddress";d:0.002;s:31:"UnitPayTest::testHandlerSuccess";d:0;s:33:"APITest::testApiUnsupportedMethod";d:0.044;s:23:"APITest::testApiSuccess";d:0.002;s:40:"UnitPayAPITest::testApiUnsupportedMethod";d:0.043;s:30:"UnitPayAPITest::testApiSuccess";d:0.883;}}} \ No newline at end of file +C:37:"PHPUnit\Runner\DefaultTestResultCache":1071:{a:2:{s:7:"defects";a:2:{s:23:"APITest::testApiSuccess";i:4;s:30:"UnitPayAPITest::testApiSuccess";i:4;}s:5:"times";a:18:{s:38:"UnitPayTest::testSuccessCreateInstance";d:0.039;s:35:"UnitPayTest::testFailCreateInstance";d:0.01;s:42:"UnitPayTest::testSuccessGetSignatureEquals";d:0.004;s:44:"UnitPayTest::testSuccessFormEqualsWithSecret";d:0.001;s:47:"UnitPayTest::testSuccessFormEqualsWithoutSecret";d:0.001;s:37:"UnitPayTest::testApiUnsupportedMethod";d:0.002;s:27:"UnitPayTest::testApiSuccess";d:0.87;s:29:"UnitPayTest::testRealIPGetter";d:0.001;s:39:"UnitPayTest::testHandlerFailEmptyMethod";d:0.004;s:39:"UnitPayTest::testHandlerFailEmptyParams";d:0.001;s:45:"UnitPayTest::testHandlerFailUnsupportedMethod";d:0.001;s:42:"UnitPayTest::testHandlerFailWrongSignature";d:0.001;s:42:"UnitPayTest::testHandlerFailWrongIpAddress";d:0.003;s:31:"UnitPayTest::testHandlerSuccess";d:0.001;s:33:"APITest::testApiUnsupportedMethod";d:0.044;s:23:"APITest::testApiSuccess";d:0.002;s:40:"UnitPayAPITest::testApiUnsupportedMethod";d:0.043;s:30:"UnitPayAPITest::testApiSuccess";d:1.371;}}} \ No newline at end of file diff --git a/composer.json b/composer.json index ac0f23b..4909642 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ ], "require":{ "php":">=7.1.0", - "ext-json": "*" + "ext-json": "*", + "ext-curl": "*" }, "autoload":{ "psr-4": { diff --git a/src/API/API.php b/src/API/API.php index b714d84..02b489c 100644 --- a/src/API/API.php +++ b/src/API/API.php @@ -127,7 +127,22 @@ public function request(string $method, $params = []) ], "", '&', PHP_QUERY_RFC3986) ); - $response = json_decode(file_get_contents($requestUrl), false); + $ch = curl_init(); + curl_setopt_array($ch, [ + CURLOPT_URL => $requestUrl, + CURLOPT_HEADER => false, + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_TIMEOUT => 30, + CURLOPT_SSL_VERIFYPEER => 30, + CURLOPT_RETURNTRANSFER => true, + ]); + + $response = curl_exec($ch); + + curl_close($ch); + + $response = json_decode($response, false); + if (!is_object($response)) { throw new InvalidArgumentException('Temporary server error. Please try again later.'); } From 93ff23465f437e130f8ef820650d044c5b329bb9 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 4 Jun 2025 13:49:31 +0400 Subject: [PATCH 5/5] added new ip address --- src/UnitPay.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/UnitPay.php b/src/UnitPay.php index 8855bbc..2c84214 100644 --- a/src/UnitPay.php +++ b/src/UnitPay.php @@ -56,6 +56,7 @@ class UnitPay ]; public const UNITPAY_IPS = [ + '51.250.20.9', '31.186.100.49', '178.132.203.105', '52.29.152.23',