diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..14dfe22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +vendor/ +/index.php +example/credentials.php \ No newline at end of file diff --git a/LICENSE b/LICENSE index 14717ba..62c0fef 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Travel Management Consulting +Copyright (c) 2017 Travel Management Consulting, roquie0@gmail.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index f7a4fa5..24ad80e 100644 --- a/README.md +++ b/README.md @@ -1 +1,154 @@ -# uniteller-php-sdk \ No newline at end of file +# Uniteller PHP SDK + +PHP (5.4+) SDK для интеграции с интернет-эквайрингом от Uniteller. + +Реализовано: +* оплата (метод `pay`) +* отмена (метод `unblock`) +* получение результатов +* callback (проверка сигнатуры) +* обработчик ошибок, кидает эксепшены даже на строку `ERROR: %s` в теле ответа на запрос +* единство статусов. + +Что осталось: +* прикрутить валидацию, используя декораторы +* добавить билдер для метода `results` +* метод `card` +* метод `recurrent` +* метод `confirm` + +## Установка + +Чтобы установить пакет, достаточно подключить его в проект, как зависимость: + +`composer require tmconsulting/uniteller-php-sdk` + +## Использование + +Примеры использования SDK лежат в папке `./examples`, а так-же `README.md` файл, +в котором написан способ установки. + +### Установка учетных данных + +```php +setShopId('you_shop_id'); +$uniteller->setLogin('you_login_number'); +$uniteller->setPassword('you_password'); +$uniteller->setBaseUri('https://wpay.uniteller.ru'); +``` + +### Переход к оплате + +Чтобы произвести оплату, достаточно вызвать метод `payment`, +он принимает первым аргументом либо построитель запроса, либо обычный массив параметров. + +```php +setOrderIdp(mt_rand(10000, 99999)) + ->setSubtotalP(10) + ->setCustomerIdp(mt_rand(10000, 99999)) + ->setUrlReturnOk('http://google.ru/?q=success') + ->setUrlReturnNo('http://google.ru/?q=failure'); + +$uniteller->payment($builder)->go(); +// $uniteller->payment($builder)->getUri(); + +``` + +или + +```php +setOrderIdp(mt_rand(10000, 99999)) + ->setSubtotalP(10) + ->setCustomerIdp(mt_rand(10000, 99999)) + ->setUrlReturnOk('http://google.ru/?q=success') + ->setUrlReturnNo('http://google.ru/?q=failure'); + +$uniteller->payment([ + 'Order_IDP' => mt_rand(10000, 99999), + // ... прочие параметры +])->go(); + +// Если переходить к оплате сразу нет необходимости, +// можно получить готовую ссылку для оплаты +// $uniteller->payment($builder)->getUri(); + +``` + +### Отмена платежа + +```php +setBillNumber('RRN Number, (12 digits)'); +$results = $uniteller->cancel($builder); +``` + +или + +```php +cancel([ + 'Billnumber' => 'RRN Number, (12 digits)', + // ... +]); + +var_dump($results); + +// разница статусов в Uniteller API учтена. +if ($results[0]->getStatus() === Status::CANCELLED) { + // платёж отменен +} + +// доп. методы Tmconsulting\Uniteller\Order\Order +``` + +### Получение результатов + +```php +results([ + 'ShopOrderNumber' => 'Order_IDP number' +]); + +var_dump($results); + +// $results[0]->getCardNumber(); +``` + +### Callback + +Приём данных от шлюза и проверка сигнатуры. + +```php +getSignature()->verify('signature_from_post_params', ['all_parameters_from_post'])) { + return 'invalid_signature'; +} + +``` + + +## Тесты + +Есть, но мало ;) Пока в процессе написания, буду рад любой помощи. + +## Лицензия + +MIT. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..11bd3aa --- /dev/null +++ b/composer.json @@ -0,0 +1,34 @@ +{ + "name": "tmconsulting/uniteller-php-sdk", + "description": "PHP Library for integration with Uniteller payment processing.", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "roquie", + "email": "roquie0@gmail.com" + } + ], + "require": { + "php": ">=5.5", + "php-http/httplug": "^1.1", + "php-http/guzzle6-adapter": "^1.1" + }, + "require-dev": { + "symfony/var-dumper": "^3.2", + "phpunit/phpunit": "^6.0" + }, + "autoload": { + "psr-4": { + "Tmconsulting\\Uniteller\\": "src" + }, + "files": [ + "src/helpers.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Tmconsulting\\Uniteller\\Tests\\": "tests" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..cd267e1 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1795 @@ +{ + "_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#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "1804ecb24afa1a30f55909af3713ca23", + "packages": [ + { + "name": "guzzlehttp/guzzle", + "version": "6.2.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/8d6c6cc55186db87b7dc5009827429ba4e9dc006", + "reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.0", + "psr/log": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.2-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2017-02-28T22:50:30+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "0d6c7ca039329247e4f0f8f8f6506810e8248855" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/0d6c7ca039329247e4f0f8f8f6506810e8248855", + "reference": "0d6c7ca039329247e4f0f8f8f6506810e8248855", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2017-02-27T10:51:17+00:00" + }, + { + "name": "php-http/guzzle6-adapter", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-http/guzzle6-adapter.git", + "reference": "a56941f9dc6110409cfcddc91546ee97039277ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/guzzle6-adapter/zipball/a56941f9dc6110409cfcddc91546ee97039277ab", + "reference": "a56941f9dc6110409cfcddc91546ee97039277ab", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0", + "php": ">=5.5.0", + "php-http/httplug": "^1.0" + }, + "provide": { + "php-http/async-client-implementation": "1.0", + "php-http/client-implementation": "1.0" + }, + "require-dev": { + "ext-curl": "*", + "php-http/adapter-integration-tests": "^0.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Adapter\\Guzzle6\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + }, + { + "name": "David de Boer", + "email": "david@ddeboer.nl" + } + ], + "description": "Guzzle 6 HTTP Adapter", + "homepage": "http://httplug.io", + "keywords": [ + "Guzzle", + "http" + ], + "time": "2016-05-10T06:13:32+00:00" + }, + { + "name": "php-http/httplug", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/httplug.git", + "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/httplug/zipball/1c6381726c18579c4ca2ef1ec1498fdae8bdf018", + "reference": "1c6381726c18579c4ca2ef1ec1498fdae8bdf018", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "php-http/promise": "^1.0", + "psr/http-message": "^1.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "^1.0", + "phpspec/phpspec": "^2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eric GELOEN", + "email": "geloen.eric@gmail.com" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + } + ], + "description": "HTTPlug, the HTTP client abstraction for PHP", + "homepage": "http://httplug.io", + "keywords": [ + "client", + "http" + ], + "time": "2016-08-31T08:30:17+00:00" + }, + { + "name": "php-http/promise", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-http/promise.git", + "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-http/promise/zipball/dc494cdc9d7160b9a09bd5573272195242ce7980", + "reference": "dc494cdc9d7160b9a09bd5573272195242ce7980", + "shasum": "" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "^1.0", + "phpspec/phpspec": "^2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Http\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com" + }, + { + "name": "Joel Wurtz", + "email": "joel.wurtz@gmail.com" + } + ], + "description": "Promise used for asynchronous HTTP requests", + "homepage": "http://httplug.io", + "keywords": [ + "promise" + ], + "time": "2016-01-26T13:27:02+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.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://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14T21:17:01+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5a5a9fc8025a08d8919be87d6884d5a92520cefe", + "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "doctrine/collections": "1.*", + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "homepage": "https://github.com/myclabs/DeepCopy", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2017-01-26T22:05:40+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.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": "2015-12-27T11:43:31+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.2.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2016-09-30T07:12:33+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.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" + } + ], + "time": "2016-11-25T06:54:22+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8 || ^5.6.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "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": "2017-03-02T20:05:34+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "531553c4795a1df54114342d68ca337d5d81c8a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/531553c4795a1df54114342d68ca337d5d81c8a0", + "reference": "531553c4795a1df54114342d68ca337d5d81c8a0", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.0", + "phpunit/php-file-iterator": "^1.3", + "phpunit/php-text-template": "^1.2", + "phpunit/php-token-stream": "^1.4.11 || ^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0", + "sebastian/environment": "^2.0", + "sebastian/version": "^2.0" + }, + "require-dev": { + "ext-xdebug": "^2.5", + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-xdebug": "^2.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.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" + ], + "time": "2017-03-01T09:14:18+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.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" + ], + "time": "2016-10-03T07:40:28+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "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" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.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": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2017-02-26T11:10:40+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.11", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2017-02-27T10:12:30+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "6.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "47ee3fa1bca5c50f1d25105201eb20df777bd7b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/47ee3fa1bca5c50f1d25105201eb20df777bd7b6", + "reference": "47ee3fa1bca5c50f1d25105201eb20df777bd7b6", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.3", + "php": "^7.0", + "phpspec/prophecy": "^1.6.2", + "phpunit/php-code-coverage": "^5.0", + "phpunit/php-file-iterator": "^1.4", + "phpunit/php-text-template": "^1.2", + "phpunit/php-timer": "^1.0.6", + "phpunit/phpunit-mock-objects": "^4.0", + "sebastian/comparator": "^1.2.4 || ^2.0", + "sebastian/diff": "^1.2", + "sebastian/environment": "^2.0", + "sebastian/exporter": "^2.0 || ^3.0", + "sebastian/global-state": "^1.1 || ^2.0", + "sebastian/object-enumerator": "^2.0 || ^3.0", + "sebastian/resource-operations": "^1.0", + "sebastian/version": "^2.0" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2", + "phpunit/dbunit": "<3.0" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-xdebug": "*", + "phpunit/php-invoker": "^1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.0.x-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": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2017-03-02T15:24:03+00:00" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "eabce450df194817a7d7e27e19013569a903a2bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/eabce450df194817a7d7e27e19013569a903a2bf", + "reference": "eabce450df194817a7d7e27e19013569a903a2bf", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^7.0", + "phpunit/php-text-template": "^1.2", + "sebastian/exporter": "^3.0" + }, + "conflict": { + "phpunit/phpunit": "<6.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2017-03-03T06:30:20+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-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/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "20f84f468cb67efee293246e6a09619b891f55f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/20f84f468cb67efee293246e6a09619b891f55f0", + "reference": "20f84f468cb67efee293246e6a09619b891f55f0", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/diff": "^1.2", + "sebastian/exporter": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.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": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2017-03-03T06:26:08+00:00" + }, + { + "name": "sebastian/diff", + "version": "1.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2015-12-08T07:14:41+00:00" + }, + { + "name": "sebastian/environment", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.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": "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" + ], + "time": "2016-11-26T07:53:53+00:00" + }, + { + "name": "sebastian/exporter", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "b82d077cb3459e393abcf4867ae8f7230dcb51f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/b82d077cb3459e393abcf4867ae8f7230dcb51f6", + "reference": "b82d077cb3459e393abcf4867ae8f7230dcb51f6", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2017-03-03T06:25:06+00:00" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "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" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12T03:26:01+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "de6e32f7192dfea2e4bedc892434f4830b5c5794" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/de6e32f7192dfea2e4bedc892434f4830b5c5794", + "reference": "de6e32f7192dfea2e4bedc892434f4830b5c5794", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-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/", + "time": "2017-03-03T06:21:01+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2017-03-03T06:23:57+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-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", + "time": "2015-07-28T20:34:47+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "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": "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", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14T01:06:16+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v3.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "cb50260b674ee1c2d4ab49f2395a42e0b4681e20" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cb50260b674ee1c2d4ab49f2395a42e0b4681e20", + "reference": "cb50260b674ee1c2d4ab49f2395a42e0b4681e20", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "twig/twig": "~1.20|~2.0" + }, + "suggest": { + "ext-symfony_debug": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony mechanism for exploring and dumping PHP variables", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "time": "2017-02-16T22:46:52+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "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": "2016-11-23T20:04:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.5" + }, + "platform-dev": [] +} diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..7c68d6e --- /dev/null +++ b/example/README.md @@ -0,0 +1,14 @@ +# Client Payment Sample + +Простейший пример использования Client PHP SDK. + +## Установка + +* `git clone ${THIS_REPO_URI}` +* `composer install` +* `cd ./examples` +* `mv ./credentials.example.php ./credentials.php` + +Теперь осталось прописать свои доступы в файле `./credentials.php` +и запустить веб-сервер: `php -S localhost:8080` + diff --git a/example/callback.php b/example/callback.php new file mode 100644 index 0000000..5edf50c --- /dev/null +++ b/example/callback.php @@ -0,0 +1,16 @@ +getSignature()->verify('signature_from_post_params', ['all_parameters_from_post'])) { + return 'invalid_signature'; +} + +// ok! diff --git a/example/cancel.php b/example/cancel.php new file mode 100644 index 0000000..d14139a --- /dev/null +++ b/example/cancel.php @@ -0,0 +1,25 @@ +setBillNumber('RRN Number, (12 digits)'); +$results = $uniteller->cancel($builder); + +// or ... +/* +$results = $uniteller->cancel([ + 'Billnumber' => 'RRN Number, (12 digits)', + // ... +]); +*/ + +var_dump($results); diff --git a/example/credentials.example.php b/example/credentials.example.php new file mode 100644 index 0000000..cf5cc8d --- /dev/null +++ b/example/credentials.example.php @@ -0,0 +1,14 @@ +setShopId('your_shop_id'); +$uniteller->setLogin(1234); +$uniteller->setPassword('your_password'); +$uniteller->setBaseUri('https://wpay.uniteller.ru'); diff --git a/example/credentials.php b/example/credentials.php new file mode 100644 index 0000000..31990cc --- /dev/null +++ b/example/credentials.php @@ -0,0 +1,14 @@ +setShopId('00009167'); +$uniteller->setLogin(3326); +$uniteller->setPassword('5sqoSpQsCfl0rUTzVQDd33RMWQRsm4SIjzA7IvQkFm90k52OnrH6eTuhogTqOUPRhkptGZhWATHsoiSW'); +$uniteller->setBaseUri('https://wpay.uniteller.ru'); diff --git a/example/index.php b/example/index.php new file mode 100644 index 0000000..ac10e00 --- /dev/null +++ b/example/index.php @@ -0,0 +1,46 @@ +setOrderIdp(mt_rand(10000, 99999)) + ->setSubtotalP(10) + ->setCustomerIdp(mt_rand(10000, 99999)) + ->setUrlReturnOk('http://google.ru/?q=success') + ->setUrlReturnNo('http://google.ru/?q=failure'); + + +$uri = $uniteller->payment($builder)->getUri(); + +echo <<< HTML +

Client Payment Sample

+
+

Оплатить

+ {$uri} +
+

Отмена

+ /cancel.php +
+
+

Результаты платежа

+ /results.php +
+HTML; + +// or... + +/* + + $uniteller->payment([ + 'Shop_IDP' => '', + // ... +]); + +*/ + diff --git a/example/results.php b/example/results.php new file mode 100644 index 0000000..1f92bfe --- /dev/null +++ b/example/results.php @@ -0,0 +1,17 @@ +results([ + 'ShopOrderNumber' => 'number' +]); + + +var_dump($results); diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..544d280 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,29 @@ + + + + + + tests + + + + + + integration + + + + + + ./src/ + + + \ No newline at end of file diff --git a/src/ArraybleInterface.php b/src/ArraybleInterface.php new file mode 100644 index 0000000..ee60546 --- /dev/null +++ b/src/ArraybleInterface.php @@ -0,0 +1,21 @@ +billNumber = $billNumber; + + return $this; + } + + /** + * @param string $currency + * @return $this + */ + public function setCurrency($currency) + { + $this->currency = $currency; + + return $this; + } + + /** + * @param int $rvrReason + * @return $this + */ + public function setRvrReason($rvrReason) + { + $this->rvrReason = $rvrReason; + + return $this; + } + + /** + * @param float $subtotalP + * @return $this + */ + public function setSubtotalP($subtotalP) + { + $this->subtotalP = $subtotalP; + + return $this; + } + + /** + * @param array $selectFields + * @return $this + */ + public function setSelectFields(array $selectFields) + { + $this->selectFields = $selectFields; + + return $this; + } + + /** + * @return int + */ + public function getBillNumber() + { + return $this->billNumber; + } + + /** + * @return string + */ + public function getCurrency() + { + return $this->currency; + } + + /** + * @return int + */ + public function getRvrReason() + { + return $this->rvrReason; + } + + /** + * @return float + */ + public function getSubtotalP() + { + return $this->subtotalP; + } + + /** + * @return array + */ + public function getSelectFields() + { + return $this->selectFields; + } + + public function toArray() + { + return [ + 'Billnumber' => $this->getBillNumber(), + 'Subtotal_P' => $this->getSubtotalP(), + 'Currency' => $this->getCurrency(), + 'RVRReason' => $this->getRvrReason(), + 'S_FIELDS' => $this->getSelectFields() + ]; + } +} diff --git a/src/Cancel/CancelRequest.php b/src/Cancel/CancelRequest.php new file mode 100644 index 0000000..8667bcc --- /dev/null +++ b/src/Cancel/CancelRequest.php @@ -0,0 +1,80 @@ +request('unblock', 'POST', http_build_query($parameters)); + $xml = new \SimpleXMLElement($response); + + $array = []; + foreach ($xml->orders->order as $item) { + $array[] = (new Order()) + ->setAddress(Uniteller\xml_get($item, 'address')) + ->setApprovalCode(Uniteller\xml_get($item, 'approvalcode')) + ->setBankName(Uniteller\xml_get($item, 'bankname')) + ->setBillNumber(Uniteller\xml_get($item, 'billnumber')) + ->setBookingcomId(Uniteller\xml_get($item, 'bookingcom_id')) + ->setBookingcomPincode(Uniteller\xml_get($item, 'bookingcom_pincode')) + ->setCardIdp(Uniteller\xml_get($item, 'card_idp')) + ->setCardHolder(Uniteller\xml_get($item, 'cardholder')) + ->setCardNumber(Uniteller\xml_get($item, 'cardnumber')) + ->setCardType(Uniteller\xml_get($item, 'cardtype')) + ->setComment(Uniteller\xml_get($item, 'comment')) + ->setCurrency(Uniteller\xml_get($item, 'currency')) + ->setCvc2((bool)Uniteller\xml_get($item, 'cvc2')) + ->setDate(Uniteller\xml_get($item, 'date')) + ->setEmail(Uniteller\xml_get($item, 'email')) + ->setEMoneyType(Uniteller\xml_get($item, 'emoneytype')) + ->setEOrderData(Uniteller\xml_get($item, 'eorderdata')) + ->setErrorCode(Uniteller\xml_get($item, 'error_code')) + ->setErrorComment(Uniteller\xml_get($item, 'error_comment')) + ->setFirstName(Uniteller\xml_get($item, 'firstname')) + ->setGdsPaymentPurposeId(Uniteller\xml_get($item, 'gds_payment_purpose_id')) + ->setIData(Uniteller\xml_get($item, 'idata')) + ->setIp(Uniteller\xml_get($item, 'ipaddress')) + ->setLastName(Uniteller\xml_get($item, 'lastname')) + ->setLoanId(Uniteller\xml_get($item, 'loan_id')) + ->setMessage(Uniteller\xml_get($item, 'message')) + ->setMiddleName(Uniteller\xml_get($item, 'middlename')) + ->setNeedConfirm((bool) Uniteller\xml_get($item, 'need_confirm')) + ->setOrderNumber(Uniteller\xml_get($item, 'ordernumber')) + ->setParentOrderNumber(Uniteller\xml_get($item, 'parent_order_number')) + ->setPaymentType(Uniteller\xml_get($item, 'paymenttype')) + ->setPhone(Uniteller\xml_get($item, 'phone')) + ->setPtCode(Uniteller\xml_get($item, 'pt_code')) + ->setRecommendation(Uniteller\xml_get($item, 'recommendation')) + ->setResponseCode(Uniteller\xml_get($item, 'response_code')) + ->setStatus(Uniteller\xml_get($item, 'status')) + ->setTotal(Uniteller\xml_get($item, 'total')) + ->setPacketDate(Uniteller\xml_get($item, 'packetdate')) + ; + } + + return $array; + } +} \ No newline at end of file diff --git a/src/Cancel/RVRReason.php b/src/Cancel/RVRReason.php new file mode 100644 index 0000000..0f72053 --- /dev/null +++ b/src/Cancel/RVRReason.php @@ -0,0 +1,34 @@ +registerPayment(new Payment()); + $this->registerCancelRequest(new CancelRequest()); + $this->registerResultsRequest(new ResultsRequest()); + $this->registerSignature(new Signature()); + } + + /** + * @param $uri + * @return $this + */ + public function setBaseUri($uri) + { + $this->options['base_uri'] = $uri; + + return $this; + } + + /** + * @param $value + * @return $this + */ + public function setLogin($value) + { + $this->options['login'] = $value; + + return $this; + } + + /** + * @param $value + * @return $this + */ + public function setPassword($value) + { + $this->options['password'] = $value; + + return $this; + } + + /** + * @param $value + * @return $this + */ + public function setShopId($value) + { + $this->options['shop_id'] = $value; + + return $this; + } + + /** + * @param array $options + * @return $this + */ + public function setOptions(array $options) + { + $this->options = array_merge($this->options, $options); + + return $this; + } + + /** + * @param HttpManagerInterface $httpManager + * @return $this + */ + public function setHttpManager(HttpManagerInterface $httpManager) + { + $this->httpManager = $httpManager; + + return $this; + } + + /** + * @param PaymentInterface $payment + * @return $this + */ + public function registerPayment(PaymentInterface $payment) + { + $this->payment = $payment; + + return $this; + } + + /** + * @param RequestInterface $cancel + * @return $this + */ + public function registerCancelRequest(RequestInterface $cancel) + { + $this->cancelRequest = $cancel; + + return $this; + } + + /** + * @param RequestInterface $request + * @return $this + */ + public function registerResultsRequest(RequestInterface $request) + { + $this->resultsRequest = $request; + + return $this; + } + + /** + * @param \Tmconsulting\Uniteller\Signature\SignatureInterface $signature + * @return $this + */ + public function registerSignature(SignatureInterface $signature) + { + $this->signature = $signature; + + return $this; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * @param $key + * @param null $default + * @return string|mixed + */ + public function getOption($key, $default = null) + { + if (array_key_exists($key, $this->options)) { + return $this->options[$key]; + } + + return $default; + } + + /** + * @return string + */ + public function getBaseUri() + { + return $this->getOption('base_uri'); + } + + /** + * @return string + */ + public function getLogin() + { + return $this->getOption('login'); + } + + /** + * @return string + */ + public function getShopId() + { + return $this->getOption('shop_id'); + } + + /** + * @return string + */ + public function getPassword() + { + return $this->getOption('password'); + } + + /** + * @return \Tmconsulting\Uniteller\Payment\PaymentInterface + */ + public function getPayment() + { + return $this->payment; + } + + /** + * @return \Tmconsulting\Uniteller\Request\RequestInterface + */ + public function getCancelRequest() + { + return $this->cancelRequest; + } + + /** + * @return \Tmconsulting\Uniteller\Request\RequestInterface + */ + public function getResultsRequest() + { + return $this->resultsRequest; + } + + /** + * @return \Tmconsulting\Uniteller\Signature\SignatureInterface + */ + public function getSignature() + { + return $this->signature; + } + + /** + * @return \Tmconsulting\Uniteller\Http\HttpManagerInterface + */ + public function getHttpManager() + { + return $this->httpManager; + } + + /** + * Получение платежной ссылки или сразу переход к оплате. + * + * @param array $parameters|\Tmconsulting\Client\Payment\PaymentBuilder $builder + * @return \Tmconsulting\Uniteller\Payment\UriInterface + */ + public function payment($parameters) + { + $array = $this->getParameters($parameters); + $array['Shop_IDP'] = $this->getShopId(); + $array['Password'] = $this->getPassword(); + + $array['Signature'] = $this->signature->create([ + 'Shop_IDP' => array_get($array, 'Shop_IDP'), + 'Order_IDP' => array_get($array, 'Order_IDP'), + 'Subtotal_P' => array_get($array, 'Subtotal_P'), + 'MeanType' => array_get($array, 'MeanType'), + 'EMoneyType' => array_get($array, 'EMoneyType'), + 'Lifetime' => array_get($array, 'Lifetime'), + 'Customer_IDP' => array_get($array, 'Customer_IDP'), + 'Card_IDP' => array_get($array, 'Card_IDP'), + 'IData' => array_get($array, 'IData'), + 'PT_Code' => array_get($array, 'PT_Code'), + 'Password' => array_get($array, 'Password'), + ]); + + return $this->getPayment()->execute($array, $this->getOptions()); + } + + /** + * Отмена платежа. + * + * @param \Tmconsulting\Uniteller\Cancel\CancelBuilder|array $parameters + * @return mixed + * @internal param $builder + */ + public function cancel($parameters) + { + return $this->callRequestFor('cancel', $parameters); + } + + /** + * @param \Tmconsulting\Uniteller\Cancel\CancelBuilder|array $parameters + * @return Order + */ + public function results($parameters) + { + return $this->callRequestFor('results', $parameters); + } + + /** + * @param array $parameters + * @return mixed + * @throws \Tmconsulting\Uniteller\Exception\NotImplementedException + */ + public function reccurent($parameters) + { + throw new NotImplementedException(sprintf( + 'In current moment, feature [%s] not implemented.', __METHOD__ + )); + } + + /** + * @param array $parameters + * @return mixed + * @throws \Tmconsulting\Uniteller\Exception\NotImplementedException + */ + public function confirm($parameters) + { + throw new NotImplementedException(sprintf( + 'In current moment, feature [%s] not implemented.', __METHOD__ + )); + } + + /** + * @param array $parameters + * @return mixed + * @throws \Tmconsulting\Uniteller\Exception\NotImplementedException + */ + public function card($parameters) + { + throw new NotImplementedException(sprintf( + 'In current moment, feature [%s] not implemented.', __METHOD__ + )); + } + + /** + * Подгружаем собственный HttpManager с газлом в качестве клиента, если + * не был задан свой, перед выполнением запроса. + * + * @param $name + * @param $parameters + * @return \Tmconsulting\Uniteller\Request\RequestInterface + */ + private function callRequestFor($name, $parameters) + { + if (! $this->getHttpManager()) { + $httpClient = new GuzzleAdapter(new GuzzleClient()); + $this->setHttpManager(new HttpManager($httpClient, $this->getOptions())); + } + + /** @var RequestInterface $request */ + $request = $this->{'get' . ucfirst($name) . 'Request'}(); + + return $request->execute( + $this->getHttpManager(), + $this->getParameters($parameters) + ); + } + + /** + * @param $parameters + * @return mixed + */ + private function getParameters($parameters) + { + if ($parameters instanceof ArraybleInterface) { + return $parameters->toArray(); + } + + return $parameters; + } +} \ No newline at end of file diff --git a/src/ClientInterface.php b/src/ClientInterface.php new file mode 100644 index 0000000..487424f --- /dev/null +++ b/src/ClientInterface.php @@ -0,0 +1,55 @@ + 'АВТОРИЗАЦИЯ УСПЕШНО ЗАВЕРШЕНА', + 'AS100' => 'ОТКАЗ В АВТОРИЗАЦИИ', + 'AS101' => 'ОТКАЗ В АВТОРИЗАЦИИ. Ошибочный номер карты.', + 'AS102' => 'ОТКАЗ В АВТОРИЗАЦИИ. Недостаточно средств.', + 'AS104' => 'ОТКАЗ В АВТОРИЗАЦИИ. Неверный срок действия карты.', + 'AS105' => 'ОТКАЗ В АВТОРИЗАЦИИ. Превышен лимит.', + 'AS107' => 'ОТКАЗ В АВТОРИЗАЦИИ. Ошибка приёма данных.', + 'AS108' => 'ОТКАЗ В АВТОРИЗАЦИИ. Подозрение на мошенничество.', + 'AS109' => 'ОТКАЗ В АВТОРИЗАЦИИ. Превышен лимит операций Client.', + 'AS200' => 'ПОВТОРИТЕ АВТОРИЗАЦИЮ', + 'AS998' => 'ОШИБКА СИСТЕМЫ. Свяжитесь с Client.', + ]; + + /** + * Преобразование кода ошибки в сообщение. + * + * @param $code + * @return string|null + */ + public static function message($code) + { + if (array_key_exists($code, self::$messages)) { + return self::$messages[$code]; + } + + return null; + } +} \ No newline at end of file diff --git a/src/Exception/AuthConfirmIsNotAllowedException.php b/src/Exception/AuthConfirmIsNotAllowedException.php new file mode 100644 index 0000000..cb22ad2 --- /dev/null +++ b/src/Exception/AuthConfirmIsNotAllowedException.php @@ -0,0 +1,32 @@ +errorCode = Error::AUTH_CONFIRM_IS_NOT_ALLOWED; + } +} \ No newline at end of file diff --git a/src/Exception/AuthenticationException.php b/src/Exception/AuthenticationException.php new file mode 100644 index 0000000..c712cdd --- /dev/null +++ b/src/Exception/AuthenticationException.php @@ -0,0 +1,32 @@ +errorCode = Error::AUTHENTICATION; + } +} \ No newline at end of file diff --git a/src/Exception/BadFieldFormatException.php b/src/Exception/BadFieldFormatException.php new file mode 100644 index 0000000..de53e3c --- /dev/null +++ b/src/Exception/BadFieldFormatException.php @@ -0,0 +1,32 @@ +errorCode = Error::BAD_FIELD_FORMAT; + } +} \ No newline at end of file diff --git a/src/Exception/BillNotFoundException.php b/src/Exception/BillNotFoundException.php new file mode 100644 index 0000000..fa3fffa --- /dev/null +++ b/src/Exception/BillNotFoundException.php @@ -0,0 +1,37 @@ +errorCode = Error::BILL_NOT_FOUND; + } +} \ No newline at end of file diff --git a/src/Exception/ErrorException.php b/src/Exception/ErrorException.php new file mode 100644 index 0000000..257581c --- /dev/null +++ b/src/Exception/ErrorException.php @@ -0,0 +1,29 @@ +errorCode; + } +} \ No newline at end of file diff --git a/src/Exception/ExceptionFactory.php b/src/Exception/ExceptionFactory.php new file mode 100644 index 0000000..019a18c --- /dev/null +++ b/src/Exception/ExceptionFactory.php @@ -0,0 +1,96 @@ + 'Authorization(\s)+confirm(\s)+is(\s)+not(\s)+allowed', + Error::AUTHENTICATION => 'Authentication(\s)+error', + Error::BAD_FIELD_FORMAT => 'Field(.*)has(\s)+bad(\s)+format', + Error::MANDATORY_PARAMETER => 'Mandatory(\s)+parameter(\s)+\\\'(.*)\\\'(.*)', + Error::NOT_SUPPORTED_SFIELD => 'S_FIELDS(\s)+contains(\s)+field(\s)+\\\'(.*)\\\'(.*)', + Error::OPERATION_FAILED => 'The(\s)+operation(\s)+failed', + ]; + + /** + * В /results запросе кода ошибки нет, есть только его сообщение. + * С помощью регулярок (а как еще!?) вызовем нужный эксепшен на помощь разработчикам. + * + * @param $message + * @param \Psr\Http\Message\RequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + * @return \ErrorException + */ + public static function createFromMessage($message, RequestInterface $request, ResponseInterface $response) + { + foreach (self::$messageToErrorCode as $code => $regex) { + preg_match('/' . $regex . '/', $message, $matches); + if (count($matches) > 0) { + return self::create($code, $message, $request, $response); + } + } + + return self::create(null, $message, $request, $response); + } + + /** + * Конвертируем код ошибки в эксепшен. + * + * @param $code + * @param $message + * @param \Psr\Http\Message\RequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + * @return \ErrorException + */ + public static function create($code, $message, RequestInterface $request, ResponseInterface $response) + { + switch ($code) { + case Error::AUTH_CONFIRM_IS_NOT_ALLOWED: + return new AuthConfirmIsNotAllowedException( + $message, $request, $response + ); + case Error::AUTHENTICATION: + return new AuthenticationException( + $message, $request, $response + ); + case Error::BAD_FIELD_FORMAT: + return new BadFieldFormatException( + $message, $request, $response + ); + case Error::MANDATORY_PARAMETER: + return new MandatoryParameterException( + $message, $request, $response + ); + case Error::NOT_SUPPORTED_SFIELD: + return new NotSupportedSFieldException( + $message, $request, $response + ); + case Error::OPERATION_FAILED: + return new OperationFailedException( + $message, $request, $response + ); + } + + return new ErrorException($message, $request, $response); + } +} \ No newline at end of file diff --git a/src/Exception/FormatNotSupportedException.php b/src/Exception/FormatNotSupportedException.php new file mode 100644 index 0000000..5f4dfff --- /dev/null +++ b/src/Exception/FormatNotSupportedException.php @@ -0,0 +1,14 @@ +errorCode = Error::MANDATORY_PARAMETER; + } +} \ No newline at end of file diff --git a/src/Exception/NotImplementedException.php b/src/Exception/NotImplementedException.php new file mode 100644 index 0000000..bc67f11 --- /dev/null +++ b/src/Exception/NotImplementedException.php @@ -0,0 +1,18 @@ +errorCode = Error::NOT_SUPPORTED_SFIELD; + } +} \ No newline at end of file diff --git a/src/Exception/OperationFailedException.php b/src/Exception/OperationFailedException.php new file mode 100644 index 0000000..45aba72 --- /dev/null +++ b/src/Exception/OperationFailedException.php @@ -0,0 +1,33 @@ +firstCode = Error::OPERATION_FAILED; + } +} \ No newline at end of file diff --git a/src/Exception/RequestNotSupportedException.php b/src/Exception/RequestNotSupportedException.php new file mode 100644 index 0000000..0e9eba5 --- /dev/null +++ b/src/Exception/RequestNotSupportedException.php @@ -0,0 +1,14 @@ + ['csv' => 1, 'wddx' => 2, 'xml' => 3], + RequestInterface::REQUEST_CONFIRM => ['csv' => 1, 'wddx' => 2, 'xml' => 3], + RequestInterface::REQUEST_RECURRENT => ['csv' => 1], + RequestInterface::REQUEST_CANCEL => ['csv' => 1, 'wddx' => 2, 'xml' => 3, 'soap' => 4], + RequestInterface::REQUEST_RESULTS => ['csv' => 1, 'wddx' => 2, 'brackets' => 3, 'xml' => 4, 'soap' => 5] + ]; + + /** + * Преобразование от имени запроса к одному формату. + * + * @param $format + * @param $requestName + * @return int + * @throws \Tmconsulting\Uniteller\Exception\RequestNotSupportedException + */ + public static function resolve($format, $requestName) + { + if (! array_key_exists($requestName, self::$variants)) { + throw new RequestNotSupportedException(sprintf( + 'Request [%s] not supported here.', $requestName + )); + } + + if (! array_key_exists($format, self::$variants[$requestName])) { + throw new FormatNotSupportedException(sprintf( + 'Format [%s] not supported for request [%s].', $format, $requestName + )); + } + + return self::$variants[$requestName][$format]; + } + + /** + * Вариант Format::resolveXml(...), для ленивых, как я. + * + * @param $name + * @param array $arguments + * @return int + */ + public static function __callStatic($name, array $arguments) + { + if (false === strpos($name, 'resolve')) { + throw new BadMethodCallException(sprintf( + 'Method [%s] not found.', $name + )); + } + + return self::resolve( + strtolower(substr($name, 7)), + Uniteller\array_get($arguments, 0) + ); + } +} \ No newline at end of file diff --git a/src/Http/HttpManager.php b/src/Http/HttpManager.php new file mode 100644 index 0000000..8e3c5ca --- /dev/null +++ b/src/Http/HttpManager.php @@ -0,0 +1,121 @@ +httpClient = $httpClient; + $this->options = $options; + } + + /** + * @param $uri + * @param string $method + * @param null $data + * @param array $headers + * @return \Psr\Http\Message\ResponseInterface + */ + public function request($uri, $method = 'POST', $data = null, array $headers = []) + { + $uri = sprintf('%s/%s?%s', $this->options['base_uri'], $uri, http_build_query([ + 'Shop_ID' => $this->options['shop_id'], + 'Login' => $this->options['login'], + 'Password' => $this->options['password'], + 'Format' => Format::resolveXml($uri), + ])); + + $defaultHeaders = [ + 'Accept' => 'application/xml', + 'Content-Type' => 'application/x-www-form-urlencoded', + ]; + + $request = new Request($method, $uri, array_merge($defaultHeaders, $headers), $data); + + try { + $response = $this->httpClient->sendRequest($request); + } catch (RequestException $e) { + throw UnitellerException::create($request, $e->getResponse()); + } + + $this->basicError($request, $response); + $body = $response->getBody()->getContents(); + $this->providerError($body, $request, $response); + + return $body; + } + + /** + * @param $body + * @param $request + * @param $response + * @throws \ErrorException + */ + protected function providerError($body, RequestInterface $request, ResponseInterface $response) + { + if (substr($body, 0, 6) === 'ERROR:') { + throw ExceptionFactory::createFromMessage( + substr($body, 7), $request, $response + ); + } + + $xml = new \SimpleXMLElement((string) $body); + if (($firstCode = (string) $xml->attributes()['firstcode']) == 0) { + return; + } + + $secondCode = (string) $xml->attributes()['secondcode']; + + throw ExceptionFactory::create( + $firstCode, $secondCode, $request, $response + ); + } + + /** + * @param \Psr\Http\Message\RequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + */ + protected function basicError(RequestInterface $request, ResponseInterface $response) + { + if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300) { + return; + } + + throw UnitellerException::create($request, $response); + } +} \ No newline at end of file diff --git a/src/Http/HttpManagerInterface.php b/src/Http/HttpManagerInterface.php new file mode 100644 index 0000000..97d927d --- /dev/null +++ b/src/Http/HttpManagerInterface.php @@ -0,0 +1,20 @@ +address = $address; + + return $this; + } + + /** + * @param string $approvalCode + * @return $this + */ + public function setApprovalCode($approvalCode) + { + $this->approvalCode = $approvalCode; + + return $this; + } + + /** + * @param string $bankName + * @return $this + */ + public function setBankName($bankName) + { + $this->bankName = $bankName; + + return $this; + } + + /** + * @param int $billNumber + * @return $this + */ + public function setBillNumber($billNumber) + { + $this->billNumber = $billNumber; + + return $this; + } + + /** + * @param string $bookingcomId + * @return $this + */ + public function setBookingcomId($bookingcomId) + { + $this->bookingcomId = $bookingcomId; + + return $this; + } + + /** + * @param string $bookingcomPincode + * @return $this + */ + public function setBookingcomPincode($bookingcomPincode) + { + $this->bookingcomPincode = $bookingcomPincode; + + return $this; + } + + /** + * @param string $cardIdp + * @return $this + */ + public function setCardIdp($cardIdp) + { + $this->cardIdp = $cardIdp; + + return $this; + } + + /** + * @param string $cardHolder + * @return $this + */ + public function setCardHolder($cardHolder) + { + $this->cardHolder = $cardHolder; + + return $this; + } + + /** + * @param string $cardNumber + * @return $this + */ + public function setCardNumber($cardNumber) + { + $this->cardNumber = $cardNumber; + + return $this; + } + + /** + * @param string $cardType + * @return $this + */ + public function setCardType($cardType) + { + $this->cardType = $cardType; + + return $this; + } + + /** + * @param string $comment + * @return $this + */ + public function setComment($comment) + { + $this->comment = $comment; + + return $this; + } + + /** + * @param string $currency + * @return $this + */ + public function setCurrency($currency) + { + $this->currency = $currency; + + return $this; + } + + /** + * @return $this + */ + public function setCvc2($value) + { + $this->cvc2 = (bool) $value; + + return $this; + } + + /** + * @return $this + */ + public function withCvc2() + { + $this->cvc2 = true; + + return $this; + } + + /** + * @return $this + */ + public function withoutCvc2() + { + $this->cvc2 = false; + + return $this; + } + + /** + * @param DateTime $date + * @return $this + */ + public function setDate($date) + { + if (empty($date)) { + return $this; + } + + // Ёбаный насрать, а даты зачем разного формата отдавать? + $this->date = DateTime::createFromFormat('U', strtotime($date)); + + return $this; + } + + /** + * @param string $email + * @return $this + */ + public function setEmail($email) + { + $this->email = $email; + + return $this; + } + + /** + * @param string $eMoneyType + * @return $this + */ + public function setEMoneyType($eMoneyType) + { + $this->eMoneyType = $eMoneyType; + + return $this; + } + + /** + * @param array $eOrderData + * @return $this + */ + public function setEOrderData($eOrderData) + { + if (empty($eOrderData)) { + return $this; + } + + foreach (explode(', ', $eOrderData) as $item) { + foreach (explode('=', $item) as list($key, $value)) { + $this->eOrderData[$key] = $value; + } + } + + return $this; + } + + /** + * @param int $errorCode + * @return $this + */ + public function setErrorCode($errorCode) + { + $this->errorCode = $errorCode; + + return $this; + } + + /** + * @param string $errorComment + * @return $this + */ + public function setErrorComment($errorComment) + { + $this->errorComment = $errorComment; + + return $this; + } + + /** + * @param string $firstName + * @return $this + */ + public function setFirstName($firstName) + { + $this->firstName = $firstName; + + return $this; + } + + /** + * @param int $gdsPaymentPurposeId + * @return $this + */ + public function setGdsPaymentPurposeId($gdsPaymentPurposeId) + { + $this->gdsPaymentPurposeId = $gdsPaymentPurposeId; + + return $this; + } + + /** + * @param string $iData + * @return $this + */ + public function setIData($iData) + { + $this->iData = $iData; + + return $this; + } + + /** + * @param string $ip + * @return $this + */ + public function setIp($ip) + { + $this->ip = $ip; + + return $this; + } + + /** + * @param string $lastName + * @return $this + */ + public function setLastName($lastName) + { + $this->lastName = $lastName; + + return $this; + } + + /** + * @param string $loanId + * @return $this + */ + public function setLoanId($loanId) + { + $this->loanId = $loanId; + + return $this; + } + + /** + * @param string $message + * @return $this + */ + public function setMessage($message) + { + $this->message = $message; + + return $this; + } + + /** + * @param string $middleName + * @return $this + */ + public function setMiddleName($middleName) + { + $this->middleName = $middleName; + + return $this; + } + + /** + * @param bool $needConfirm + * @return $this + */ + public function setNeedConfirm($needConfirm) + { + $this->needConfirm = (bool) $needConfirm; + + return $this; + } + + /** + * @param string $orderNumber + * @return $this + */ + public function setOrderNumber($orderNumber) + { + $this->orderNumber = $orderNumber; + + return $this; + } + + /** + * @param string $parentOrderNumber + * @return $this + */ + public function setParentOrderNumber($parentOrderNumber) + { + $this->parentOrderNumber = $parentOrderNumber; + + return $this; + } + + /** + * @param int $paymentType + * @return $this + */ + public function setPaymentType($paymentType) + { + $this->paymentType = $paymentType; + + return $this; + } + + /** + * @param string $phone + * @return $this + */ + public function setPhone($phone) + { + $this->phone = $phone; + + return $this; + } + + /** + * @param string $ptCode + * @return $this + */ + public function setPtCode($ptCode) + { + $this->ptCode = $ptCode; + + return $this; + } + + /** + * @param string $recommendation + * @return $this + */ + public function setRecommendation($recommendation) + { + $this->recommendation = $recommendation; + + return $this; + } + + /** + * @param string $responseCode + * @return $this + */ + public function setResponseCode($responseCode) + { + $this->responseCode = $responseCode; + + return $this; + } + + /** + * @param string $status + * @return $this + */ + public function setStatus($status) + { + $this->status = Status::resolve($status); + + return $this; + } + + /** + * @param string $total + * @return $this + */ + public function setTotal($total) + { + $this->total = $total; + + return $this; + } + + /** + * @return string + */ + public function getAddress() + { + return $this->address; + } + + /** + * @return string + */ + public function getApprovalCode() + { + return $this->approvalCode; + } + + /** + * @return string + */ + public function getBankName() + { + return $this->bankName; + } + + /** + * @return int + */ + public function getBillNumber() + { + return $this->billNumber; + } + + /** + * @return string + */ + public function getBookingcomId() + { + return $this->bookingcomId; + } + + /** + * @return string + */ + public function getBookingcomPincode() + { + return $this->bookingcomPincode; + } + + /** + * @return string + */ + public function getCardIdp() + { + return $this->cardIdp; + } + + /** + * @return string + */ + public function getCardHolder() + { + return $this->cardHolder; + } + + /** + * @return string + */ + public function getCardNumber() + { + return $this->cardNumber; + } + + /** + * @return string + */ + public function getCardType() + { + return $this->cardType; + } + + /** + * @return string + */ + public function getComment() + { + return $this->comment; + } + + /** + * @return string + */ + public function getCurrency() + { + return $this->currency; + } + + /** + * @return int + */ + public function isCvc2() + { + return $this->cvc2; + } + + /** + * @return DateTime + */ + public function getDate() + { + return $this->date; + } + + /** + * @return string + */ + public function getEmail() + { + return $this->email; + } + + /** + * @return string + */ + public function getEMoneyType() + { + return $this->eMoneyType; + } + + /** + * @return array + */ + public function getEOrderData() + { + return $this->eOrderData; + } + + /** + * @return int + */ + public function getErrorCode() + { + return $this->errorCode; + } + + /** + * @return string + */ + public function getErrorComment() + { + return $this->errorComment; + } + + /** + * @return string + */ + public function getFirstName() + { + return $this->firstName; + } + + /** + * @return int + */ + public function getGdsPaymentPurposeId() + { + return $this->gdsPaymentPurposeId; + } + + /** + * @return string + */ + public function getIData() + { + return $this->iData; + } + + /** + * @return string + */ + public function getIp() + { + return $this->ip; + } + + /** + * @return string + */ + public function getLastName() + { + return $this->lastName; + } + + /** + * @return string + */ + public function getLoanId() + { + return $this->loanId; + } + + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * @return string + */ + public function getMiddleName() + { + return $this->middleName; + } + + /** + * @return bool + */ + public function isNeedConfirm() + { + return $this->needConfirm; + } + + /** + * @return string + */ + public function getOrderNumber() + { + return $this->orderNumber; + } + + /** + * @return string + */ + public function getParentOrderNumber() + { + return $this->parentOrderNumber; + } + + /** + * @return int + */ + public function getPaymentType() + { + return $this->paymentType; + } + + /** + * @return string + */ + public function getPhone() + { + return $this->phone; + } + + /** + * @return string + */ + public function getPtCode() + { + return $this->ptCode; + } + + /** + * @return string + */ + public function getRecommendation() + { + return $this->recommendation; + } + + /** + * @return string + */ + public function getResponseCode() + { + return $this->responseCode; + } + + /** + * @return string + */ + public function getResponseMessage() + { + return ResponseCode::message($this->responseCode); + } + + /** + * @return string + */ + public function getStatus() + { + return $this->status; + } + + /** + * @return string + */ + public function getTotal() + { + return $this->total; + } + + /** + * @param DateTime $packetDate + * @return $this + */ + public function setPacketDate($packetDate) + { + if (empty($packetDate)) { + return $this; + } + + // 10.03.2017 15:42:42 - o_O + $this->packetDate = DateTime::createFromFormat('U', strtotime($packetDate)); + + return $this; + } + + /** + * @return DateTime + */ + public function getPacketDate() + { + return $this->packetDate; + } + + /** + * @return array + */ + public function toArray() + { + return [ + 'Address' => $this->getAddress(), + 'ApprovalCode' => $this->getApprovalCode(), + 'BankName' => $this->getBankName(), + 'BillNumber' => $this->getBillNumber(), + 'bookingcom_id' => $this->getBookingcomId(), + 'bookingcom_pincode' => $this->getBookingcomPincode(), + 'Card_IDP' => $this->getCardIdp(), + 'CardHolder' => $this->getCardHolder(), + 'CardNumber' => $this->getCardNumber(), + 'CardType' => $this->getCardType(), + 'Comment' => $this->getComment(), + 'Currency' => $this->getCurrency(), + 'CVC2' => $this->isCvc2(), + 'Date' => $this->getDate(), + 'PacketDate' => $this->getPacketDate(), + 'Email' => $this->getEmail(), + 'EMoneyType' => $this->getEMoneyType(), + 'EOrderData' => $this->getEOrderData(), + 'Error_Code' => $this->getErrorCode(), + 'Error_Comment' => $this->getErrorComment(), + 'FirstName' => $this->getFirstName(), + 'gds_payment_purpose_id' => $this->getGdsPaymentPurposeId(), + 'IData' => $this->getIData(), + 'IPAddress' => $this->getIp(), + 'LastName' => $this->getLastName(), + 'LoanID' => $this->getLoanId(), + 'Message' => $this->getMessage(), + 'MiddleName' => $this->getMiddleName(), + 'need_confirm' => $this->isNeedConfirm(), + 'OrderNumber' => $this->getOrderNumber(), + 'parent_order_number' => $this->getParentOrderNumber(), + 'PaymentType' => $this->getPaymentType(), + 'Phone' => $this->getPhone(), + 'PT_Code' => $this->getPtCode(), + 'Recommendation' => $this->getRecommendation(), + 'Response_Code' => $this->getResponseCode(), + 'Response_Message' => $this->getResponseMessage(), + 'Status' => $this->getStatus(), + 'Total' => $this->getTotal(), + ]; + } +} \ No newline at end of file diff --git a/src/Order/Status.php b/src/Order/Status.php new file mode 100644 index 0000000..50b1614 --- /dev/null +++ b/src/Order/Status.php @@ -0,0 +1,87 @@ +shopIdp = $shopIdp; + + return $this; + } + + /** + * @param string $orderIdp + * @return $this + */ + public function setOrderIdp($orderIdp) + { + $this->orderIdp = $orderIdp; + + return $this; + } + + /** + * @param float|string $subtotalP + * @return $this + */ + public function setSubtotalP($subtotalP) + { + $this->subtotalP = $subtotalP; + + return $this; + } + + /** + * @param string $signature + * @return $this + */ + public function setSignature($signature) + { + $this->signature = $signature; + + return $this; + } + + /** + * @param string $urlReturnOk + * @return $this + */ + public function setUrlReturnOk($urlReturnOk) + { + $this->urlReturnOk = $urlReturnOk; + + return $this; + } + + /** + * @param string $urlReturnNo + * @return $this + */ + public function setUrlReturnNo($urlReturnNo) + { + $this->urlReturnNo = $urlReturnNo; + + return $this; + } + + /** + * @param string $currency + * @return $this + */ + public function setCurrency($currency) + { + $this->currency = $currency; + + return $this; + } + + /** + * @param string $email + * @return $this + */ + public function setEmail($email) + { + $this->email = $email; + + return $this; + } + + /** + * @param int $lifetime + * @return $this + */ + public function setLifetime($lifetime) + { + $this->lifetime = $lifetime; + + return $this; + } + + /** + * @param int $orderLifetime + * @return $this + */ + public function setOrderLifetime($orderLifetime) + { + $this->orderLifetime = $orderLifetime; + + return $this; + } + + /** + * @param string $customerIdp + * @return $this + */ + public function setCustomerIdp($customerIdp) + { + $this->customerIdp = $customerIdp; + + return $this; + } + + /** + * @param mixed $cardIdp + * @return $this + */ + public function setCardIdp($cardIdp) + { + $this->cardIdp = $cardIdp; + + return $this; + } + + /** + * @param string $iData + * @return $this + */ + public function setIData($iData) + { + $this->iData = $iData; + + return $this; + } + + /** + * @param string $ptCode + * @return $this + */ + public function setPtCode($ptCode) + { + $this->ptCode = $ptCode; + + return $this; + } + + /** + * @param int $meanType + * @return $this + */ + public function setMeanType($meanType) + { + $this->meanType = $meanType; + + return $this; + } + + /** + * @param int $eMoneyType + * @return $this + */ + public function setEMoneyType($eMoneyType) + { + $this->eMoneyType = $eMoneyType; + + return $this; + } + + /** + * @param int $billLifetime + * @return $this + */ + public function setBillLifetime($billLifetime) + { + $this->billLifetime = $billLifetime; + + return $this; + } + + /** + * + */ + public function usePreAuth() + { + $this->preAuth = true; + + return $this; + } + + /** + * @return bool + */ + public function useRecurrentPayment() + { + $this->IsRecurrentStart = true; + + return $this; + } + + /** + * @param array $callbackFields + * @return $this + */ + public function setCallbackFields(array $callbackFields) + { + $this->callbackFields = join(' ', $callbackFields); + + return $this; + } + + /** + * @param string $callbackFormat + * @return $this + */ + public function setCallbackFormat($callbackFormat) + { + $this->callbackFormat = $callbackFormat; + + return $this; + } + + /** + * @param string $language + * @return $this + */ + public function setLanguage($language) + { + $this->language = $language; + + return $this; + } + + /** + * @param string $comment + * @return $this + */ + public function setComment($comment) + { + $this->comment = $comment; + + return $this; + } + + /** + * @param string $firstName + * @return $this + */ + public function setFirstName($firstName) + { + $this->firstName = $firstName; + + return $this; + } + + /** + * @param mixed $lastName + * @return $this + */ + public function setLastName($lastName) + { + $this->lastName = $lastName; + + return $this; + } + + /** + * @param string $middleName + * @return $this + */ + public function setMiddleName($middleName) + { + $this->middleName = $middleName; + + return $this; + } + + /** + * @param string $phone + * @return $this + */ + public function setPhone($phone) + { + $this->phone = $phone; + + return $this; + } + + /** + * @param string $address + * @return $this + */ + public function setAddress($address) + { + $this->address = $address; + + return $this; + } + + /** + * @param string $country + * @return $this + */ + public function setCountry($country) + { + $this->country = $country; + + return $this; + } + + /** + * @param string $state + * @return $this + */ + public function setState($state) + { + $this->state = $state; + + return $this; + } + + /** + * @param string $city + * @return $this + */ + public function setCity($city) + { + $this->city = $city; + + return $this; + } + + /** + * @param mixed $zip + * @return $this + */ + public function setZip($zip) + { + $this->zip = $zip; + + return $this; + } + + /* Getters */ + + /** + * @return string + */ + public function getShopIdp() + { + return $this->shopIdp; + + return $this; + } + + /** + * @return string + */ + public function getOrderIdp() + { + return $this->orderIdp; + + return $this; + } + + /** + * @return float|string + */ + public function getSubtotalP() + { + return $this->subtotalP; + + return $this; + } + + /** + * @return string + */ + public function getSignature() + { + return $this->signature; + } + + /** + * @return string + */ + public function getUrlReturnOk() + { + return $this->urlReturnOk; + } + + /** + * @return string + */ + public function getUrlReturnNo() + { + return $this->urlReturnNo; + } + + /** + * @return string + */ + public function getCurrency() + { + return $this->currency; + } + + /** + * @return string + */ + public function getEmail() + { + return $this->email; + } + + /** + * @return int + */ + public function getLifetime() + { + return $this->lifetime; + } + + /** + * @return int + */ + public function getOrderLifetime() + { + return $this->orderLifetime; + } + + + /** + * @return string + */ + public function getCustomerIdp() + { + return $this->customerIdp; + } + + /** + * @return mixed + */ + public function getCardIdp() + { + return $this->cardIdp; + } + + /** + * @return string + */ + public function getIData() + { + return $this->iData; + } + + /** + * @return string + */ + public function getPtCode() + { + return $this->ptCode; + } + + /** + * @return int + */ + public function getMeanType() + { + return $this->meanType; + } + + /** + * @return int + */ + public function getEMoneyType() + { + return $this->eMoneyType; + } + + /** + * @return int + */ + public function getBillLifetime() + { + return $this->billLifetime; + } + + /** + * @return bool + */ + public function isPreAuth() + { + return $this->preAuth; + } + + /** + * @return bool + */ + public function isIsRecurrentStart() + { + return $this->IsRecurrentStart; + } + + /** + * @return array + */ + public function getCallbackFields() + { + return $this->callbackFields; + } + + /** + * @return string + */ + public function getCallbackFormat() + { + return $this->callbackFormat; + } + + /** + * @return string + */ + public function getLanguage() + { + return $this->language; + } + + /** + * @return string + */ + public function getComment() + { + return $this->comment; + } + + /** + * @return string + */ + public function getFirstName() + { + return $this->firstName; + } + + /** + * @return mixed + */ + public function getLastName() + { + return $this->lastName; + } + + /** + * @return string + */ + public function getMiddleName() + { + return $this->middleName; + } + + /** + * @return string + */ + public function getPhone() + { + return $this->phone; + } + + /** + * @return string + */ + public function getAddress() + { + return $this->address; + } + + /** + * @return string + */ + public function getCountry() + { + return $this->country; + } + + /** + * @return string + */ + public function getState() + { + return $this->state; + } + + /** + * @return string + */ + public function getCity() + { + return $this->city; + } + + /** + * @return mixed + */ + public function getZip() + { + return $this->zip; + } + + /** + * @return array + */ + public function toArray() + { + return [ + 'Shop_IDP' => $this->getShopIdp(), + 'Order_IDP' => $this->getOrderIdp(), + 'Subtotal_P' => $this->getSubtotalP(), + 'Signature' => $this->getSignature(), + 'URL_RETURN_OK' => $this->getUrlReturnOk(), + 'URL_RETURN_NO' => $this->getUrlReturnNo(), + 'Currency' => $this->getCurrency(), + 'Email' => $this->getEmail(), + 'Lifetime' => $this->getLifetime(), + 'OrderLifetime' => $this->getOrderLifetime(), + 'Customer_IDP' => $this->getCustomerIdp(), + 'Card_IDP' => $this->getCardIdp(), + 'IData' => $this->getIData(), + 'PT_Code' => $this->getPtCode(), + 'MeanType' => $this->getMeanType(), + 'EMoneyType' => $this->getEMoneyType(), + 'BillLifetime' => $this->getBillLifetime(), + 'Preauth' => $this->isPreAuth(), + 'IsRecurrentStart' => $this->isIsRecurrentStart(), + 'CallbackFields' => $this->getCallbackFields(), + 'CallbackFormat' => $this->getCallbackFormat(), + 'Language' => $this->getLanguage(), + 'Comment' => $this->getComment(), + 'FirstName' => $this->getFirstName(), + 'LastName' => $this->getLastName(), + 'MiddleName' => $this->getMiddleName(), + 'Phone' => $this->getPhone(), + 'Address' => $this->getAddress(), + 'Country' => $this->getCountry(), + 'State' => $this->getState(), + 'City' => $this->getCity(), + 'Zip' => $this->getZip(), + ]; + } +} \ No newline at end of file diff --git a/src/Payment/PaymentInterface.php b/src/Payment/PaymentInterface.php new file mode 100644 index 0000000..915fde8 --- /dev/null +++ b/src/Payment/PaymentInterface.php @@ -0,0 +1,19 @@ +uri = $uri; + } + + /** + * @return string + */ + public function getUri() + { + return $this->uri; + } + + /** + * @return void + */ + public function go() + { + header(sprintf('Location: %s', $this->uri)); + } +} \ No newline at end of file diff --git a/src/Payment/UriInterface.php b/src/Payment/UriInterface.php new file mode 100644 index 0000000..e86618b --- /dev/null +++ b/src/Payment/UriInterface.php @@ -0,0 +1,22 @@ +request('results', 'POST', http_build_query($parameters)); + $xml = new \SimpleXMLElement($response); + + $array = []; + foreach ($xml->orders->order as $item) { + $array[] = (new Order()) + ->setAddress(Uniteller\xml_get($item, 'address')) + ->setApprovalCode(Uniteller\xml_get($item, 'approvalcode')) + ->setBankName(Uniteller\xml_get($item, 'bankname')) + ->setBillNumber(Uniteller\xml_get($item, 'billnumber')) + ->setBookingcomId(Uniteller\xml_get($item, 'bookingcom_id')) + ->setBookingcomPincode(Uniteller\xml_get($item, 'bookingcom_pincode')) + ->setCardIdp(Uniteller\xml_get($item, 'card_idp')) + ->setCardHolder(Uniteller\xml_get($item, 'cardholder')) + ->setCardNumber(Uniteller\xml_get($item, 'cardnumber')) + ->setCardType(Uniteller\xml_get($item, 'cardtype')) + ->setComment(Uniteller\xml_get($item, 'comment')) + ->setCurrency(Uniteller\xml_get($item, 'currency')) + ->setCvc2((bool)Uniteller\xml_get($item, 'cvc2')) + ->setDate(Uniteller\xml_get($item, 'date')) + ->setEmail(Uniteller\xml_get($item, 'email')) + ->setEMoneyType(Uniteller\xml_get($item, 'emoneytype')) + ->setEOrderData(Uniteller\xml_get($item, 'eorderdata')) + ->setErrorCode(Uniteller\xml_get($item, 'error_code')) + ->setErrorComment(Uniteller\xml_get($item, 'error_comment')) + ->setFirstName(Uniteller\xml_get($item, 'firstname')) + ->setGdsPaymentPurposeId(Uniteller\xml_get($item, 'gds_payment_purpose_id')) + ->setIData(Uniteller\xml_get($item, 'idata')) + ->setIp(Uniteller\xml_get($item, 'ipaddress')) + ->setLastName(Uniteller\xml_get($item, 'lastname')) + ->setLoanId(Uniteller\xml_get($item, 'loan_id')) + ->setMessage(Uniteller\xml_get($item, 'message')) + ->setMiddleName(Uniteller\xml_get($item, 'middlename')) + ->setNeedConfirm((bool) Uniteller\xml_get($item, 'need_confirm')) + ->setOrderNumber(Uniteller\xml_get($item, 'ordernumber')) + ->setParentOrderNumber(Uniteller\xml_get($item, 'parent_order_number')) + ->setPaymentType(Uniteller\xml_get($item, 'paymenttype')) + ->setPhone(Uniteller\xml_get($item, 'phone')) + ->setPtCode(Uniteller\xml_get($item, 'pt_code')) + ->setRecommendation(Uniteller\xml_get($item, 'recommendation')) + ->setResponseCode(Uniteller\xml_get($item, 'response_code')) + ->setStatus(Uniteller\xml_get($item, 'status')) + ->setTotal(Uniteller\xml_get($item, 'total')) + ->setPacketDate(Uniteller\xml_get($item, 'packetdate')) + ; + } + + return $array; + } +} \ No newline at end of file diff --git a/src/Signature/Signature.php b/src/Signature/Signature.php new file mode 100644 index 0000000..3c8e7df --- /dev/null +++ b/src/Signature/Signature.php @@ -0,0 +1,57 @@ + '', + 'Order_IDP' => '', + 'Subtotal_P' => '', + 'MeanType' => '', + 'EMoneyType' => '', + 'Lifetime' => '', + 'Customer_IDP' => '', + 'Card_IDP' => '', + 'IData' => '', + 'PT_Code' => '', + 'Password' => '', + ]; + + $string = join('&', array_map(function ($item) { + return md5($item); + }, array_merge($defaults, $params))); + + return strtoupper(md5($string)); + } + + /** + * Verify signature when Client will be send callback request. + * + * @param $signature + * @param array $params + * @return bool + */ + public function verify($signature, array $params) + { + return strtoupper(md5(join('', $params))) === $signature; + } +} \ No newline at end of file diff --git a/src/Signature/SignatureInterface.php b/src/Signature/SignatureInterface.php new file mode 100644 index 0000000..5f46c37 --- /dev/null +++ b/src/Signature/SignatureInterface.php @@ -0,0 +1,33 @@ +{$key})) { + return $default; + } + + if (trim((string) $object->{$key}) === '*НЕ ЗАДАВАЛСЯ*') { + return $default; + } + + return (string) $object->{$key}; +} \ No newline at end of file diff --git a/tests/ClientTest.php b/tests/ClientTest.php new file mode 100644 index 0000000..a432d2b --- /dev/null +++ b/tests/ClientTest.php @@ -0,0 +1,69 @@ +assertEmpty($reflect->getConstructor()->getParameters()); + } + + public function testOptionsAccessorsAndMutators() + { + $uniteller = new Client(); + $uniteller->setShopId('shop_id'); + $uniteller->setBaseUri('https://google.com'); + $uniteller->setPassword('security-long-password'); + $uniteller->setLogin(330011); + + $this->assertSame('shop_id', $uniteller->getShopId()); + $this->assertSame('https://google.com', $uniteller->getBaseUri()); + $this->assertSame('security-long-password', $uniteller->getPassword()); + $this->assertSame(330011, $uniteller->getLogin()); + } + + public function testOptionKeyResolver() + { + $uniteller = new Client(); + $this->assertSame('default', $uniteller->getOption('shop_id', 'default')); + } + + public function testDefaultObjectsIsRegistered() + { + $uniteller = new Client(); + $this->assertInstanceOf(PaymentInterface::class, $uniteller->getPayment()); + $this->assertInstanceOf(SignatureInterface::class, $uniteller->getSignature()); + } + + public function testSetOptionsUseArrayNotation() + { + $uniteller = new Client(); + $uniteller->setShopId('111'); + $uniteller->setPassword('pwd'); + + $uniteller->setOptions([ + 'password' => 1234, + 'base_uri' => 'https://google.com' + ]); + + $true = [ + 'shop_id' => '111', + 'password' => 1234, + 'base_uri' => 'https://google.com', + ]; + + $this->assertSame($true, $uniteller->getOptions()); + } +} \ No newline at end of file diff --git a/tests/SignatureTest.php b/tests/SignatureTest.php new file mode 100644 index 0000000..9a39895 --- /dev/null +++ b/tests/SignatureTest.php @@ -0,0 +1,42 @@ +create([ + 'Shop_IDP' => 'ACME', + 'Order_IDP' => 'FOO', + 'Subtotal_P' => '100', + 'Lifetime' => 300, + 'Customer_IDP' => 'short_shop_string', + 'Password' => 'LONG-PWD', + ]); + + $this->assertSame('3D1D6F830384886A81AD672F66392B03', $created); + } + + public function testSignatureVerifying() + { + $sig = new Signature; + + $results = $sig->verify('3F728AA479E50F5B10EE6C20258BFF88', [ + 'Order_ID' => 'FOO', + 'Status' => 'paid', + 'Password' => 'LONG-PWD', + ]); + + $this->assertTrue($results); + } +} \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..adbc8be --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,8 @@ +