├── tests ├── bootstrap.php ├── stub │ ├── error.xml │ ├── recurrent.csv │ ├── cancel.xml │ └── results.xml ├── TestCase.php ├── Cancel │ ├── RVRReasonTest.php │ ├── CancelBuilderTest.php │ └── CancelRequestTest.php ├── Payment │ └── PaymentTest.php ├── Recurrent │ ├── RecurrentBuilderTest.php │ └── RecurrentRequestTest.php ├── Error │ └── ResponseCodeTest.php ├── Order │ └── StatusTest.php ├── Http │ ├── FormatTest.php │ └── HttpManagerTest.php ├── Exception │ └── ExceptionFactoryTest.php ├── Signature │ └── SignatureTest.php ├── Results │ └── ResultsRequestTest.php └── ClientTest.php ├── .gitignore ├── src ├── Exception │ ├── FormatNotSupportedException.php │ ├── RequestNotSupportedException.php │ ├── NotImplementedException.php │ ├── ErrorException.php │ ├── AuthenticationException.php │ ├── BadFieldFormatException.php │ ├── OperationFailedException.php │ ├── MandatoryParameterException.php │ ├── NotSupportedSFieldException.php │ ├── AuthConfirmIsNotAllowedException.php │ ├── BillNotFoundException.php │ ├── UnitellerException.php │ └── ExceptionFactory.php ├── ArraybleInterface.php ├── Payment │ ├── UriInterface.php │ ├── PaymentInterface.php │ ├── Type.php │ ├── MeanType.php │ ├── Currency.php │ ├── EMoneyType.php │ ├── Payment.php │ ├── Uri.php │ └── PaymentBuilder.php ├── Http │ ├── HttpManagerInterface.php │ ├── Format.php │ └── HttpManager.php ├── Cancel │ ├── RVRReason.php │ ├── CancelBuilder.php │ └── CancelRequest.php ├── Signature │ ├── SignatureInterface.php │ ├── AbstractSignature.php │ ├── SignatureCallback.php │ ├── SignatureRecurrent.php │ └── SignaturePayment.php ├── Request │ └── RequestInterface.php ├── ClientInterface.php ├── helpers.php ├── Error │ ├── ResponseCode.php │ └── Error.php ├── Order │ ├── Status.php │ └── Order.php ├── Results │ └── ResultsRequest.php ├── Recurrent │ ├── RecurrentRequest.php │ └── RecurrentBuilder.php └── Client.php ├── example ├── results.php ├── README_RU.md ├── callback.php ├── README.md ├── credentials.example.php ├── recurrent.php ├── cancel.php └── index.php ├── .travis.yml ├── phpunit.xml ├── composer.json ├── LICENSE ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── .github └── workflows │ └── run-tests.yml ├── README.md └── README_RU.md /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/Exception/FormatNotSupportedException.php: -------------------------------------------------------------------------------- 1 | results([ 13 | 'ShopOrderNumber' => 'number' 14 | ]); 15 | 16 | 17 | var_dump($results); 18 | -------------------------------------------------------------------------------- /src/Exception/NotImplementedException.php: -------------------------------------------------------------------------------- 1 | getSignature()->verify('signature_from_post_params', ['all_parameters_from_post'])) { 13 | return 'invalid_signature'; 14 | } 15 | 16 | // ok! 17 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # SDK Payment Sample 2 | 3 | Simple example of usage Client PHP SDK. 4 | 5 | ## Install 6 | 7 | * `git clone git@github.com:tmconsulting/uniteller-php-sdk.git` 8 | * `composer install` 9 | * `cd ./examples` 10 | * `mv ./credentials.example.php ./credentials.php` 11 | 12 | First, your should be fill `./credentials.php` file with your credentials. 13 | Second, run server: `php -S localhost:8080` 14 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | setShopId('your_shop_id'); 16 | $uniteller->setLogin(1234); 17 | $uniteller->setPassword('your_password'); 18 | $uniteller->setBaseUri('https://wpay.uniteller.ru'); 19 | -------------------------------------------------------------------------------- /src/Payment/MeanType.php: -------------------------------------------------------------------------------- 1 | setOrderIdp(mt_rand(10000, 99999)) 11 | ->setSubtotalP(15) 12 | ->setParentOrderIdp(00000) // order id of any past payment 13 | ->setParentShopIdp($uniteller->getShopId()); // optional 14 | 15 | $results = $uniteller->recurrent($builder); 16 | 17 | var_dump($results); 18 | -------------------------------------------------------------------------------- /src/Exception/ErrorException.php: -------------------------------------------------------------------------------- 1 | errorCode; 28 | } 29 | } -------------------------------------------------------------------------------- /example/cancel.php: -------------------------------------------------------------------------------- 1 | setBillNumber('RRN Number, (12 digits)'); 15 | $results = $uniteller->cancel($builder); 16 | 17 | // or ... 18 | /* 19 | $results = $uniteller->cancel([ 20 | 'Billnumber' => 'RRN Number, (12 digits)', 21 | // ... 22 | ]); 23 | */ 24 | 25 | var_dump($results); 26 | -------------------------------------------------------------------------------- /tests/Cancel/RVRReasonTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(RVRReason::SHOP, 1); 19 | $this->assertEquals(RVRReason::CARDHOLDER, 2); 20 | $this->assertEquals(RVRReason::FRAUD, 3); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Cancel/RVRReason.php: -------------------------------------------------------------------------------- 1 | execute(['q' => 'banana'], ['base_uri' => 'https://google.com']); 21 | 22 | $this->assertInstanceOf(UriInterface::class, $results); 23 | $this->assertEquals('https://google.com/pay?q=banana', $results->getUri()); 24 | } 25 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.6 5 | - 7.0 6 | - 7.1 7 | - 7.2 8 | - nightly 9 | 10 | # This triggers builds to run on the new TravisCI infrastructure. 11 | # See: http://docs.travis-ci.com/user/workers/container-based-infrastructure/ 12 | sudo: false 13 | 14 | ## Cache composer 15 | cache: 16 | directories: 17 | - $HOME/.composer/cache 18 | 19 | matrix: 20 | fast_finish: true 21 | include: 22 | - php: 5.6 23 | env: 'COMPOSER_FLAGS="--prefer-stable --prefer-dist --optimize-autoloader"' 24 | allow_failures: 25 | - php: nightly 26 | 27 | before_script: 28 | - travis_retry composer self-update 29 | - travis_retry composer update ${COMPOSER_FLAGS} --no-interaction 30 | - composer config --list --global 31 | 32 | script: 33 | - vendor/bin/phpunit 34 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | tests 13 | 14 | 15 | 16 | 17 | 18 | integration 19 | 20 | 21 | 22 | 23 | 24 | ./src/ 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/Payment/EMoneyType.php: -------------------------------------------------------------------------------- 1 | uri = $uri; 30 | } 31 | 32 | /** 33 | * @return string 34 | */ 35 | public function getUri() 36 | { 37 | return $this->uri; 38 | } 39 | 40 | /** 41 | * @return void 42 | */ 43 | public function go() 44 | { 45 | header(sprintf('Location: %s', $this->uri)); 46 | } 47 | } -------------------------------------------------------------------------------- /src/Exception/AuthenticationException.php: -------------------------------------------------------------------------------- 1 | errorCode = Error::AUTHENTICATION; 31 | } 32 | } -------------------------------------------------------------------------------- /src/Exception/BadFieldFormatException.php: -------------------------------------------------------------------------------- 1 | errorCode = Error::BAD_FIELD_FORMAT; 31 | } 32 | } -------------------------------------------------------------------------------- /src/Exception/OperationFailedException.php: -------------------------------------------------------------------------------- 1 | errorCode = Error::OPERATION_FAILED; 31 | } 32 | } -------------------------------------------------------------------------------- /src/Exception/MandatoryParameterException.php: -------------------------------------------------------------------------------- 1 | errorCode = Error::MANDATORY_PARAMETER; 31 | } 32 | } -------------------------------------------------------------------------------- /src/Exception/NotSupportedSFieldException.php: -------------------------------------------------------------------------------- 1 | errorCode = Error::NOT_SUPPORTED_SFIELD; 31 | } 32 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tmconsulting/uniteller-php-sdk", 3 | "description": "PHP Library for integration with Uniteller payment processing.", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "roquie", 9 | "email": "roquie0@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": "^7.2 || ^8.0", 14 | "ext-simplexml": "*", 15 | "php-http/httplug": "^2", 16 | "php-http/guzzle7-adapter": "^1", 17 | "guzzlehttp/guzzle": "^7.4" 18 | }, 19 | "require-dev": { 20 | "symfony/var-dumper": "^3.2", 21 | "phpunit/phpunit": "^8" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "Tmconsulting\\Uniteller\\": "src" 26 | }, 27 | "files": [ 28 | "src/helpers.php" 29 | ] 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "Tmconsulting\\Uniteller\\Tests\\": "tests" 34 | } 35 | }, 36 | "scripts": { 37 | "test": "phpunit --coverage-text" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Exception/AuthConfirmIsNotAllowedException.php: -------------------------------------------------------------------------------- 1 | errorCode = Error::AUTH_CONFIRM_IS_NOT_ALLOWED; 31 | } 32 | } -------------------------------------------------------------------------------- /tests/Recurrent/RecurrentBuilderTest.php: -------------------------------------------------------------------------------- 1 | setShopIdp('FOO'); 19 | $builder->setParentShopIdp('BAR'); 20 | $builder->setOrderIdp('BAZ'); 21 | $builder->setParentOrderIdp('old'); 22 | $builder->setSubtotalP(10); 23 | 24 | $expected = [ 25 | 'Shop_IDP' => 'FOO', 26 | 'Parent_Shop_IDP' => 'BAR', 27 | 'Order_IDP' => 'BAZ', 28 | 'Parent_Order_IDP' => 'old', 29 | 'Subtotal_P' => 10, 30 | ]; 31 | 32 | $this->assertEquals($expected, $builder->toArray()); 33 | } 34 | } -------------------------------------------------------------------------------- /src/Signature/AbstractSignature.php: -------------------------------------------------------------------------------- 1 | toArray())); 31 | 32 | return strtoupper(md5($string)); 33 | } 34 | 35 | /** 36 | * Verify signature 37 | * 38 | * @param string $signature 39 | * @return bool 40 | */ 41 | public function verify($signature) 42 | { 43 | return $this->create() === $signature; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Exception/BillNotFoundException.php: -------------------------------------------------------------------------------- 1 | errorCode = Error::BILL_NOT_FOUND; 36 | } 37 | } -------------------------------------------------------------------------------- /tests/Cancel/CancelBuilderTest.php: -------------------------------------------------------------------------------- 1 | setBillNumber(1); 21 | $builder->setCurrency('RUB'); 22 | $builder->setRvrReason(RVRReason::SHOP); 23 | $builder->setSelectFields([]); 24 | $builder->setSubtotalP(10); 25 | 26 | $expected = [ 27 | 'Billnumber' => 1, 28 | 'Subtotal_P' => 10, 29 | 'Currency' => 'RUB', 30 | 'RVRReason' => RVRReason::SHOP, 31 | 'S_FIELDS' => [] 32 | ]; 33 | 34 | $this->assertEquals($expected, $builder->toArray()); 35 | } 36 | } -------------------------------------------------------------------------------- /example/index.php: -------------------------------------------------------------------------------- 1 | useRecurrentPayment() 13 | ->setOrderIdp(mt_rand(10000, 99999)) 14 | ->setSubtotalP(10) 15 | ->setCustomerIdp(mt_rand(10000, 99999)) 16 | ->setUrlReturnOk('http://google.ru/?q=success') 17 | ->setUrlReturnNo('http://google.ru/?q=failure'); 18 | 19 | 20 | $uri = $uniteller->payment($builder)->getUri(); 21 | 22 | echo <<< HTML 23 |

Client Payment Sample

24 |
25 |

Оплатить

26 | {$uri} 27 |
28 |

Отмена

29 | /cancel.php 30 |
31 |
32 |

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

33 | /results.php 34 |
35 | HTML; 36 | 37 | // or... 38 | 39 | /* 40 | 41 | $uniteller->payment([ 42 | 'Shop_IDP' => '', 43 | // ... 44 | ]); 45 | 46 | */ 47 | 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 SpaceTab.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/Error/ResponseCodeTest.php: -------------------------------------------------------------------------------- 1 | {$method}(ResponseCode::message($code)); 42 | } 43 | } -------------------------------------------------------------------------------- /src/Exception/UnitellerException.php: -------------------------------------------------------------------------------- 1 | getRequestTarget(), 30 | $request->getMethod(), 31 | $response->getStatusCode(), 32 | $response->getReasonPhrase() 33 | ); 34 | 35 | return new static($message, $request, $response, $previous); 36 | } 37 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | * Coding standard for the project is [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) 4 | * The project will follow strict [object calisthenics](http://www.slideshare.net/guilhermeblanco/object-calisthenics-applied-to-php) 5 | * Any contribution must provide tests for additional introduced conditions 6 | * Any un-confirmed issue needs a failing test case before being accepted 7 | * Pull requests must be sent from a new hotfix/feature branch, not from `master`. 8 | 9 | ## Installation 10 | 11 | To install the project and run the tests, you need to clone it first: 12 | 13 | ```sh 14 | git@github.com:tmconsulting/uniteller-php-sdk.git 15 | ``` 16 | 17 | You will then need to run a composer installation: 18 | 19 | ```sh 20 | cd uniteller-php-sdk 21 | curl -s https://getcomposer.org/installer | php 22 | php composer.phar install 23 | ``` 24 | 25 | ## Testing 26 | 27 | The PHPUnit version to be used is the one installed as a dev- dependency via composer: 28 | 29 | ```sh 30 | ./vendor/bin/phpunit 31 | ``` 32 | 33 | Accepted coverage for new contributions is 80%. Any contribution not satisfying this requirement 34 | won't be merged. 35 | -------------------------------------------------------------------------------- /tests/Order/StatusTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($expected, Status::resolve($name)); 41 | } 42 | } -------------------------------------------------------------------------------- /src/Request/RequestInterface.php: -------------------------------------------------------------------------------- 1 | expectException(RequestNotSupportedException::class); 23 | $this->expectExceptionMessage('Request [unknown] not supported here.'); 24 | 25 | Format::resolveXml('unknown'); 26 | } 27 | 28 | public function testCanNotResolveUnsupportedFormat() 29 | { 30 | $this->expectException(FormatNotSupportedException::class); 31 | $this->expectExceptionMessage('Format [xml] not supported for request [recurrent].'); 32 | 33 | Format::resolveXml(RequestInterface::REQUEST_RECURRENT); 34 | } 35 | 36 | public function testCanCallBadMagicStaticMethod() 37 | { 38 | $this->expectException(BadMethodCallException::class); 39 | $this->expectExceptionMessage('Method [resolvingJson] not found.'); 40 | 41 | Format::resolvingJson('unknown'); 42 | } 43 | 44 | public function testCanResolveSupportedFormat() 45 | { 46 | $value = Format::resolveXml(RequestInterface::REQUEST_CONFIRM); 47 | $this->assertEquals(3, $value); 48 | } 49 | } -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | {$key})) { 34 | return $default; 35 | } 36 | 37 | if (trim((string) $object->{$key}) === '*НЕ ЗАДАВАЛСЯ*') { 38 | return $default; 39 | } 40 | 41 | return (string) $object->{$key}; 42 | } 43 | 44 | /** 45 | * @param string $string 46 | * @param bool $isFlat 47 | * @return array|mixed 48 | */ 49 | function csv_to_array($string, $isFlat = false) 50 | { 51 | $lines = explode("\n", trim($string)); 52 | $headers = str_getcsv(array_shift($lines), ';'); 53 | $data = []; 54 | foreach ($lines as $line) { 55 | $row = []; 56 | foreach (str_getcsv($line, ';') as $key => $field) { 57 | $row[$headers[$key]] = $field; 58 | } 59 | $data[] = array_filter($row); 60 | } 61 | 62 | if ($isFlat && !empty($data)) { 63 | $data = $data[0]; 64 | } 65 | 66 | return $data; 67 | } 68 | 69 | /** 70 | * @param $array 71 | * @param array $excludeKeys 72 | * @return mixed 73 | */ 74 | function array_except($array, array $excludeKeys) 75 | { 76 | foreach ($excludeKeys as $key) { 77 | unset($array[$key]); 78 | } 79 | 80 | return $array; 81 | } -------------------------------------------------------------------------------- /src/Error/ResponseCode.php: -------------------------------------------------------------------------------- 1 | 'АВТОРИЗАЦИЯ УСПЕШНО ЗАВЕРШЕНА', 28 | 'AS100' => 'ОТКАЗ В АВТОРИЗАЦИИ', 29 | 'AS101' => 'ОТКАЗ В АВТОРИЗАЦИИ. Ошибочный номер карты.', 30 | 'AS102' => 'ОТКАЗ В АВТОРИЗАЦИИ. Недостаточно средств.', 31 | 'AS104' => 'ОТКАЗ В АВТОРИЗАЦИИ. Неверный срок действия карты.', 32 | 'AS105' => 'ОТКАЗ В АВТОРИЗАЦИИ. Превышен лимит.', 33 | 'AS107' => 'ОТКАЗ В АВТОРИЗАЦИИ. Ошибка приёма данных.', 34 | 'AS108' => 'ОТКАЗ В АВТОРИЗАЦИИ. Подозрение на мошенничество.', 35 | 'AS109' => 'ОТКАЗ В АВТОРИЗАЦИИ. Превышен лимит операций Client.', 36 | 'AS200' => 'ПОВТОРИТЕ АВТОРИЗАЦИЮ', 37 | 'AS998' => 'ОШИБКА СИСТЕМЫ. Свяжитесь с Client.', 38 | ]; 39 | 40 | /** 41 | * Преобразование кода ошибки в сообщение. 42 | * 43 | * @param $code 44 | * @return string|null 45 | */ 46 | public static function message($code) 47 | { 48 | if (array_key_exists($code, self::$messages)) { 49 | return self::$messages[$code]; 50 | } 51 | 52 | return null; 53 | } 54 | } -------------------------------------------------------------------------------- /tests/Exception/ExceptionFactoryTest.php: -------------------------------------------------------------------------------- 1 | expectException($e); 52 | 53 | throw ExceptionFactory::createFromMessage( 54 | $message, 55 | $this->createMock(RequestInterface::class), 56 | $this->createMock(ResponseInterface::class) 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/stub/cancel.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Value of ordernumber 6 | Value of response_code 7 | Value of recommendation 8 | Value of message 9 | Value of comment 10 | 10.03.2017 15:42:42 11 | Value of total 12 | Value of currency 13 | Value of cardtype 14 | Value of emoneytype 15 | Value of cardnumber 16 | foo=bar, baz=bue 17 | Value of lastname 18 | Value of firstname 19 | Value of middlename 20 | Value of bookingcom_id 21 | Value of bookingcom_pincode 22 | Value of gds_payment_purpose_id 23 | Value of idata 24 | 1 25 | Value of loan_id 26 | Value of card_idp 27 | Value of parent_order_number 28 |
Value of address
29 | Value of email 30 | paid 31 | Value of pt_code 32 | Value of approvalcode 33 | Value of cvc2 34 | Value of cardholder 35 | Value of ipaddress 36 | Value of billnumber 37 | Value of bankname 38 | Value of status 39 | Value of error_code 40 | Value of error_comment 41 | 10.03.2017 15:42:42 42 | Value of paymenttype 43 | Value of phone 44 |
45 |
46 |
-------------------------------------------------------------------------------- /tests/stub/results.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Value of ordernumber 6 | Value of response_code 7 | Value of recommendation 8 | Value of message 9 | Value of comment 10 | 10.03.2017 15:42:42 11 | Value of total 12 | Value of currency 13 | Value of cardtype 14 | Value of emoneytype 15 | Value of cardnumber 16 | foo=bar, baz=bue 17 | Value of lastname 18 | Value of firstname 19 | Value of middlename 20 | Value of bookingcom_id 21 | Value of bookingcom_pincode 22 | Value of gds_payment_purpose_id 23 | Value of idata 24 | 1 25 | Value of loan_id 26 | Value of card_idp 27 | Value of parent_order_number 28 |
Value of address
29 | Value of email 30 | paid 31 | Value of pt_code 32 | Value of approvalcode 33 | Value of cvc2 34 | Value of cardholder 35 | Value of ipaddress 36 | Value of billnumber 37 | Value of bankname 38 | Value of status 39 | Value of error_code 40 | Value of error_comment 41 | 10.03.2017 15:42:42 42 | Value of paymenttype 43 | Value of phone 44 |
45 |
46 |
-------------------------------------------------------------------------------- /src/Order/Status.php: -------------------------------------------------------------------------------- 1 | orderId = $orderId; 45 | 46 | return $this; 47 | } 48 | 49 | /** 50 | * @param $status 51 | * @return SignatureCallback 52 | */ 53 | public function setStatus($status) 54 | { 55 | $this->status = $status; 56 | 57 | return $this; 58 | } 59 | 60 | /** 61 | * @param array $fields 62 | * @return SignatureCallback 63 | */ 64 | public function setFields($fields) 65 | { 66 | $this->fields = $fields; 67 | 68 | return $this; 69 | } 70 | 71 | /** 72 | * @param string $password 73 | * @return SignatureCallback 74 | */ 75 | public function setPassword($password) 76 | { 77 | $this->password = $password; 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * @return string 84 | */ 85 | public function getOrderId() 86 | { 87 | return $this->orderId; 88 | } 89 | 90 | /** 91 | * @return string 92 | */ 93 | public function getStatus() 94 | { 95 | return $this->status; 96 | } 97 | 98 | /** 99 | * @return array 100 | */ 101 | public function getFields() 102 | { 103 | return $this->fields; 104 | } 105 | 106 | /** 107 | * @return string 108 | */ 109 | public function getPassword() 110 | { 111 | return $this->password; 112 | } 113 | 114 | /** 115 | * @return array 116 | */ 117 | public function toArray() 118 | { 119 | $array = []; 120 | $array['Order_ID'] = $this->getOrderId(); 121 | $array['Status'] = $this->getStatus(); 122 | $array = array_merge($array, $this->getFields()); 123 | $array['Password'] = $this->getPassword(); 124 | 125 | return $array; 126 | } 127 | 128 | /** 129 | * Create signature 130 | * 131 | * @return string 132 | */ 133 | public function create() 134 | { 135 | return strtoupper(md5(join('', $this->toArray()))); 136 | } 137 | } -------------------------------------------------------------------------------- /src/Http/Format.php: -------------------------------------------------------------------------------- 1 | ['csv' => 1, 'wddx' => 2, 'xml' => 3], 34 | RequestInterface::REQUEST_CONFIRM => ['csv' => 1, 'wddx' => 2, 'xml' => 3], 35 | RequestInterface::REQUEST_RECURRENT => ['csv' => 1], 36 | RequestInterface::REQUEST_CANCEL => ['csv' => 1, 'wddx' => 2, 'xml' => 3, 'soap' => 4], 37 | RequestInterface::REQUEST_RESULTS => ['csv' => 1, 'wddx' => 2, 'brackets' => 3, 'xml' => 4, 'soap' => 5] 38 | ]; 39 | 40 | /** 41 | * Преобразование от имени запроса к одному формату. 42 | * 43 | * @param $format 44 | * @param $requestName 45 | * @return int 46 | * @throws \Tmconsulting\Uniteller\Exception\FormatNotSupportedException 47 | * @throws \Tmconsulting\Uniteller\Exception\RequestNotSupportedException 48 | */ 49 | public static function resolve($format, $requestName) 50 | { 51 | if (! array_key_exists($requestName, self::$variants)) { 52 | throw new RequestNotSupportedException(sprintf( 53 | 'Request [%s] not supported here.', $requestName 54 | )); 55 | } 56 | 57 | if (! array_key_exists($format, self::$variants[$requestName])) { 58 | throw new FormatNotSupportedException(sprintf( 59 | 'Format [%s] not supported for request [%s].', $format, $requestName 60 | )); 61 | } 62 | 63 | return self::$variants[$requestName][$format]; 64 | } 65 | 66 | /** 67 | * Вариант Format::resolveXml(...), для ленивых, как я. 68 | * 69 | * @param $name 70 | * @param array $arguments 71 | * @return int 72 | */ 73 | public static function __callStatic($name, array $arguments) 74 | { 75 | if (false === strpos($name, 'resolve')) { 76 | throw new BadMethodCallException(sprintf( 77 | 'Method [%s] not found.', $name 78 | )); 79 | } 80 | 81 | return self::resolve( 82 | strtolower(substr($name, 7)), 83 | Uniteller\array_get($arguments, 0) 84 | ); 85 | } 86 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at roquie0@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /src/Exception/ExceptionFactory.php: -------------------------------------------------------------------------------- 1 | 'Authorization(\s)+confirm(\s)+is(\s)+not(\s)+allowed', 28 | Error::AUTHENTICATION => 'Authentication(\s)+error', 29 | Error::BAD_FIELD_FORMAT => 'Field(.*)has(\s)+bad(\s)+format', 30 | Error::MANDATORY_PARAMETER => 'Mandatory(\s)+parameter(\s)+\\\'(.*)\\\'(.*)', 31 | Error::NOT_SUPPORTED_SFIELD => 'S_FIELDS(\s)+contains(\s)+field(\s)+\\\'(.*)\\\'(.*)', 32 | Error::OPERATION_FAILED => 'The(\s)+operation(\s)+failed', 33 | ]; 34 | 35 | /** 36 | * В /results запросе кода ошибки нет, есть только его сообщение. 37 | * С помощью регулярок (а как еще!?) вызовем нужный эксепшен на помощь разработчикам. 38 | */ 39 | public static function createFromMessage( 40 | string $message, 41 | RequestInterface $request, 42 | ResponseInterface $response 43 | ): ErrorException { 44 | foreach (self::$messageToErrorCode as $code => $regex) { 45 | preg_match('/' . $regex . '/', $message, $matches); 46 | 47 | if (count($matches) > 0) { 48 | return self::create($code, $message, $request, $response); 49 | } 50 | } 51 | 52 | return self::create(Error::UNKNOWN, $message, $request, $response); 53 | } 54 | 55 | /** 56 | * Конвертируем код ошибки в эксепшен. 57 | */ 58 | public static function create( 59 | int $code, 60 | string $message, 61 | RequestInterface $request, 62 | ResponseInterface $response 63 | ): ErrorException { 64 | switch ($code) { 65 | case Error::AUTH_CONFIRM_IS_NOT_ALLOWED: 66 | return new AuthConfirmIsNotAllowedException( 67 | $message, $request, $response 68 | ); 69 | case Error::AUTHENTICATION: 70 | return new AuthenticationException( 71 | $message, $request, $response 72 | ); 73 | case Error::BAD_FIELD_FORMAT: 74 | return new BadFieldFormatException( 75 | $message, $request, $response 76 | ); 77 | case Error::MANDATORY_PARAMETER: 78 | return new MandatoryParameterException( 79 | $message, $request, $response 80 | ); 81 | case Error::NOT_SUPPORTED_SFIELD: 82 | return new NotSupportedSFieldException( 83 | $message, $request, $response 84 | ); 85 | case Error::OPERATION_FAILED: 86 | return new OperationFailedException( 87 | $message, $request, $response 88 | ); 89 | } 90 | 91 | return new ErrorException($message, $request, $response); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tests/Http/HttpManagerTest.php: -------------------------------------------------------------------------------- 1 | http([ 30 | new Response(200, [], $this->getStubContents('cancel')), 31 | ]); 32 | 33 | $this->assertEquals( 34 | $this->getStubContents('cancel'), 35 | $manager->request(RequestInterface::REQUEST_CANCEL) 36 | ); 37 | } 38 | 39 | public function testBehaviorIfServerReturnError() 40 | { 41 | $this->expectException(UnitellerException::class); 42 | 43 | $manager = $this->http([ 44 | new RequestException( 45 | 'Error Communicating with Server', 46 | new Request('GET', 'test'), 47 | new Response(500) 48 | ) 49 | ]); 50 | 51 | $manager->request(RequestInterface::REQUEST_CANCEL); 52 | } 53 | 54 | public function testCreatingExceptionsFromPayloadErrorText() 55 | { 56 | $this->expectException(AuthConfirmIsNotAllowedException::class); 57 | 58 | $manager = $this->http([ 59 | new Response(200, [], 'ERROR: Authorization confirm is not allowed') 60 | ]); 61 | 62 | $manager->request(RequestInterface::REQUEST_CANCEL); 63 | } 64 | 65 | public function testCreatingExceptionsFromXmlResponse() 66 | { 67 | $this->expectException(AuthenticationException::class); 68 | 69 | $manager = $this->http([ 70 | new Response(200, [], $this->getStubContents('error')) 71 | ]); 72 | 73 | $manager->request(RequestInterface::REQUEST_CANCEL); 74 | } 75 | 76 | public function testThrownBasicExceptionIfStatusNotIncludedInTheRange() 77 | { 78 | $this->expectException(UnitellerException::class); 79 | 80 | $manager = $this->http([new Response(302, [])]); 81 | 82 | $manager->request(RequestInterface::REQUEST_CANCEL); 83 | } 84 | 85 | /** 86 | * @param $queue 87 | * @return \Tmconsulting\Uniteller\Http\HttpManager 88 | */ 89 | protected function http($queue) 90 | { 91 | $mock = new MockHandler($queue); 92 | 93 | $httpClient = new GuzzleAdapter(new GuzzleClient([ 94 | 'handler' => HandlerStack::create($mock) 95 | ])); 96 | 97 | return new HttpManager($httpClient, [ 98 | 'base_uri' => '', 99 | 'shop_id' => '', 100 | 'login' => '', 101 | 'password' => '', 102 | ]); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Error/Error.php: -------------------------------------------------------------------------------- 1 | billNumber = $billNumber; 66 | 67 | return $this; 68 | } 69 | 70 | /** 71 | * @param string $currency 72 | * @return $this 73 | */ 74 | public function setCurrency($currency) 75 | { 76 | $this->currency = $currency; 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * @param int $rvrReason 83 | * @return $this 84 | */ 85 | public function setRvrReason($rvrReason) 86 | { 87 | $this->rvrReason = $rvrReason; 88 | 89 | return $this; 90 | } 91 | 92 | /** 93 | * @param float $subtotalP 94 | * @return $this 95 | */ 96 | public function setSubtotalP($subtotalP) 97 | { 98 | $this->subtotalP = $subtotalP; 99 | 100 | return $this; 101 | } 102 | 103 | /** 104 | * @param array $selectFields 105 | * @return $this 106 | */ 107 | public function setSelectFields(array $selectFields) 108 | { 109 | $this->selectFields = $selectFields; 110 | 111 | return $this; 112 | } 113 | 114 | /** 115 | * @return int 116 | */ 117 | public function getBillNumber() 118 | { 119 | return $this->billNumber; 120 | } 121 | 122 | /** 123 | * @return string 124 | */ 125 | public function getCurrency() 126 | { 127 | return $this->currency; 128 | } 129 | 130 | /** 131 | * @return int 132 | */ 133 | public function getRvrReason() 134 | { 135 | return $this->rvrReason; 136 | } 137 | 138 | /** 139 | * @return float 140 | */ 141 | public function getSubtotalP() 142 | { 143 | return $this->subtotalP; 144 | } 145 | 146 | /** 147 | * @return array 148 | */ 149 | public function getSelectFields() 150 | { 151 | return $this->selectFields; 152 | } 153 | 154 | public function toArray() 155 | { 156 | return [ 157 | 'Billnumber' => $this->getBillNumber(), 158 | 'Subtotal_P' => $this->getSubtotalP(), 159 | 'Currency' => $this->getCurrency(), 160 | 'RVRReason' => $this->getRvrReason(), 161 | 'S_FIELDS' => $this->getSelectFields() 162 | ]; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /tests/Recurrent/RecurrentRequestTest.php: -------------------------------------------------------------------------------- 1 | createMock(HttpManagerInterface::class); 21 | $manager 22 | ->expects($this->once()) 23 | ->method('request') 24 | ->willReturn($this->getStubContents('recurrent', 'csv')); 25 | 26 | $request = new RecurrentRequest(); 27 | /** @var \Tmconsulting\Uniteller\Order\Order $order */ 28 | foreach ($request->execute($manager) as $order) { 29 | $this->assertInstanceOf(Order::class, $order); 30 | $this->assertEquals('Value of address', $order->getAddress()); 31 | $this->assertEquals('Value of approvalCode', $order->getApprovalCode()); 32 | $this->assertEquals('Value of bankName', $order->getBankName()); 33 | $this->assertEquals('Value of billNumber', $order->getBillNumber()); 34 | $this->assertEquals(null, $order->getBookingcomId()); 35 | $this->assertEquals(null, $order->getBookingcomPincode()); 36 | $this->assertEquals(null, $order->getCardIdp()); 37 | $this->assertEquals('Value of cardHolder', $order->getCardHolder()); 38 | $this->assertEquals('Value of cardNumber', $order->getCardNumber()); 39 | $this->assertEquals('Value of cardType', $order->getCardType()); 40 | $this->assertEquals('Value of comment', $order->getComment()); 41 | $this->assertEquals('Value of currency', $order->getCurrency()); 42 | $this->assertEquals(true, $order->isCvc2()); 43 | $this->assertInstanceOf(\DateTime::class, $order->getDate()); 44 | $this->assertEquals('Value of email', $order->getEmail()); 45 | $this->assertEquals(null, $order->getEMoneyType()); 46 | $this->assertEquals(null, $order->getEOrderData()); 47 | $this->assertEquals('Value of error_code', $order->getErrorCode()); 48 | $this->assertEquals('Value of error_comment', $order->getErrorComment()); 49 | $this->assertEquals('Value of firstName', $order->getFirstName()); 50 | $this->assertEquals(null, $order->getGdsPaymentPurposeId()); 51 | $this->assertEquals(null, $order->getIData()); 52 | $this->assertEquals('Value of ipAddress', $order->getIp()); 53 | $this->assertEquals('Value of lastName', $order->getLastName()); 54 | $this->assertEquals(null, $order->getLoanId()); 55 | $this->assertEquals('Value of message', $order->getMessage()); 56 | $this->assertEquals('Value of middleName', $order->getMiddleName()); 57 | $this->assertEquals(false, $order->isNeedConfirm()); 58 | $this->assertEquals('Value of orderNumber', $order->getOrderNumber()); 59 | $this->assertEquals(null, $order->getParentOrderNumber()); 60 | $this->assertEquals('Value of paymentType', $order->getPaymentType()); 61 | $this->assertEquals('Value of phone', $order->getPhone()); 62 | $this->assertEquals(null, $order->getPtCode()); 63 | $this->assertEquals('Value of recommendation', $order->getRecommendation()); 64 | $this->assertEquals('Value of response_code', $order->getResponseCode()); 65 | $this->assertEquals(Status::AUTHORIZED, $order->getStatus()); 66 | $this->assertEquals('Value of total', $order->getTotal()); 67 | $this->assertInstanceOf(\DateTime::class, $order->getPacketDate()); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/Cancel/CancelRequest.php: -------------------------------------------------------------------------------- 1 | request('unblock', 'POST', http_build_query($parameters)); 32 | $xml = new \SimpleXMLElement($response); 33 | 34 | $array = []; 35 | foreach ($xml->orders->order as $item) { 36 | $array[] = (new Order()) 37 | ->setAddress(Uniteller\xml_get($item, 'address')) 38 | ->setApprovalCode(Uniteller\xml_get($item, 'approvalcode')) 39 | ->setBankName(Uniteller\xml_get($item, 'bankname')) 40 | ->setBillNumber(Uniteller\xml_get($item, 'billnumber')) 41 | ->setBookingcomId(Uniteller\xml_get($item, 'bookingcom_id')) 42 | ->setBookingcomPincode(Uniteller\xml_get($item, 'bookingcom_pincode')) 43 | ->setCardIdp(Uniteller\xml_get($item, 'card_idp')) 44 | ->setCardHolder(Uniteller\xml_get($item, 'cardholder')) 45 | ->setCardNumber(Uniteller\xml_get($item, 'cardnumber')) 46 | ->setCardType(Uniteller\xml_get($item, 'cardtype')) 47 | ->setComment(Uniteller\xml_get($item, 'comment')) 48 | ->setCurrency(Uniteller\xml_get($item, 'currency')) 49 | ->setCvc2((bool)Uniteller\xml_get($item, 'cvc2')) 50 | ->setDate(Uniteller\xml_get($item, 'date')) 51 | ->setEmail(Uniteller\xml_get($item, 'email')) 52 | ->setEMoneyType(Uniteller\xml_get($item, 'emoneytype')) 53 | ->setEOrderData(Uniteller\xml_get($item, 'eorderdata')) 54 | ->setErrorCode(Uniteller\xml_get($item, 'error_code')) 55 | ->setErrorComment(Uniteller\xml_get($item, 'error_comment')) 56 | ->setFirstName(Uniteller\xml_get($item, 'firstname')) 57 | ->setGdsPaymentPurposeId(Uniteller\xml_get($item, 'gds_payment_purpose_id')) 58 | ->setIData(Uniteller\xml_get($item, 'idata')) 59 | ->setIp(Uniteller\xml_get($item, 'ipaddress')) 60 | ->setLastName(Uniteller\xml_get($item, 'lastname')) 61 | ->setLoanId(Uniteller\xml_get($item, 'loan_id')) 62 | ->setMessage(Uniteller\xml_get($item, 'message')) 63 | ->setMiddleName(Uniteller\xml_get($item, 'middlename')) 64 | ->setNeedConfirm((bool) Uniteller\xml_get($item, 'need_confirm')) 65 | ->setOrderNumber(Uniteller\xml_get($item, 'ordernumber')) 66 | ->setParentOrderNumber(Uniteller\xml_get($item, 'parent_order_number')) 67 | ->setPaymentType(Uniteller\xml_get($item, 'paymenttype')) 68 | ->setPhone(Uniteller\xml_get($item, 'phone')) 69 | ->setPtCode(Uniteller\xml_get($item, 'pt_code')) 70 | ->setRecommendation(Uniteller\xml_get($item, 'recommendation')) 71 | ->setResponseCode(Uniteller\xml_get($item, 'response_code')) 72 | ->setStatus(Uniteller\xml_get($item, 'status')) 73 | ->setTotal(Uniteller\xml_get($item, 'total')) 74 | ->setPacketDate(Uniteller\xml_get($item, 'packetdate')) 75 | ; 76 | } 77 | 78 | return $array; 79 | } 80 | } -------------------------------------------------------------------------------- /src/Results/ResultsRequest.php: -------------------------------------------------------------------------------- 1 | request('results', 'POST', http_build_query($parameters)); 32 | $xml = new \SimpleXMLElement($response); 33 | 34 | $array = []; 35 | foreach ($xml->orders->order as $item) { 36 | $array[] = (new Order()) 37 | ->setAddress(Uniteller\xml_get($item, 'address')) 38 | ->setApprovalCode(Uniteller\xml_get($item, 'approvalcode')) 39 | ->setBankName(Uniteller\xml_get($item, 'bankname')) 40 | ->setBillNumber(Uniteller\xml_get($item, 'billnumber')) 41 | ->setBookingcomId(Uniteller\xml_get($item, 'bookingcom_id')) 42 | ->setBookingcomPincode(Uniteller\xml_get($item, 'bookingcom_pincode')) 43 | ->setCardIdp(Uniteller\xml_get($item, 'card_idp')) 44 | ->setCardHolder(Uniteller\xml_get($item, 'cardholder')) 45 | ->setCardNumber(Uniteller\xml_get($item, 'cardnumber')) 46 | ->setCardType(Uniteller\xml_get($item, 'cardtype')) 47 | ->setComment(Uniteller\xml_get($item, 'comment')) 48 | ->setCurrency(Uniteller\xml_get($item, 'currency')) 49 | ->setCvc2((bool)Uniteller\xml_get($item, 'cvc2')) 50 | ->setDate(Uniteller\xml_get($item, 'date')) 51 | ->setEmail(Uniteller\xml_get($item, 'email')) 52 | ->setEMoneyType(Uniteller\xml_get($item, 'emoneytype')) 53 | ->setEOrderData(Uniteller\xml_get($item, 'eorderdata')) 54 | ->setErrorCode(Uniteller\xml_get($item, 'error_code')) 55 | ->setErrorComment(Uniteller\xml_get($item, 'error_comment')) 56 | ->setFirstName(Uniteller\xml_get($item, 'firstname')) 57 | ->setGdsPaymentPurposeId(Uniteller\xml_get($item, 'gds_payment_purpose_id')) 58 | ->setIData(Uniteller\xml_get($item, 'idata')) 59 | ->setIp(Uniteller\xml_get($item, 'ipaddress')) 60 | ->setLastName(Uniteller\xml_get($item, 'lastname')) 61 | ->setLoanId(Uniteller\xml_get($item, 'loan_id')) 62 | ->setMessage(Uniteller\xml_get($item, 'message')) 63 | ->setMiddleName(Uniteller\xml_get($item, 'middlename')) 64 | ->setNeedConfirm((bool) Uniteller\xml_get($item, 'need_confirm')) 65 | ->setOrderNumber(Uniteller\xml_get($item, 'ordernumber')) 66 | ->setParentOrderNumber(Uniteller\xml_get($item, 'parent_order_number')) 67 | ->setPaymentType(Uniteller\xml_get($item, 'paymenttype')) 68 | ->setPhone(Uniteller\xml_get($item, 'phone')) 69 | ->setPtCode(Uniteller\xml_get($item, 'pt_code')) 70 | ->setRecommendation(Uniteller\xml_get($item, 'recommendation')) 71 | ->setResponseCode(Uniteller\xml_get($item, 'response_code')) 72 | ->setStatus(Uniteller\xml_get($item, 'status')) 73 | ->setTotal(Uniteller\xml_get($item, 'total')) 74 | ->setPacketDate(Uniteller\xml_get($item, 'packetdate')) 75 | ; 76 | } 77 | 78 | return $array; 79 | } 80 | } -------------------------------------------------------------------------------- /src/Signature/SignatureRecurrent.php: -------------------------------------------------------------------------------- 1 | shopIdp = $shopIdp; 56 | 57 | return $this; 58 | } 59 | 60 | /** 61 | * @param string $parentShopIdp 62 | * @return SignatureRecurrent 63 | */ 64 | public function setParentShopIdp($parentShopIdp) 65 | { 66 | $this->parentShopIdp = $parentShopIdp; 67 | 68 | return $this; 69 | } 70 | 71 | /** 72 | * @param string $orderIdp 73 | * @return SignatureRecurrent 74 | */ 75 | public function setOrderIdp($orderIdp) 76 | { 77 | $this->orderIdp = $orderIdp; 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * @param string $subtotalP 84 | * @return SignatureRecurrent 85 | */ 86 | public function setSubtotalP($subtotalP) 87 | { 88 | $this->subtotalP = $subtotalP; 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * @param string $parentOrderIdp 95 | * @return SignatureRecurrent 96 | */ 97 | public function setParentOrderIdp($parentOrderIdp) 98 | { 99 | $this->parentOrderIdp = $parentOrderIdp; 100 | 101 | return $this; 102 | } 103 | 104 | /** 105 | * @param string $password 106 | * @return SignatureRecurrent 107 | */ 108 | public function setPassword($password) 109 | { 110 | $this->password = $password; 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * @return string 117 | */ 118 | public function getShopIdp() 119 | { 120 | return $this->shopIdp; 121 | } 122 | 123 | /** 124 | * @return string 125 | */ 126 | public function getParentShopIdp() 127 | { 128 | return $this->parentShopIdp; 129 | } 130 | 131 | /** 132 | * @return string 133 | */ 134 | public function getOrderIdp() 135 | { 136 | return $this->orderIdp; 137 | } 138 | 139 | /** 140 | * @return string 141 | */ 142 | public function getSubtotalP() 143 | { 144 | return $this->subtotalP; 145 | } 146 | 147 | /** 148 | * @return string 149 | */ 150 | public function getParentOrderIdp() 151 | { 152 | return $this->parentOrderIdp; 153 | } 154 | 155 | /** 156 | * @return string 157 | */ 158 | public function getPassword() 159 | { 160 | return $this->password; 161 | } 162 | 163 | /** 164 | * @return array 165 | */ 166 | public function toArray() 167 | { 168 | $array = []; 169 | $array['Shop_IDP'] = $this->getShopIdp(); 170 | if($this->getParentShopIdp() !== null) 171 | $array['Parent_Shop_IDP'] = $this->getParentShopIdp(); 172 | $array['Order_IDP'] = $this->getOrderIdp(); 173 | $array['Subtotal_P'] = $this->getSubtotalP(); 174 | $array['Parent_Order_IDP'] = $this->getParentOrderIdp(); 175 | $array['Password'] = $this->getPassword(); 176 | 177 | return $array; 178 | } 179 | } -------------------------------------------------------------------------------- /tests/Signature/SignatureTest.php: -------------------------------------------------------------------------------- 1 | setShopIdp('ACME') 21 | ->setOrderIdp('FOO') 22 | ->setSubtotalP(100) 23 | ->setLifeTime(300) 24 | ->setCustomerIdp('short_shop_string') 25 | ->setPassword('LONG-PWD') 26 | ->create(); 27 | 28 | $this->assertSame('3D1D6F830384886A81AD672F66392B03', $sig); 29 | } 30 | 31 | public function testRecurrentSignatureCreation() 32 | { 33 | $sig = (new SignatureRecurrent()) 34 | ->setShopIdp('ACME') 35 | ->setOrderIdp('FOO') 36 | ->setSubtotalP(100) 37 | ->setParentOrderIdp('BAR') 38 | ->setPassword('LONG-PWD') 39 | ->create(); 40 | 41 | $this->assertSame('A5FE1C95A2819EBACFC2145EE83742F6', $sig); 42 | } 43 | 44 | public function testCallbackSignatureCreation() 45 | { 46 | $sig = (new SignatureCallback()) 47 | ->setOrderId('FOO') 48 | ->setStatus('paid') 49 | ->setPassword('LONG-PWD') 50 | ->create(); 51 | 52 | $this->assertSame('3F728AA479E50F5B10EE6C20258BFF88', $sig); 53 | } 54 | 55 | public function testCallbackSignatureCreationWithFields() 56 | { 57 | $sig = (new SignatureCallback()) 58 | ->setOrderId('FOO') 59 | ->setStatus('paid') 60 | ->setFields([ 61 | 'AcquirerID' => 'fOO', 62 | 'ApprovalCode' => 'BaR', 63 | 'BillNumber' => 'baz', 64 | ]) 65 | ->setPassword('LONG-PWD') 66 | ->create(); 67 | 68 | $this->assertSame('1F4E3B63AE408D0BE1E33965E6697236', $sig); 69 | } 70 | 71 | public function testPaymentSignatureVerifying() 72 | { 73 | $sig = (new SignaturePayment()) 74 | ->setShopIdp('ACME') 75 | ->setOrderIdp('FOO') 76 | ->setSubtotalP(100) 77 | ->setLifeTime(300) 78 | ->setCustomerIdp('short_shop_string') 79 | ->setPassword('LONG-PWD'); 80 | 81 | $this->assertTrue($sig->verify('3D1D6F830384886A81AD672F66392B03')); 82 | } 83 | 84 | public function testRecurrentSignatureVerifying() 85 | { 86 | $sig = (new SignatureRecurrent()) 87 | ->setShopIdp('ACME') 88 | ->setOrderIdp('FOO') 89 | ->setSubtotalP(100) 90 | ->setParentOrderIdp('BAR') 91 | ->setPassword('LONG-PWD'); 92 | 93 | $this->assertTrue($sig->verify('A5FE1C95A2819EBACFC2145EE83742F6')); 94 | } 95 | 96 | public function testCallbackSignatureVerifying() 97 | { 98 | $sig = (new SignatureCallback()) 99 | ->setOrderId('FOO') 100 | ->setStatus('paid') 101 | ->setPassword('LONG-PWD'); 102 | 103 | $this->assertTrue($sig->verify('3F728AA479E50F5B10EE6C20258BFF88')); 104 | } 105 | 106 | public function testCallbackSignatureVerifyingWithFields() 107 | { 108 | $sig = (new SignatureCallback()) 109 | ->setOrderId('FOO') 110 | ->setStatus('paid') 111 | ->setFields([ 112 | 'AcquirerID' => 'fOO', 113 | 'ApprovalCode' => 'BaR', 114 | 'BillNumber' => 'baz', 115 | ]) 116 | ->setPassword('LONG-PWD'); 117 | 118 | $this->assertTrue($sig->verify('1F4E3B63AE408D0BE1E33965E6697236')); 119 | } 120 | } -------------------------------------------------------------------------------- /src/Recurrent/RecurrentRequest.php: -------------------------------------------------------------------------------- 1 | request('recurrent', 'POST', http_build_query($parameters), [], 'csv'); 32 | $csv = Uniteller\csv_to_array($response); 33 | $array = []; 34 | foreach ($csv as $item) { 35 | $array[] = (new Order()) 36 | ->setAddress(Uniteller\array_get($item, 'Address')) 37 | ->setApprovalCode(Uniteller\array_get($item, 'ApprovalCode')) 38 | ->setBankName(Uniteller\array_get($item, 'BankName')) 39 | ->setBillNumber(Uniteller\array_get($item, 'BillNumber')) 40 | //->setBookingcomId(Uniteller\array_get($item, 'bookingcom_id')) 41 | //->setBookingcomPincode(Uniteller\array_get($item, 'bookingcom_pincode')) 42 | //->setCardIdp(Uniteller\array_get($item, 'card_idp')) 43 | ->setCardHolder(Uniteller\array_get($item, 'CardHolder')) 44 | ->setCardNumber(Uniteller\array_get($item, 'CardNumber')) 45 | ->setCardType(Uniteller\array_get($item, 'CardType')) 46 | ->setComment(Uniteller\array_get($item, 'Comment')) 47 | ->setCurrency(Uniteller\array_get($item, 'Currency')) 48 | ->setCvc2((bool)Uniteller\array_get($item, 'CVC2', false)) 49 | ->setDate(Uniteller\array_get($item, 'Date')) 50 | ->setEmail(Uniteller\array_get($item, 'Email')) 51 | //->setEMoneyType(Uniteller\array_get($item, 'emoneytype')) 52 | //->setEOrderData(Uniteller\array_get($item, 'eorderdata')) 53 | ->setErrorCode(Uniteller\array_get($item, 'Error_Code')) 54 | ->setErrorComment(Uniteller\array_get($item, 'Error_Comment')) 55 | ->setFirstName(Uniteller\array_get($item, 'FirstName')) 56 | //->setGdsPaymentPurposeId(Uniteller\array_get($item, 'gds_payment_purpose_id')) 57 | //->setIData(Uniteller\array_get($item, 'idata')) 58 | ->setIp(Uniteller\array_get($item, 'IPAddress')) 59 | ->setLastName(Uniteller\array_get($item, 'LastName')) 60 | //->setLoanId(Uniteller\array_get($item, 'loan_id')) 61 | ->setMessage(Uniteller\array_get($item, 'Message')) 62 | ->setMiddleName(Uniteller\array_get($item, 'MiddleName')) 63 | //->setNeedConfirm((bool)Uniteller\array_get($item, 'need_confirm')) 64 | ->setOrderNumber(Uniteller\array_get($item, 'OrderNumber')) 65 | //->setParentOrderNumber(Uniteller\array_get($item, 'parent_order_number')) 66 | ->setPaymentType(Uniteller\array_get($item, 'PaymentType')) 67 | ->setPhone(Uniteller\array_get($item, 'Phone')) 68 | //->setPtCode(Uniteller\array_get($item, 'pt_code')) 69 | ->setRecommendation(Uniteller\array_get($item, 'Recommendation')) 70 | ->setResponseCode(Uniteller\array_get($item, 'Response_Code')) 71 | ->setStatus(Uniteller\array_get($item, 'Status')) 72 | ->setTotal(Uniteller\array_get($item, 'Total')) 73 | ->setPacketDate(Uniteller\array_get($item, 'PacketDate')) 74 | ->setSignature(Uniteller\array_get($item, 'Signature')); 75 | } 76 | 77 | return $array; 78 | } 79 | } -------------------------------------------------------------------------------- /tests/Cancel/CancelRequestTest.php: -------------------------------------------------------------------------------- 1 | createMock(HttpManagerInterface::class); 22 | $manager 23 | ->expects($this->once()) 24 | ->method('request') 25 | ->willReturn($this->getStubContents('cancel')); 26 | 27 | $request = new CancelRequest(); 28 | /** @var \Tmconsulting\Uniteller\Order\Order $order */ 29 | foreach ($request->execute($manager) as $order) { 30 | $this->assertInstanceOf(Order::class, $order); 31 | $this->assertEquals('Value of address', $order->getAddress()); 32 | $this->assertEquals('Value of approvalcode', $order->getApprovalCode()); 33 | $this->assertEquals('Value of bankname', $order->getBankName()); 34 | $this->assertEquals('Value of billnumber', $order->getBillNumber()); 35 | $this->assertEquals('Value of bookingcom_id', $order->getBookingcomId()); 36 | $this->assertEquals('Value of bookingcom_pincode', $order->getBookingcomPincode()); 37 | $this->assertEquals('Value of card_idp', $order->getCardIdp()); 38 | $this->assertEquals('Value of cardholder', $order->getCardHolder()); 39 | $this->assertEquals('Value of cardnumber', $order->getCardNumber()); 40 | $this->assertEquals('Value of cardtype', $order->getCardType()); 41 | $this->assertEquals('Value of comment', $order->getComment()); 42 | $this->assertEquals('Value of currency', $order->getCurrency()); 43 | $this->assertEquals(true, $order->isCvc2()); 44 | $this->assertInstanceOf(\DateTime::class, $order->getDate()); 45 | $this->assertEquals('Value of email', $order->getEmail()); 46 | $this->assertEquals('Value of emoneytype', $order->getEMoneyType()); 47 | $this->assertEquals(['foo' => 'bar', 'baz' => 'bue'], $order->getEOrderData()); 48 | $this->assertEquals('Value of error_code', $order->getErrorCode()); 49 | $this->assertEquals('Value of error_comment', $order->getErrorComment()); 50 | $this->assertEquals('Value of firstname', $order->getFirstName()); 51 | $this->assertEquals('Value of gds_payment_purpose_id', $order->getGdsPaymentPurposeId()); 52 | $this->assertEquals('Value of idata', $order->getIData()); 53 | $this->assertEquals('Value of ipaddress', $order->getIp()); 54 | $this->assertEquals('Value of lastname', $order->getLastName()); 55 | $this->assertEquals('Value of loan_id', $order->getLoanId()); 56 | $this->assertEquals('Value of message', $order->getMessage()); 57 | $this->assertEquals('Value of middlename', $order->getMiddleName()); 58 | $this->assertEquals(true, $order->isNeedConfirm()); 59 | $this->assertEquals('Value of ordernumber', $order->getOrderNumber()); 60 | $this->assertEquals('Value of parent_order_number', $order->getParentOrderNumber()); 61 | $this->assertEquals('Value of paymenttype', $order->getPaymentType()); 62 | $this->assertEquals('Value of phone', $order->getPhone()); 63 | $this->assertEquals('Value of pt_code', $order->getPtCode()); 64 | $this->assertEquals('Value of recommendation', $order->getRecommendation()); 65 | $this->assertEquals('Value of response_code', $order->getResponseCode()); 66 | $this->assertEquals(Status::PAID, $order->getStatus()); 67 | $this->assertEquals('Value of total', $order->getTotal()); 68 | $this->assertInstanceOf(\DateTime::class, $order->getPacketDate()); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /tests/Results/ResultsRequestTest.php: -------------------------------------------------------------------------------- 1 | createMock(HttpManagerInterface::class); 22 | $manager 23 | ->expects($this->once()) 24 | ->method('request') 25 | ->willReturn($this->getStubContents('cancel')); 26 | 27 | $request = new ResultsRequest(); 28 | /** @var \Tmconsulting\Uniteller\Order\Order $order */ 29 | foreach ($request->execute($manager) as $order) { 30 | $this->assertInstanceOf(Order::class, $order); 31 | $this->assertEquals('Value of address', $order->getAddress()); 32 | $this->assertEquals('Value of approvalcode', $order->getApprovalCode()); 33 | $this->assertEquals('Value of bankname', $order->getBankName()); 34 | $this->assertEquals('Value of billnumber', $order->getBillNumber()); 35 | $this->assertEquals('Value of bookingcom_id', $order->getBookingcomId()); 36 | $this->assertEquals('Value of bookingcom_pincode', $order->getBookingcomPincode()); 37 | $this->assertEquals('Value of card_idp', $order->getCardIdp()); 38 | $this->assertEquals('Value of cardholder', $order->getCardHolder()); 39 | $this->assertEquals('Value of cardnumber', $order->getCardNumber()); 40 | $this->assertEquals('Value of cardtype', $order->getCardType()); 41 | $this->assertEquals('Value of comment', $order->getComment()); 42 | $this->assertEquals('Value of currency', $order->getCurrency()); 43 | $this->assertEquals(true, $order->isCvc2()); 44 | $this->assertInstanceOf(\DateTime::class, $order->getDate()); 45 | $this->assertEquals('Value of email', $order->getEmail()); 46 | $this->assertEquals('Value of emoneytype', $order->getEMoneyType()); 47 | $this->assertEquals(['foo' => 'bar', 'baz' => 'bue'], $order->getEOrderData()); 48 | $this->assertEquals('Value of error_code', $order->getErrorCode()); 49 | $this->assertEquals('Value of error_comment', $order->getErrorComment()); 50 | $this->assertEquals('Value of firstname', $order->getFirstName()); 51 | $this->assertEquals('Value of gds_payment_purpose_id', $order->getGdsPaymentPurposeId()); 52 | $this->assertEquals('Value of idata', $order->getIData()); 53 | $this->assertEquals('Value of ipaddress', $order->getIp()); 54 | $this->assertEquals('Value of lastname', $order->getLastName()); 55 | $this->assertEquals('Value of loan_id', $order->getLoanId()); 56 | $this->assertEquals('Value of message', $order->getMessage()); 57 | $this->assertEquals('Value of middlename', $order->getMiddleName()); 58 | $this->assertEquals(true, $order->isNeedConfirm()); 59 | $this->assertEquals('Value of ordernumber', $order->getOrderNumber()); 60 | $this->assertEquals('Value of parent_order_number', $order->getParentOrderNumber()); 61 | $this->assertEquals('Value of paymenttype', $order->getPaymentType()); 62 | $this->assertEquals('Value of phone', $order->getPhone()); 63 | $this->assertEquals('Value of pt_code', $order->getPtCode()); 64 | $this->assertEquals('Value of recommendation', $order->getRecommendation()); 65 | $this->assertEquals('Value of response_code', $order->getResponseCode()); 66 | $this->assertEquals(Status::PAID, $order->getStatus()); 67 | $this->assertEquals('Value of total', $order->getTotal()); 68 | $this->assertInstanceOf(\DateTime::class, $order->getPacketDate()); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: [push, workflow_dispatch, pull_request] 4 | 5 | jobs: 6 | php7234: 7 | name: Run tests PHP 7.2.34 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout repository 11 | uses: actions/checkout@v4 12 | 13 | - name: Composer install 14 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-7.2.34-cli-alpine3.12 composer install 15 | 16 | - name: Run tests 17 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-7.2.34-cli-alpine3.12 vendor/bin/phpunit --do-not-cache-result 18 | php7333: 19 | name: Run tests PHP 7.3.33 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v4 24 | 25 | - name: Composer install 26 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-7.3.33-cli-alpine3.15 composer install 27 | 28 | - name: Run tests 29 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-7.3.33-cli-alpine3.15 vendor/bin/phpunit --do-not-cache-result 30 | php7433: 31 | name: Run tests PHP 7.4.33 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v4 36 | 37 | - name: Composer install 38 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-7.4.33-cli-alpine3.16 composer install 39 | 40 | - name: Run tests 41 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-7.4.33-cli-alpine3.16 vendor/bin/phpunit --do-not-cache-result 42 | php8030: 43 | name: Run tests PHP 8.0.30 44 | runs-on: ubuntu-latest 45 | steps: 46 | - name: Checkout repository 47 | uses: actions/checkout@v4 48 | 49 | - name: Composer install 50 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-8.0.30-cli-alpine3.16 composer install 51 | 52 | - name: Run tests 53 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-8.0.30-cli-alpine3.16 vendor/bin/phpunit --do-not-cache-result 54 | php8131: 55 | name: Run tests PHP 8.1.31 56 | runs-on: ubuntu-latest 57 | steps: 58 | - name: Checkout repository 59 | uses: actions/checkout@v4 60 | 61 | - name: Composer install 62 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-8.1.31-cli-alpine3.21 composer install 63 | 64 | - name: Run tests 65 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-8.1.31-cli-alpine3.21 vendor/bin/phpunit --do-not-cache-result 66 | php8227: 67 | name: Run tests PHP 8.2.27 68 | runs-on: ubuntu-latest 69 | steps: 70 | - name: Checkout repository 71 | uses: actions/checkout@v4 72 | 73 | - name: Composer install 74 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-8.2.27-cli-alpine3.21 composer install 75 | 76 | - name: Run tests 77 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-8.2.27-cli-alpine3.21 vendor/bin/phpunit --do-not-cache-result 78 | php8316: 79 | name: Run tests PHP 8.3.16 80 | runs-on: ubuntu-latest 81 | steps: 82 | - name: Checkout repository 83 | uses: actions/checkout@v4 84 | 85 | - name: Composer install 86 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-8.3.16-cli-alpine3.21 composer install 87 | 88 | - name: Run tests 89 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-8.3.16-cli-alpine3.21 vendor/bin/phpunit --do-not-cache-result 90 | php8403: 91 | name: Run tests PHP 8.4.3 92 | runs-on: ubuntu-latest 93 | steps: 94 | - name: Checkout repository 95 | uses: actions/checkout@v4 96 | 97 | - name: Composer install 98 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-8.4.3-cli-alpine3.21 composer install 99 | 100 | - name: Run tests 101 | run: docker run --rm -v $(pwd):/home/www-data/application i3bepb/php-for-test:1.2.6-php-8.4.3-cli-alpine3.21 vendor/bin/phpunit --do-not-cache-result 102 | -------------------------------------------------------------------------------- /src/Recurrent/RecurrentBuilder.php: -------------------------------------------------------------------------------- 1 | shopIdp = $shopIdp; 73 | 74 | return $this; 75 | } 76 | 77 | /** 78 | * @param string $orderIdp 79 | * @return RecurrentBuilder 80 | */ 81 | public function setOrderIdp($orderIdp) 82 | { 83 | $this->orderIdp = $orderIdp; 84 | 85 | return $this; 86 | } 87 | 88 | /** 89 | * @param float|string $subtotalP 90 | * @return RecurrentBuilder 91 | */ 92 | public function setSubtotalP($subtotalP) 93 | { 94 | $this->subtotalP = $subtotalP; 95 | 96 | return $this; 97 | } 98 | 99 | /** 100 | * @param string $parentOrderIdp 101 | * @return RecurrentBuilder 102 | */ 103 | public function setParentOrderIdp($parentOrderIdp) 104 | { 105 | $this->parentOrderIdp = $parentOrderIdp; 106 | 107 | return $this; 108 | } 109 | 110 | /** 111 | * @param string $parentShopIdp 112 | * @return RecurrentBuilder 113 | */ 114 | public function setParentShopIdp($parentShopIdp) 115 | { 116 | $this->parentShopIdp = $parentShopIdp; 117 | 118 | return $this; 119 | } 120 | 121 | /** 122 | * @return string 123 | */ 124 | public function getShopIdp() 125 | { 126 | return $this->shopIdp; 127 | } 128 | 129 | /** 130 | * @return string 131 | */ 132 | public function getOrderIdp() 133 | { 134 | return $this->orderIdp; 135 | } 136 | 137 | /** 138 | * @return float|string 139 | */ 140 | public function getSubtotalP() 141 | { 142 | return $this->subtotalP; 143 | } 144 | 145 | /** 146 | * @return string 147 | */ 148 | public function getParentOrderIdp() 149 | { 150 | return $this->parentOrderIdp; 151 | } 152 | 153 | /** 154 | * @return string 155 | */ 156 | public function getParentShopIdp() 157 | { 158 | return $this->parentShopIdp; 159 | } 160 | 161 | /** 162 | * @return array 163 | */ 164 | public function toArray() 165 | { 166 | return [ 167 | 'Shop_IDP' => $this->getShopIdp(), 168 | 'Parent_Shop_IDP' => $this->getParentShopIdp(), 169 | 'Order_IDP' => $this->getOrderIdp(), 170 | 'Parent_Order_IDP' => $this->getParentOrderIdp(), 171 | 'Subtotal_P' => $this->getSubtotalP(), 172 | ]; 173 | } 174 | } -------------------------------------------------------------------------------- /src/Http/HttpManager.php: -------------------------------------------------------------------------------- 1 | httpClient = $httpClient; 50 | $this->options = $options; 51 | } 52 | 53 | protected function getDefaultHeaders(string $format): array 54 | { 55 | $headers = [ 56 | 'Content-Type' => 'application/x-www-form-urlencoded', 57 | ]; 58 | 59 | switch ($format) { 60 | case 'xml': 61 | $headers['Accept'] = 'application/xml'; 62 | break; 63 | case 'csv': 64 | $headers['Accept'] = 'text/csv'; 65 | break; 66 | case 'json': 67 | $headers['Accept'] = 'application/json'; 68 | break; 69 | } 70 | 71 | return $headers; 72 | } 73 | 74 | /** 75 | * @param $uri 76 | * @param string $method 77 | * @param null $data 78 | * @param array $headers 79 | * @param string $format 80 | * @return string 81 | * @throws ErrorException|ClientExceptionInterface 82 | */ 83 | public function request($uri, $method = 'POST', $data = null, array $headers = [], string $format = 'xml'): string 84 | { 85 | $uri = sprintf('%s/%s?%s', $this->options['base_uri'], $uri, http_build_query([ 86 | 'Shop_ID' => $this->options['shop_id'], 87 | 'Login' => $this->options['login'], 88 | 'Password' => $this->options['password'], 89 | 'Format' => Format::{"resolve$format"}($uri), 90 | ])); 91 | 92 | $request = new Request($method, $uri, array_merge($this->getDefaultHeaders($format), $headers), $data); 93 | 94 | try { 95 | $response = $this->httpClient->sendRequest($request); 96 | } catch (RequestException $e) { 97 | throw UnitellerException::create($request, $e->getResponse()); 98 | } 99 | 100 | if ($response->getStatusCode() < 200 || $response->getStatusCode() > 299) { 101 | throw UnitellerException::create($request, $response); 102 | } 103 | 104 | $body = (string) $response->getBody(); 105 | 106 | $this->providerError($body, $request, $response, $format); 107 | 108 | return $body; 109 | } 110 | 111 | /** 112 | * @param $body 113 | * @param RequestInterface $request 114 | * @param ResponseInterface $response 115 | * @param string $format 116 | * @throws ErrorException 117 | */ 118 | protected function providerError($body, RequestInterface $request, ResponseInterface $response, string $format = 'xml'): void 119 | { 120 | if ($format === 'csv') { 121 | $data = csv_to_array($body, true); 122 | 123 | if (isset($data['ErrorMessage'])) { 124 | throw ExceptionFactory::createFromMessage( 125 | $data['ErrorMessage'], $request, $response 126 | ); 127 | } 128 | 129 | return; 130 | } 131 | 132 | if ($format === 'xml') { 133 | if (substr($body, 0, 6) === 'ERROR:') { 134 | throw ExceptionFactory::createFromMessage( 135 | substr($body, 7), $request, $response 136 | ); 137 | } 138 | 139 | try { 140 | $xml = new SimpleXMLElement((string) $body); 141 | } catch (Throwable $e) { 142 | throw new UnitellerException('XML parsing failed', $request, $response, $e); 143 | } 144 | 145 | $firstCode = (int) $xml->attributes()['firstcode']; 146 | 147 | if ($firstCode === 0) { 148 | return; 149 | } 150 | 151 | $secondCode = (string) $xml->attributes()['secondcode']; 152 | 153 | throw ExceptionFactory::create( 154 | $firstCode, $secondCode, $request, $response 155 | ); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Uniteller PHP SDK 2 | 3 |
4 | 5 |

6 | Uniteller 7 |

8 | 9 |
10 | 11 |

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |

22 | 23 |
24 | 25 | PHP (7.2+) SDK for integration internet-acquiring of the Uniteller (unofficial). 26 | [This documentation is available in Russian language](README_RU.md). 27 | Also, this SDK integrated with [Payum](https://github.com/Payum/Payum) library and you can use [gateway](https://github.com/tmconsulting/payum-uniteller-gateway). 28 | 29 | Features: 30 | * payment (method `pay`) 31 | * recurrent (method `recurrent`) 32 | * cancel (method `unblock`) 33 | * receive results 34 | * callback (method for verify incoming signature) 35 | * general error handler for any request 36 | * general statuses (In the requests/responses may to meet `canceled` or `cancelled` variants. They will be converted to general status like as `cancelled`.) 37 | 38 | TODO: 39 | * translate to English comments and system (error) messages 40 | * validation 41 | * implement method `card` 42 | * implement method `confirm` 43 | 44 | ## Install 45 | 46 | For install package follow this command: 47 | 48 | `composer require tmconsulting/uniteller-php-sdk` 49 | 50 | ## Usage 51 | 52 | A few usage example the current SDK your can found on the `examples` folder. 53 | Just follow instruction on `README.md` file. 54 | 55 | ### Configure credentials 56 | 57 | ```php 58 | setShopId('you_shop_id'); 61 | $uniteller->setLogin('you_login_number'); 62 | $uniteller->setPassword('you_password'); 63 | $uniteller->setBaseUri('https://wpay.uniteller.ru'); 64 | ``` 65 | 66 | ### Redirect to page payment 67 | 68 | So, for redirect to page your enough to run `payment` method with parameters like as: 69 | 70 | ```php 71 | setOrderIdp(mt_rand(10000, 99999)) 77 | ->setSubtotalP(10) 78 | ->setCustomerIdp(mt_rand(10000, 99999)) 79 | ->setUrlReturnOk('http://google.ru/?q=success') 80 | ->setUrlReturnNo('http://google.ru/?q=failure'); 81 | 82 | $uniteller->payment($builder)->go(); 83 | // if you don't need redirect 84 | // $uniteller->payment($builder)->getUri(); 85 | 86 | ``` 87 | 88 | or use plain array 89 | 90 | ```php 91 | payment([ 93 | 'Order_IDP' => mt_rand(10000, 99999), 94 | // ... other parameters 95 | ])->go(); 96 | ``` 97 | 98 | ### Recurrent payment 99 | 100 | ```php 101 | setOrderIdp(mt_rand(10000, 99999)) 106 | ->setSubtotalP(15) 107 | ->setParentOrderIdp(00000) // order id of any past payment 108 | ->setParentShopIdp($uniteller->getShopId()); // optional 109 | 110 | $results = $uniteller->recurrent($builder); 111 | ``` 112 | 113 | or use plain array 114 | 115 | ```php 116 | recurrent([ 118 | 'Order_IDP' => mt_rand(10000, 99999), 119 | // ... other parameters 120 | ]); 121 | ``` 122 | 123 | ### Cancel payment 124 | 125 | ```php 126 | setBillNumber('RRN Number, (12 digits)'); 130 | $results = $uniteller->cancel($builder); 131 | ``` 132 | 133 | or 134 | 135 | ```php 136 | cancel([ 140 | 'Billnumber' => 'RRN Number, (12 digits)', 141 | // ... 142 | ]); 143 | 144 | foreach ($results as $payment) { 145 | // see Tmconsulting\Uniteller\Order\Order for other methods. 146 | if ($payment->getStatus() === Status::CANCELLED) { 147 | // payment was cancelled 148 | } 149 | } 150 | ``` 151 | 152 | ### Receive results 153 | 154 | ```php 155 | results([ 158 | 'ShopOrderNumber' => 'Order_IDP number' 159 | ]); 160 | 161 | var_dump($results); 162 | 163 | // $results[0]->getCardNumber(); 164 | ``` 165 | 166 | ### Callback (gateway notification) 167 | 168 | Receive incoming parameters from gateway and verifying signature. 169 | 170 | ```php 171 | verifyCallbackRequest(['all_parameters_from_post_with_signature'])) { 173 | return 'invalid_signature'; 174 | } 175 | ``` 176 | 177 | ## Tests 178 | 179 | `vendor/bin/phpunit` 180 | 181 | ## License 182 | 183 | MIT. 184 | -------------------------------------------------------------------------------- /README_RU.md: -------------------------------------------------------------------------------- 1 | # Uniteller PHP SDK 2 | 3 |
4 | 5 |

6 | 7 |

8 | 9 |
10 |
11 | 12 |

13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |

23 | 24 |
25 | 26 | PHP (7.2+) SDK для интеграции с интернет-эквайрингом от Uniteller (не официальная). 27 | Также, SDK интегрирован с библиотекой [Payum](https://github.com/Payum/Payum) и вы можете использовать этот [шлюз]((https://github.com/tmconsulting/payum-uniteller-gateway)) для работы. 28 | 29 | Реализовано: 30 | * оплата (метод `pay`) 31 | * рекуррентные платежи (метод `recurrent`) 32 | * отмена (метод `unblock`) 33 | * получение результатов 34 | * callback (проверка сигнатуры) 35 | * обработчик ошибок, кидает эксепшены даже на строку `ERROR: %s` в теле ответа на запрос 36 | * единство статусов. 37 | 38 | Что осталось: 39 | * прикрутить валидацию, используя декораторы 40 | * добавить билдер для метода `results` 41 | * метод `card` 42 | * метод `confirm` 43 | 44 | ## Установка 45 | 46 | Чтобы установить пакет, достаточно подключить его в проект, как зависимость: 47 | 48 | `composer require tmconsulting/uniteller-php-sdk` 49 | 50 | ## Использование 51 | 52 | Примеры использования SDK лежат в папке `./examples`, а так-же `README.md` файл, 53 | в котором написан способ установки. 54 | 55 | ### Установка учетных данных 56 | 57 | ```php 58 | setShopId('you_shop_id'); 61 | $uniteller->setLogin('you_login_number'); 62 | $uniteller->setPassword('you_password'); 63 | $uniteller->setBaseUri('https://wpay.uniteller.ru'); 64 | ``` 65 | 66 | ### Переход к оплате 67 | 68 | Чтобы произвести оплату, достаточно вызвать метод `payment`, 69 | он принимает первым аргументом либо построитель запроса, либо обычный массив параметров. 70 | 71 | ```php 72 | setOrderIdp(mt_rand(10000, 99999)) 78 | ->setSubtotalP(10) 79 | ->setCustomerIdp(mt_rand(10000, 99999)) 80 | ->setUrlReturnOk('http://google.ru/?q=success') 81 | ->setUrlReturnNo('http://google.ru/?q=failure'); 82 | 83 | $uniteller->payment($builder)->go(); 84 | // Если переходить к оплате сразу нет необходимости, 85 | // можно получить готовую ссылку для оплаты 86 | // $uniteller->payment($builder)->getUri(); 87 | 88 | ``` 89 | 90 | или 91 | 92 | ```php 93 | payment([ 95 | 'Order_IDP' => mt_rand(10000, 99999), 96 | // ... прочие параметры 97 | ])->go(); 98 | ``` 99 | 100 | ### Рекуррентный платеж 101 | 102 | ```php 103 | setOrderIdp(mt_rand(10000, 99999)) 108 | ->setSubtotalP(15) 109 | ->setParentOrderIdp(00000) // id заказа магазина из ранее оплаченных в uniteller 110 | ->setParentShopIdp($uniteller->getShopId()); // не обязательно задавать, если родительский платеж из того же магазина 111 | 112 | $results = $uniteller->recurrent($builder); 113 | ``` 114 | 115 | или 116 | 117 | ```php 118 | recurrent([ 120 | 'Order_IDP' => mt_rand(10000, 99999), 121 | // ... 122 | ]); 123 | ``` 124 | 125 | ### Отмена платежа 126 | 127 | ```php 128 | setBillNumber('RRN Number, (12 digits)'); 132 | $results = $uniteller->cancel($builder); 133 | ``` 134 | 135 | или 136 | 137 | ```php 138 | cancel([ 142 | 'Billnumber' => 'RRN Number, (12 digits)', 143 | // ... 144 | ]); 145 | 146 | var_dump($results); 147 | 148 | foreach ($results as $payment) { 149 | // смотрите в Tmconsulting\Uniteller\Order\Order остальные методы. 150 | if ($payment->getStatus() === Status::CANCELLED) { 151 | // платеж отменён 152 | } 153 | } 154 | ``` 155 | 156 | ### Получение результатов 157 | 158 | ```php 159 | results([ 162 | 'ShopOrderNumber' => 'Order_IDP number' 163 | ]); 164 | 165 | var_dump($results); 166 | 167 | // $results[0]->getCardNumber(); 168 | ``` 169 | 170 | ### Callback 171 | 172 | Проверка сигнатуры при приёме данных от шлюза. 173 | 174 | ```php 175 | verifyCallbackRequest(['all_parameters_from_post_with_signature'])) { 177 | return 'invalid_signature'; 178 | } 179 | ``` 180 | 181 | 182 | ## Тесты 183 | 184 | `vendor/bin/phpunit` 185 | 186 | ## Лицензия 187 | 188 | MIT. 189 | -------------------------------------------------------------------------------- /src/Signature/SignaturePayment.php: -------------------------------------------------------------------------------- 1 | shopIdp = $shopIdp; 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * @param string $orderIdp 86 | * @return SignaturePayment 87 | */ 88 | public function setOrderIdp($orderIdp) 89 | { 90 | $this->orderIdp = $orderIdp; 91 | 92 | return $this; 93 | } 94 | 95 | /** 96 | * @param string $subtotalP 97 | * @return SignaturePayment 98 | */ 99 | public function setSubtotalP($subtotalP) 100 | { 101 | $this->subtotalP = $subtotalP; 102 | 103 | return $this; 104 | } 105 | 106 | /** 107 | * @param string $meanType 108 | * @return SignaturePayment 109 | */ 110 | public function setMeanType($meanType) 111 | { 112 | $this->meanType = $meanType; 113 | 114 | return $this; 115 | } 116 | 117 | /** 118 | * @param string $eMoneyType 119 | * @return SignaturePayment 120 | */ 121 | public function setEMoneyType($eMoneyType) 122 | { 123 | $this->eMoneyType = $eMoneyType; 124 | 125 | return $this; 126 | } 127 | 128 | /** 129 | * @param string $lifeTime 130 | * @return SignaturePayment 131 | */ 132 | public function setLifeTime($lifeTime) 133 | { 134 | $this->lifeTime = $lifeTime; 135 | 136 | return $this; 137 | } 138 | 139 | /** 140 | * @param string $customerIdp 141 | * @return SignaturePayment 142 | */ 143 | public function setCustomerIdp($customerIdp) 144 | { 145 | $this->customerIdp = $customerIdp; 146 | 147 | return $this; 148 | } 149 | 150 | /** 151 | * @param string $cardIdp 152 | * @return SignaturePayment 153 | */ 154 | public function setCardIdp($cardIdp) 155 | { 156 | $this->cardIdp = $cardIdp; 157 | 158 | return $this; 159 | } 160 | 161 | /** 162 | * @param string $iData 163 | * @return SignaturePayment 164 | */ 165 | public function setIData($iData) 166 | { 167 | $this->iData = $iData; 168 | 169 | return $this; 170 | } 171 | 172 | /** 173 | * @param string $ptCode 174 | * @return SignaturePayment 175 | */ 176 | public function setPtCode($ptCode) 177 | { 178 | $this->ptCode = $ptCode; 179 | 180 | return $this; 181 | } 182 | 183 | /** 184 | * @param string $password 185 | * @return SignaturePayment 186 | */ 187 | public function setPassword($password) 188 | { 189 | $this->password = $password; 190 | 191 | return $this; 192 | } 193 | 194 | /** 195 | * @return string 196 | */ 197 | public function getShopIdp() 198 | { 199 | return $this->shopIdp; 200 | } 201 | 202 | /** 203 | * @return string 204 | */ 205 | public function getOrderIdp() 206 | { 207 | return $this->orderIdp; 208 | } 209 | 210 | /** 211 | * @return string 212 | */ 213 | public function getSubtotalP() 214 | { 215 | return $this->subtotalP; 216 | } 217 | 218 | /** 219 | * @return string 220 | */ 221 | public function getMeanType() 222 | { 223 | return $this->meanType; 224 | } 225 | 226 | /** 227 | * @return string 228 | */ 229 | public function getEMoneyType() 230 | { 231 | return $this->eMoneyType; 232 | } 233 | 234 | /** 235 | * @return string 236 | */ 237 | public function getLifeTime() 238 | { 239 | return $this->lifeTime; 240 | } 241 | 242 | /** 243 | * @return string 244 | */ 245 | public function getCustomerIdp() 246 | { 247 | return $this->customerIdp; 248 | } 249 | 250 | /** 251 | * @return string 252 | */ 253 | public function getCardIdp() 254 | { 255 | return $this->cardIdp; 256 | } 257 | 258 | /** 259 | * @return string 260 | */ 261 | public function getIData() 262 | { 263 | return $this->iData; 264 | } 265 | 266 | /** 267 | * @return string 268 | */ 269 | public function getPtCode() 270 | { 271 | return $this->ptCode; 272 | } 273 | 274 | /** 275 | * @return string 276 | */ 277 | public function getPassword() 278 | { 279 | return $this->password; 280 | } 281 | 282 | /** 283 | * @return array 284 | */ 285 | public function toArray() 286 | { 287 | $array = []; 288 | $array['Shop_IDP'] = $this->getShopIdp(); 289 | $array['Order_IDP'] = $this->getOrderIdp(); 290 | $array['Subtotal_P'] = $this->getSubtotalP(); 291 | $array['MeanType'] = $this->getMeanType(); 292 | $array['EMoneyType'] = $this->getEMoneyType(); 293 | $array['Lifetime'] = $this->getLifeTime(); 294 | $array['Customer_IDP'] = $this->getCustomerIdp(); 295 | $array['Card_IDP'] = $this->getCardIdp(); 296 | $array['IData'] = $this->getIData(); 297 | $array['PT_Code'] = $this->getPtCode(); 298 | $array['Password'] = $this->getPassword(); 299 | 300 | return $array; 301 | } 302 | } -------------------------------------------------------------------------------- /tests/ClientTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(ClientInterface::class, $client); 27 | } 28 | 29 | public function testOptionsAccessorsAndMutators() 30 | { 31 | $uniteller = new Client(); 32 | $uniteller->setShopId('shop_id'); 33 | $uniteller->setBaseUri('https://google.com'); 34 | $uniteller->setPassword('security-long-password'); 35 | $uniteller->setLogin(330011); 36 | 37 | $this->assertSame('shop_id', $uniteller->getShopId()); 38 | $this->assertSame('https://google.com', $uniteller->getBaseUri()); 39 | $this->assertSame('security-long-password', $uniteller->getPassword()); 40 | $this->assertSame(330011, $uniteller->getLogin()); 41 | } 42 | 43 | public function testOptionKeyResolver() 44 | { 45 | $uniteller = new Client(); 46 | $this->assertSame('default', $uniteller->getOption('shop_id', 'default')); 47 | } 48 | 49 | public function testDefaultObjectsIsRegistered() 50 | { 51 | $uniteller = new Client(); 52 | $this->assertInstanceOf(PaymentInterface::class, $uniteller->getPayment()); 53 | $this->assertInstanceOf(SignatureInterface::class, $uniteller->getSignaturePayment()); 54 | $this->assertInstanceOf(SignatureInterface::class, $uniteller->getSignatureRecurrent()); 55 | } 56 | 57 | public function testSetOptionsUseArrayNotation() 58 | { 59 | $uniteller = new Client(); 60 | $uniteller->setShopId('111'); 61 | $uniteller->setPassword('pwd'); 62 | 63 | $uniteller->setOptions([ 64 | 'password' => 1234, 65 | 'base_uri' => 'https://google.com' 66 | ]); 67 | 68 | $true = [ 69 | 'shop_id' => '111', 70 | 'password' => 1234, 71 | 'base_uri' => 'https://google.com', 72 | ]; 73 | 74 | $this->assertSame($true, $uniteller->getOptions()); 75 | } 76 | 77 | // public function testGivenArgumentShouldBeImplementHttpManagerInterface() 78 | // { 79 | // $part1 = 'Argument 1 passed to ' . Client::class . '::setHttpManager()'; 80 | // $part2 = HttpManagerInterface::class; 81 | // 82 | // $client = new Client(); 83 | // try { 84 | // $client->setHttpManager(new \stdClass()); 85 | // } catch (\Throwable $e) { // PHP 7 86 | // $this->assertStringStartsWith($part1, $e->getMessage()); 87 | // $this->assertContains($part2, $e->getMessage()); 88 | // } 89 | // 90 | // $client->setHttpManager($this->createMock(HttpManagerInterface::class)); 91 | // $this->assertInstanceOf(HttpManagerInterface::class, $client->getHttpManager()); 92 | // } 93 | 94 | /** 95 | * @return array 96 | */ 97 | public function provideMethodsWhenHaveAnFirstArgumentIsRequestInterface() 98 | { 99 | return [ 100 | ['registerCancelRequest'], 101 | ['registerResultsRequest'], 102 | ]; 103 | } 104 | 105 | /** 106 | * @dataProvider provideMethodsWhenHaveAnFirstArgumentIsRequestInterface 107 | * @param $methodName 108 | */ 109 | // public function testGivenArgumentShouldBeTypeHintedAsRequestInterface($methodName) 110 | // { 111 | // $reflect = new \ReflectionClass(Client::class); 112 | // $first = $reflect->getMethod($methodName)->getParameters()[0]->getClass()->getName(); 113 | // 114 | // $this->assertEquals(RequestInterface::class, $first); 115 | // } 116 | 117 | public function testCanRequestMethodsMayReturnCorrectResult() 118 | { 119 | $client = new Client(); 120 | $client->registerCancelRequest($this->createMock(RequestInterface::class)); 121 | $client->registerResultsRequest($this->createMock(RequestInterface::class)); 122 | $client->registerRecurrentRequest($this->createMock(RequestInterface::class)); 123 | 124 | $this->assertInstanceOf(RequestInterface::class, $client->getCancelRequest()); 125 | $this->assertInstanceOf(RequestInterface::class, $client->getResultsRequest()); 126 | $this->assertInstanceOf(RequestInterface::class, $client->getRecurrentRequest()); 127 | } 128 | 129 | public function testShouldBePaymentMethodBuildCorrectArray() 130 | { 131 | $payment = $this->createMock(PaymentInterface::class); 132 | $payment 133 | ->expects($this->once()) 134 | ->method('execute') 135 | ->willReturnCallback(function ($array) { 136 | $this->assertArrayHasKey('Shop_IDP', $array); 137 | $this->assertArrayHasKey('Signature', $array); 138 | }); 139 | 140 | $client = new Client(); 141 | $client->registerPayment($payment); 142 | 143 | $client->payment([]); 144 | } 145 | 146 | public function testCanCancelMethodGiveControlToTheRequestClass() 147 | { 148 | $request = $this->createMock(RequestInterface::class); 149 | $request 150 | ->expects($this->at(1)) 151 | ->method('execute') 152 | ->willReturnCallback(function ($manager, $array) { 153 | $this->assertInstanceOf(HttpManagerInterface::class, $manager); 154 | $this->assertTrue(is_array($array)); 155 | }); 156 | 157 | $client = new Client(); 158 | $client->registerCancelRequest($request); 159 | $client->registerResultsRequest($request); 160 | $client->registerRecurrentRequest($request); 161 | 162 | $client->cancel([]); 163 | $client->results([]); 164 | 165 | $this->assertInstanceOf(HttpManagerInterface::class, $client->getHttpManager()); 166 | } 167 | 168 | public function testCanUseCustomHttpManager() 169 | { 170 | $cancel = $this->createMock(CancelRequest::class); 171 | $cancel 172 | ->expects($this->once()) 173 | ->method('execute'); 174 | 175 | $client = new Client(); 176 | $client->setHttpManager(new HttpManagerStub()); 177 | $client->registerCancelRequest($cancel); 178 | 179 | $client->cancel([]); 180 | 181 | $this->assertInstanceOf(HttpManagerStub::class, $client->getHttpManager()); 182 | } 183 | 184 | /** 185 | * @return array 186 | */ 187 | public function provideUnsupportedRequestMethods() 188 | { 189 | return [ 190 | ['confirm'], 191 | ['card'], 192 | ]; 193 | } 194 | 195 | /** 196 | * @dataProvider provideUnsupportedRequestMethods 197 | * @param $methodName 198 | */ 199 | public function testShouldBeUnsupportedMethodsThrowException($methodName) 200 | { 201 | $this->expectException(NotImplementedException::class); 202 | $this->expectExceptionMessageMatches('/In current moment, feature \[.*\] not implemented./'); 203 | 204 | $client = new Client(); 205 | $client->{$methodName}([]); 206 | } 207 | 208 | // public function provideActionsWhichShouldBeAcceptArrayble(): array 209 | // { 210 | // return [ 211 | // ['payment'], 212 | // ['cancel'], 213 | // ['results'], 214 | // ]; 215 | // } 216 | // 217 | // /** 218 | // * @dataProvider provideActionsWhichShouldBeAcceptArrayble 219 | // */ 220 | // public function testShouldBeActionsAcceptClassesWhichImplementArraybleInterface(string $methodName): void 221 | // { 222 | // $request = $this->createMock(RequestInterface::class); 223 | // $request->method('execute'); 224 | // 225 | // $client = new Client(); 226 | // $client->registerResultsRequest($request); 227 | // $client->registerCancelRequest($request); 228 | // $client->registerRecurrentRequest($request); 229 | // $client->registerPayment($this->createMock(PaymentInterface::class)); 230 | // 231 | // $arrayble = $this->createMock(ArraybleInterface::class); 232 | // $arrayble 233 | // ->method('toArray') 234 | // ->willReturn([]); 235 | // 236 | // $client->{$methodName}($arrayble); 237 | // } 238 | 239 | public function testCallbackSignatureVerifying() 240 | { 241 | $params = [ 242 | 'Order_ID' => 'FOO', 243 | 'Status' => 'paid', 244 | 'Signature' => '3F728AA479E50F5B10EE6C20258BFF88', 245 | ]; 246 | $client = new Client(); 247 | $client->setPassword('LONG-PWD'); 248 | $this->assertTrue($client->verifyCallbackRequest($params)); 249 | } 250 | 251 | public function testCallbackSignatureVerifyingWithFields() 252 | { 253 | $params = [ 254 | 'Order_ID' => 'FOO', 255 | 'Status' => 'paid', 256 | 'AcquirerID' => 'fOO', 257 | 'ApprovalCode' => 'BaR', 258 | 'BillNumber' => 'baz', 259 | 'Signature' => '1F4E3B63AE408D0BE1E33965E6697236', 260 | ]; 261 | $client = new Client(); 262 | $client->setPassword('LONG-PWD'); 263 | $this->assertTrue($client->verifyCallbackRequest($params)); 264 | } 265 | 266 | } 267 | 268 | class HttpManagerStub implements HttpManagerInterface { 269 | 270 | /** 271 | * @param $uri 272 | * @param string $method 273 | * @param null $data 274 | * @param array $headers 275 | * @return string 276 | */ 277 | public function request($uri, $method = 'POST', $data = null, array $headers = []) 278 | { 279 | // TODO: Implement request() method. 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | registerPayment(new Payment()); 85 | $this->registerCancelRequest(new CancelRequest()); 86 | $this->registerResultsRequest(new ResultsRequest()); 87 | $this->registerRecurrentRequest(new RecurrentRequest()); 88 | $this->registerSignaturePayment(new SignaturePayment()); 89 | $this->registerSignatureRecurrent(new SignatureRecurrent()); 90 | $this->registerSignatureCallback(new SignatureCallback()); 91 | } 92 | 93 | /** 94 | * @param $uri 95 | * @return $this 96 | */ 97 | public function setBaseUri($uri) 98 | { 99 | $this->options['base_uri'] = $uri; 100 | 101 | return $this; 102 | } 103 | 104 | /** 105 | * @param $value 106 | * @return $this 107 | */ 108 | public function setLogin($value) 109 | { 110 | $this->options['login'] = $value; 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * @param $value 117 | * @return $this 118 | */ 119 | public function setPassword($value) 120 | { 121 | $this->options['password'] = $value; 122 | 123 | return $this; 124 | } 125 | 126 | /** 127 | * @param $value 128 | * @return $this 129 | */ 130 | public function setShopId($value) 131 | { 132 | $this->options['shop_id'] = $value; 133 | 134 | return $this; 135 | } 136 | 137 | /** 138 | * @param array $options 139 | * @return $this 140 | */ 141 | public function setOptions(array $options) 142 | { 143 | $this->options = array_merge($this->options, $options); 144 | 145 | return $this; 146 | } 147 | 148 | /** 149 | * @param HttpManagerInterface $httpManager 150 | * @return $this 151 | */ 152 | public function setHttpManager(HttpManagerInterface $httpManager) 153 | { 154 | $this->httpManager = $httpManager; 155 | 156 | return $this; 157 | } 158 | 159 | /** 160 | * @param PaymentInterface $payment 161 | * @return $this 162 | */ 163 | public function registerPayment(PaymentInterface $payment) 164 | { 165 | $this->payment = $payment; 166 | 167 | return $this; 168 | } 169 | 170 | /** 171 | * @param RequestInterface $cancel 172 | * @return $this 173 | */ 174 | public function registerCancelRequest(RequestInterface $cancel) 175 | { 176 | $this->cancelRequest = $cancel; 177 | 178 | return $this; 179 | } 180 | 181 | /** 182 | * @param RequestInterface $request 183 | * @return $this 184 | */ 185 | public function registerResultsRequest(RequestInterface $request) 186 | { 187 | $this->resultsRequest = $request; 188 | 189 | return $this; 190 | } 191 | 192 | /** 193 | * @param RequestInterface $request 194 | * @return $this 195 | */ 196 | public function registerRecurrentRequest(RequestInterface $request) 197 | { 198 | $this->recurrentRequest = $request; 199 | 200 | return $this; 201 | } 202 | 203 | /** 204 | * @param \Tmconsulting\Uniteller\Signature\SignatureInterface $signature 205 | * @return $this 206 | */ 207 | public function registerSignaturePayment(SignatureInterface $signature) 208 | { 209 | $this->signaturePayment = $signature; 210 | 211 | return $this; 212 | } 213 | 214 | /** 215 | * @param \Tmconsulting\Uniteller\Signature\SignatureInterface $signature 216 | * @return $this 217 | */ 218 | public function registerSignatureRecurrent(SignatureInterface $signature) 219 | { 220 | $this->signatureRecurrent = $signature; 221 | 222 | return $this; 223 | } 224 | 225 | /** 226 | * @param \Tmconsulting\Uniteller\Signature\SignatureInterface $signature 227 | * @return $this 228 | */ 229 | public function registerSignatureCallback(SignatureInterface $signature) 230 | { 231 | $this->signatureCallback = $signature; 232 | 233 | return $this; 234 | } 235 | 236 | /** 237 | * @return array 238 | */ 239 | public function getOptions() 240 | { 241 | return $this->options; 242 | } 243 | 244 | /** 245 | * @param $key 246 | * @param null $default 247 | * @return string|mixed 248 | */ 249 | public function getOption($key, $default = null) 250 | { 251 | if (array_key_exists($key, $this->options)) { 252 | return $this->options[$key]; 253 | } 254 | 255 | return $default; 256 | } 257 | 258 | /** 259 | * @return string 260 | */ 261 | public function getBaseUri() 262 | { 263 | return $this->getOption('base_uri'); 264 | } 265 | 266 | /** 267 | * @return string 268 | */ 269 | public function getLogin() 270 | { 271 | return $this->getOption('login'); 272 | } 273 | 274 | /** 275 | * @return string 276 | */ 277 | public function getShopId() 278 | { 279 | return $this->getOption('shop_id'); 280 | } 281 | 282 | /** 283 | * @return string 284 | */ 285 | public function getPassword() 286 | { 287 | return $this->getOption('password'); 288 | } 289 | 290 | /** 291 | * @return \Tmconsulting\Uniteller\Payment\PaymentInterface 292 | */ 293 | public function getPayment() 294 | { 295 | return $this->payment; 296 | } 297 | 298 | /** 299 | * @return \Tmconsulting\Uniteller\Request\RequestInterface 300 | */ 301 | public function getCancelRequest() 302 | { 303 | return $this->cancelRequest; 304 | } 305 | 306 | /** 307 | * @return \Tmconsulting\Uniteller\Request\RequestInterface 308 | */ 309 | public function getResultsRequest() 310 | { 311 | return $this->resultsRequest; 312 | } 313 | 314 | /** 315 | * @return \Tmconsulting\Uniteller\Request\RequestInterface 316 | */ 317 | public function getRecurrentRequest() 318 | { 319 | return $this->recurrentRequest; 320 | } 321 | 322 | /** 323 | * @return \Tmconsulting\Uniteller\Signature\SignatureInterface 324 | */ 325 | public function getSignaturePayment() 326 | { 327 | return $this->signaturePayment; 328 | } 329 | 330 | /** 331 | * @return \Tmconsulting\Uniteller\Signature\SignatureInterface 332 | */ 333 | public function getSignatureRecurrent() 334 | { 335 | return $this->signatureRecurrent; 336 | } 337 | 338 | /** 339 | * @return \Tmconsulting\Uniteller\Signature\SignatureInterface 340 | */ 341 | public function getSignatureCallback() 342 | { 343 | return $this->signatureCallback; 344 | } 345 | 346 | /** 347 | * @return \Tmconsulting\Uniteller\Http\HttpManagerInterface 348 | */ 349 | public function getHttpManager() 350 | { 351 | return $this->httpManager; 352 | } 353 | 354 | /** 355 | * Получение платежной ссылки или сразу переход к оплате. 356 | * 357 | * @param array $parameters|\Tmconsulting\Client\Payment\PaymentBuilder $builder 358 | * @return \Tmconsulting\Uniteller\Payment\UriInterface 359 | */ 360 | public function payment($parameters) 361 | { 362 | $array = $this->getParameters($parameters); 363 | $array['Shop_IDP'] = $this->getShopId(); 364 | $array['Signature'] = $this->signaturePayment 365 | ->setShopIdp(array_get($array, 'Shop_IDP')) 366 | ->setOrderIdp(array_get($array, 'Order_IDP')) 367 | ->setSubtotalP(array_get($array, 'Subtotal_P')) 368 | ->setMeanType(array_get($array, 'MeanType')) 369 | ->setEMoneyType(array_get($array, 'EMoneyType')) 370 | ->setLifeTime(array_get($array, 'Lifetime')) 371 | ->setCustomerIdp(array_get($array, 'Customer_IDP')) 372 | ->setCardIdp(array_get($array, 'Card_IDP')) 373 | ->setIData(array_get($array, 'IData')) 374 | ->setPtCode(array_get($array, 'PT_Code')) 375 | ->setPassword($this->getPassword()) 376 | ->create(); 377 | 378 | return $this->getPayment()->execute($array, $this->getOptions()); 379 | } 380 | 381 | /** 382 | * Отмена платежа. 383 | * 384 | * @param \Tmconsulting\Uniteller\Cancel\CancelBuilder|array $parameters 385 | * @return mixed 386 | * @internal param $builder 387 | */ 388 | public function cancel($parameters) 389 | { 390 | return $this->callRequestFor('cancel', $parameters); 391 | } 392 | 393 | /** 394 | * @param \Tmconsulting\Uniteller\Cancel\CancelBuilder|array $parameters 395 | * @return Order 396 | */ 397 | public function results($parameters) 398 | { 399 | return $this->callRequestFor('results', $parameters); 400 | } 401 | 402 | /** 403 | * @param \Tmconsulting\Uniteller\Recurrent\RecurrentBuilder|array $parameters 404 | * @return mixed 405 | * @throws \Tmconsulting\Uniteller\Exception\NotImplementedException 406 | */ 407 | public function recurrent($parameters) 408 | { 409 | $array = $this->getParameters($parameters); 410 | $array['Shop_IDP'] = $this->getShopId(); 411 | 412 | $this->signatureRecurrent 413 | ->setShopIdp(array_get($array, 'Shop_IDP')) 414 | ->setOrderIdp(array_get($array, 'Order_IDP')) 415 | ->setSubtotalP(array_get($array, 'Subtotal_P')) 416 | ->setParentOrderIdp(array_get($array, 'Parent_Order_IDP')) 417 | ->setPassword($this->getPassword()); 418 | if (array_get($array, 'Parent_Shop_IDP')) { 419 | $this->signatureRecurrent->setParentShopIdp(array_get($array, 'Parent_Shop_IDP')); 420 | } 421 | 422 | $array['Signature'] = $this->signatureRecurrent->create(); 423 | 424 | return $this->callRequestFor('recurrent', $array); 425 | } 426 | 427 | /** 428 | * @param array $parameters 429 | * @return mixed 430 | * @throws \Tmconsulting\Uniteller\Exception\NotImplementedException 431 | */ 432 | public function confirm($parameters) 433 | { 434 | throw new NotImplementedException(sprintf( 435 | 'In current moment, feature [%s] not implemented.', __METHOD__ 436 | )); 437 | } 438 | 439 | /** 440 | * @param array $parameters 441 | * @return mixed 442 | * @throws \Tmconsulting\Uniteller\Exception\NotImplementedException 443 | */ 444 | public function card($parameters) 445 | { 446 | throw new NotImplementedException(sprintf( 447 | 'In current moment, feature [%s] not implemented.', __METHOD__ 448 | )); 449 | } 450 | 451 | /** 452 | * Подгружаем собственный HttpManager с газлом в качестве клиента, если 453 | * не был задан свой, перед выполнением запроса. 454 | * 455 | * @param $name 456 | * @param $parameters 457 | * @return Order|mixed 458 | */ 459 | private function callRequestFor($name, $parameters) 460 | { 461 | if (! $this->getHttpManager()) { 462 | $httpClient = new GuzzleAdapter(new GuzzleClient()); 463 | $this->setHttpManager(new HttpManager($httpClient, $this->getOptions())); 464 | } 465 | 466 | /** @var RequestInterface $request */ 467 | $request = $this->{'get' . ucfirst($name) . 'Request'}(); 468 | 469 | return $request->execute( 470 | $this->getHttpManager(), 471 | $this->getParameters($parameters) 472 | ); 473 | } 474 | 475 | /** 476 | * @param $parameters 477 | * @return mixed 478 | */ 479 | private function getParameters($parameters) 480 | { 481 | if ($parameters instanceof ArraybleInterface) { 482 | return $parameters->toArray(); 483 | } 484 | 485 | return $parameters; 486 | } 487 | 488 | /** 489 | * Verify signature when Client will be send callback request. 490 | * 491 | * @param array $params 492 | * @return bool 493 | */ 494 | public function verifyCallbackRequest(array $params) 495 | { 496 | return $this->signatureCallback 497 | ->setOrderId(array_get($params, 'Order_ID')) 498 | ->setStatus(array_get($params, 'Status')) 499 | ->setFields(array_except($params, ['Order_ID', 'Status', 'Signature'])) 500 | ->setPassword($this->getPassword()) 501 | ->verify(array_get($params, 'Signature')); 502 | } 503 | } 504 | -------------------------------------------------------------------------------- /src/Payment/PaymentBuilder.php: -------------------------------------------------------------------------------- 1 | shopIdp = $shopIdp; 295 | 296 | return $this; 297 | } 298 | 299 | /** 300 | * @param string $orderIdp 301 | * @return $this 302 | */ 303 | public function setOrderIdp($orderIdp) 304 | { 305 | $this->orderIdp = $orderIdp; 306 | 307 | return $this; 308 | } 309 | 310 | /** 311 | * @param float|string $subtotalP 312 | * @return $this 313 | */ 314 | public function setSubtotalP($subtotalP) 315 | { 316 | $this->subtotalP = $subtotalP; 317 | 318 | return $this; 319 | } 320 | 321 | /** 322 | * @param string $signature 323 | * @return $this 324 | */ 325 | public function setSignature($signature) 326 | { 327 | $this->signature = $signature; 328 | 329 | return $this; 330 | } 331 | 332 | /** 333 | * @param string $urlReturnOk 334 | * @return $this 335 | */ 336 | public function setUrlReturnOk($urlReturnOk) 337 | { 338 | $this->urlReturnOk = $urlReturnOk; 339 | 340 | return $this; 341 | } 342 | 343 | /** 344 | * @param string $urlReturnNo 345 | * @return $this 346 | */ 347 | public function setUrlReturnNo($urlReturnNo) 348 | { 349 | $this->urlReturnNo = $urlReturnNo; 350 | 351 | return $this; 352 | } 353 | 354 | /** 355 | * @param string $currency 356 | * @return $this 357 | */ 358 | public function setCurrency($currency) 359 | { 360 | $this->currency = $currency; 361 | 362 | return $this; 363 | } 364 | 365 | /** 366 | * @param string $email 367 | * @return $this 368 | */ 369 | public function setEmail($email) 370 | { 371 | $this->email = $email; 372 | 373 | return $this; 374 | } 375 | 376 | /** 377 | * @param int $lifetime 378 | * @return $this 379 | */ 380 | public function setLifetime($lifetime) 381 | { 382 | $this->lifetime = $lifetime; 383 | 384 | return $this; 385 | } 386 | 387 | /** 388 | * @param int $orderLifetime 389 | * @return $this 390 | */ 391 | public function setOrderLifetime($orderLifetime) 392 | { 393 | $this->orderLifetime = $orderLifetime; 394 | 395 | return $this; 396 | } 397 | 398 | /** 399 | * @param string $customerIdp 400 | * @return $this 401 | */ 402 | public function setCustomerIdp($customerIdp) 403 | { 404 | $this->customerIdp = $customerIdp; 405 | 406 | return $this; 407 | } 408 | 409 | /** 410 | * @param mixed $cardIdp 411 | * @return $this 412 | */ 413 | public function setCardIdp($cardIdp) 414 | { 415 | $this->cardIdp = $cardIdp; 416 | 417 | return $this; 418 | } 419 | 420 | /** 421 | * @param string $iData 422 | * @return $this 423 | */ 424 | public function setIData($iData) 425 | { 426 | $this->iData = $iData; 427 | 428 | return $this; 429 | } 430 | 431 | /** 432 | * @param string $ptCode 433 | * @return $this 434 | */ 435 | public function setPtCode($ptCode) 436 | { 437 | $this->ptCode = $ptCode; 438 | 439 | return $this; 440 | } 441 | 442 | /** 443 | * @param int $meanType 444 | * @return $this 445 | */ 446 | public function setMeanType($meanType) 447 | { 448 | $this->meanType = $meanType; 449 | 450 | return $this; 451 | } 452 | 453 | /** 454 | * @param int $eMoneyType 455 | * @return $this 456 | */ 457 | public function setEMoneyType($eMoneyType) 458 | { 459 | $this->eMoneyType = $eMoneyType; 460 | 461 | return $this; 462 | } 463 | 464 | /** 465 | * @param int $billLifetime 466 | * @return $this 467 | */ 468 | public function setBillLifetime($billLifetime) 469 | { 470 | $this->billLifetime = $billLifetime; 471 | 472 | return $this; 473 | } 474 | 475 | /** 476 | * 477 | */ 478 | public function usePreAuth() 479 | { 480 | $this->preAuth = true; 481 | 482 | return $this; 483 | } 484 | 485 | /** 486 | * @return bool 487 | */ 488 | public function useRecurrentPayment() 489 | { 490 | $this->IsRecurrentStart = true; 491 | 492 | return $this; 493 | } 494 | 495 | /** 496 | * @param array $callbackFields 497 | * @return $this 498 | */ 499 | public function setCallbackFields(array $callbackFields) 500 | { 501 | $this->callbackFields = join(' ', $callbackFields); 502 | 503 | return $this; 504 | } 505 | 506 | /** 507 | * @param string $callbackFormat 508 | * @return $this 509 | */ 510 | public function setCallbackFormat($callbackFormat) 511 | { 512 | $this->callbackFormat = $callbackFormat; 513 | 514 | return $this; 515 | } 516 | 517 | /** 518 | * @param string $language 519 | * @return $this 520 | */ 521 | public function setLanguage($language) 522 | { 523 | $this->language = $language; 524 | 525 | return $this; 526 | } 527 | 528 | /** 529 | * @param string $comment 530 | * @return $this 531 | */ 532 | public function setComment($comment) 533 | { 534 | $this->comment = $comment; 535 | 536 | return $this; 537 | } 538 | 539 | /** 540 | * @param string $firstName 541 | * @return $this 542 | */ 543 | public function setFirstName($firstName) 544 | { 545 | $this->firstName = $firstName; 546 | 547 | return $this; 548 | } 549 | 550 | /** 551 | * @param mixed $lastName 552 | * @return $this 553 | */ 554 | public function setLastName($lastName) 555 | { 556 | $this->lastName = $lastName; 557 | 558 | return $this; 559 | } 560 | 561 | /** 562 | * @param string $middleName 563 | * @return $this 564 | */ 565 | public function setMiddleName($middleName) 566 | { 567 | $this->middleName = $middleName; 568 | 569 | return $this; 570 | } 571 | 572 | /** 573 | * @param string $phone 574 | * @return $this 575 | */ 576 | public function setPhone($phone) 577 | { 578 | $this->phone = $phone; 579 | 580 | return $this; 581 | } 582 | 583 | /** 584 | * @param string $address 585 | * @return $this 586 | */ 587 | public function setAddress($address) 588 | { 589 | $this->address = $address; 590 | 591 | return $this; 592 | } 593 | 594 | /** 595 | * @param string $country 596 | * @return $this 597 | */ 598 | public function setCountry($country) 599 | { 600 | $this->country = $country; 601 | 602 | return $this; 603 | } 604 | 605 | /** 606 | * @param string $state 607 | * @return $this 608 | */ 609 | public function setState($state) 610 | { 611 | $this->state = $state; 612 | 613 | return $this; 614 | } 615 | 616 | /** 617 | * @param string $city 618 | * @return $this 619 | */ 620 | public function setCity($city) 621 | { 622 | $this->city = $city; 623 | 624 | return $this; 625 | } 626 | 627 | /** 628 | * @param mixed $zip 629 | * @return $this 630 | */ 631 | public function setZip($zip) 632 | { 633 | $this->zip = $zip; 634 | 635 | return $this; 636 | } 637 | 638 | /* Getters */ 639 | 640 | /** 641 | * @return string 642 | */ 643 | public function getShopIdp() 644 | { 645 | return $this->shopIdp; 646 | 647 | return $this; 648 | } 649 | 650 | /** 651 | * @return string 652 | */ 653 | public function getOrderIdp() 654 | { 655 | return $this->orderIdp; 656 | 657 | return $this; 658 | } 659 | 660 | /** 661 | * @return float|string 662 | */ 663 | public function getSubtotalP() 664 | { 665 | return $this->subtotalP; 666 | 667 | return $this; 668 | } 669 | 670 | /** 671 | * @return string 672 | */ 673 | public function getSignature() 674 | { 675 | return $this->signature; 676 | } 677 | 678 | /** 679 | * @return string 680 | */ 681 | public function getUrlReturnOk() 682 | { 683 | return $this->urlReturnOk; 684 | } 685 | 686 | /** 687 | * @return string 688 | */ 689 | public function getUrlReturnNo() 690 | { 691 | return $this->urlReturnNo; 692 | } 693 | 694 | /** 695 | * @return string 696 | */ 697 | public function getCurrency() 698 | { 699 | return $this->currency; 700 | } 701 | 702 | /** 703 | * @return string 704 | */ 705 | public function getEmail() 706 | { 707 | return $this->email; 708 | } 709 | 710 | /** 711 | * @return int 712 | */ 713 | public function getLifetime() 714 | { 715 | return $this->lifetime; 716 | } 717 | 718 | /** 719 | * @return int 720 | */ 721 | public function getOrderLifetime() 722 | { 723 | return $this->orderLifetime; 724 | } 725 | 726 | 727 | /** 728 | * @return string 729 | */ 730 | public function getCustomerIdp() 731 | { 732 | return $this->customerIdp; 733 | } 734 | 735 | /** 736 | * @return mixed 737 | */ 738 | public function getCardIdp() 739 | { 740 | return $this->cardIdp; 741 | } 742 | 743 | /** 744 | * @return string 745 | */ 746 | public function getIData() 747 | { 748 | return $this->iData; 749 | } 750 | 751 | /** 752 | * @return string 753 | */ 754 | public function getPtCode() 755 | { 756 | return $this->ptCode; 757 | } 758 | 759 | /** 760 | * @return int 761 | */ 762 | public function getMeanType() 763 | { 764 | return $this->meanType; 765 | } 766 | 767 | /** 768 | * @return int 769 | */ 770 | public function getEMoneyType() 771 | { 772 | return $this->eMoneyType; 773 | } 774 | 775 | /** 776 | * @return int 777 | */ 778 | public function getBillLifetime() 779 | { 780 | return $this->billLifetime; 781 | } 782 | 783 | /** 784 | * @return bool 785 | */ 786 | public function isPreAuth() 787 | { 788 | return $this->preAuth; 789 | } 790 | 791 | /** 792 | * @return bool 793 | */ 794 | public function isIsRecurrentStart() 795 | { 796 | return $this->IsRecurrentStart; 797 | } 798 | 799 | /** 800 | * @return array 801 | */ 802 | public function getCallbackFields() 803 | { 804 | return $this->callbackFields; 805 | } 806 | 807 | /** 808 | * @return string 809 | */ 810 | public function getCallbackFormat() 811 | { 812 | return $this->callbackFormat; 813 | } 814 | 815 | /** 816 | * @return string 817 | */ 818 | public function getLanguage() 819 | { 820 | return $this->language; 821 | } 822 | 823 | /** 824 | * @return string 825 | */ 826 | public function getComment() 827 | { 828 | return $this->comment; 829 | } 830 | 831 | /** 832 | * @return string 833 | */ 834 | public function getFirstName() 835 | { 836 | return $this->firstName; 837 | } 838 | 839 | /** 840 | * @return mixed 841 | */ 842 | public function getLastName() 843 | { 844 | return $this->lastName; 845 | } 846 | 847 | /** 848 | * @return string 849 | */ 850 | public function getMiddleName() 851 | { 852 | return $this->middleName; 853 | } 854 | 855 | /** 856 | * @return string 857 | */ 858 | public function getPhone() 859 | { 860 | return $this->phone; 861 | } 862 | 863 | /** 864 | * @return string 865 | */ 866 | public function getAddress() 867 | { 868 | return $this->address; 869 | } 870 | 871 | /** 872 | * @return string 873 | */ 874 | public function getCountry() 875 | { 876 | return $this->country; 877 | } 878 | 879 | /** 880 | * @return string 881 | */ 882 | public function getState() 883 | { 884 | return $this->state; 885 | } 886 | 887 | /** 888 | * @return string 889 | */ 890 | public function getCity() 891 | { 892 | return $this->city; 893 | } 894 | 895 | /** 896 | * @return mixed 897 | */ 898 | public function getZip() 899 | { 900 | return $this->zip; 901 | } 902 | 903 | /** 904 | * @return array 905 | */ 906 | public function toArray() 907 | { 908 | return [ 909 | 'Shop_IDP' => $this->getShopIdp(), 910 | 'Order_IDP' => $this->getOrderIdp(), 911 | 'Subtotal_P' => $this->getSubtotalP(), 912 | 'Signature' => $this->getSignature(), 913 | 'URL_RETURN_OK' => $this->getUrlReturnOk(), 914 | 'URL_RETURN_NO' => $this->getUrlReturnNo(), 915 | 'Currency' => $this->getCurrency(), 916 | 'Email' => $this->getEmail(), 917 | 'Lifetime' => $this->getLifetime(), 918 | 'OrderLifetime' => $this->getOrderLifetime(), 919 | 'Customer_IDP' => $this->getCustomerIdp(), 920 | 'Card_IDP' => $this->getCardIdp(), 921 | 'IData' => $this->getIData(), 922 | 'PT_Code' => $this->getPtCode(), 923 | 'MeanType' => $this->getMeanType(), 924 | 'EMoneyType' => $this->getEMoneyType(), 925 | 'BillLifetime' => $this->getBillLifetime(), 926 | 'Preauth' => $this->isPreAuth(), 927 | 'IsRecurrentStart' => $this->isIsRecurrentStart(), 928 | 'CallbackFields' => $this->getCallbackFields(), 929 | 'CallbackFormat' => $this->getCallbackFormat(), 930 | 'Language' => $this->getLanguage(), 931 | 'Comment' => $this->getComment(), 932 | 'FirstName' => $this->getFirstName(), 933 | 'LastName' => $this->getLastName(), 934 | 'MiddleName' => $this->getMiddleName(), 935 | 'Phone' => $this->getPhone(), 936 | 'Address' => $this->getAddress(), 937 | 'Country' => $this->getCountry(), 938 | 'State' => $this->getState(), 939 | 'City' => $this->getCity(), 940 | 'Zip' => $this->getZip(), 941 | ]; 942 | } 943 | } -------------------------------------------------------------------------------- /src/Order/Order.php: -------------------------------------------------------------------------------- 1 | address = $address; 307 | 308 | return $this; 309 | } 310 | 311 | /** 312 | * @param string $approvalCode 313 | * @return $this 314 | */ 315 | public function setApprovalCode($approvalCode) 316 | { 317 | $this->approvalCode = $approvalCode; 318 | 319 | return $this; 320 | } 321 | 322 | /** 323 | * @param string $bankName 324 | * @return $this 325 | */ 326 | public function setBankName($bankName) 327 | { 328 | $this->bankName = $bankName; 329 | 330 | return $this; 331 | } 332 | 333 | /** 334 | * @param int $billNumber 335 | * @return $this 336 | */ 337 | public function setBillNumber($billNumber) 338 | { 339 | $this->billNumber = $billNumber; 340 | 341 | return $this; 342 | } 343 | 344 | /** 345 | * @param string $bookingcomId 346 | * @return $this 347 | */ 348 | public function setBookingcomId($bookingcomId) 349 | { 350 | $this->bookingcomId = $bookingcomId; 351 | 352 | return $this; 353 | } 354 | 355 | /** 356 | * @param string $bookingcomPincode 357 | * @return $this 358 | */ 359 | public function setBookingcomPincode($bookingcomPincode) 360 | { 361 | $this->bookingcomPincode = $bookingcomPincode; 362 | 363 | return $this; 364 | } 365 | 366 | /** 367 | * @param string $cardIdp 368 | * @return $this 369 | */ 370 | public function setCardIdp($cardIdp) 371 | { 372 | $this->cardIdp = $cardIdp; 373 | 374 | return $this; 375 | } 376 | 377 | /** 378 | * @param string $cardHolder 379 | * @return $this 380 | */ 381 | public function setCardHolder($cardHolder) 382 | { 383 | $this->cardHolder = $cardHolder; 384 | 385 | return $this; 386 | } 387 | 388 | /** 389 | * @param string $cardNumber 390 | * @return $this 391 | */ 392 | public function setCardNumber($cardNumber) 393 | { 394 | $this->cardNumber = $cardNumber; 395 | 396 | return $this; 397 | } 398 | 399 | /** 400 | * @param string $cardType 401 | * @return $this 402 | */ 403 | public function setCardType($cardType) 404 | { 405 | $this->cardType = $cardType; 406 | 407 | return $this; 408 | } 409 | 410 | /** 411 | * @param string $comment 412 | * @return $this 413 | */ 414 | public function setComment($comment) 415 | { 416 | $this->comment = $comment; 417 | 418 | return $this; 419 | } 420 | 421 | /** 422 | * @param string $currency 423 | * @return $this 424 | */ 425 | public function setCurrency($currency) 426 | { 427 | $this->currency = $currency; 428 | 429 | return $this; 430 | } 431 | 432 | /** 433 | * @param $value 434 | * @return $this 435 | */ 436 | public function setCvc2($value) 437 | { 438 | $this->cvc2 = (bool) $value; 439 | 440 | return $this; 441 | } 442 | 443 | /** 444 | * @return $this 445 | */ 446 | public function withCvc2() 447 | { 448 | $this->cvc2 = true; 449 | 450 | return $this; 451 | } 452 | 453 | /** 454 | * @return $this 455 | */ 456 | public function withoutCvc2() 457 | { 458 | $this->cvc2 = false; 459 | 460 | return $this; 461 | } 462 | 463 | /** 464 | * @param DateTime $date 465 | * @return $this 466 | */ 467 | public function setDate($date) 468 | { 469 | if (empty($date)) { 470 | return $this; 471 | } 472 | 473 | // Ёбаный насрать, а даты зачем разного формата отдавать? 474 | // Полностью разделяю и поддерживаю данное негодование, значит ебашим костыли)) 475 | $date = str_replace('.', '-', $date); 476 | $this->date = DateTime::createFromFormat('U', strtotime($date)); 477 | 478 | return $this; 479 | } 480 | 481 | /** 482 | * @param string $email 483 | * @return $this 484 | */ 485 | public function setEmail($email) 486 | { 487 | $this->email = $email; 488 | 489 | return $this; 490 | } 491 | 492 | /** 493 | * @param string $eMoneyType 494 | * @return $this 495 | */ 496 | public function setEMoneyType($eMoneyType) 497 | { 498 | $this->eMoneyType = $eMoneyType; 499 | 500 | return $this; 501 | } 502 | 503 | /** 504 | * @param array $eOrderData 505 | * @return $this 506 | */ 507 | public function setEOrderData($eOrderData) 508 | { 509 | if (empty($eOrderData)) { 510 | return $this; 511 | } 512 | 513 | foreach (explode(', ', $eOrderData) as $item) { 514 | list($key, $value) = explode('=', $item); 515 | $this->eOrderData[$key] = $value; 516 | } 517 | 518 | return $this; 519 | } 520 | 521 | /** 522 | * @param int $errorCode 523 | * @return $this 524 | */ 525 | public function setErrorCode($errorCode) 526 | { 527 | $this->errorCode = $errorCode; 528 | 529 | return $this; 530 | } 531 | 532 | /** 533 | * @param string $errorComment 534 | * @return $this 535 | */ 536 | public function setErrorComment($errorComment) 537 | { 538 | $this->errorComment = $errorComment; 539 | 540 | return $this; 541 | } 542 | 543 | /** 544 | * @param string $firstName 545 | * @return $this 546 | */ 547 | public function setFirstName($firstName) 548 | { 549 | $this->firstName = $firstName; 550 | 551 | return $this; 552 | } 553 | 554 | /** 555 | * @param int $gdsPaymentPurposeId 556 | * @return $this 557 | */ 558 | public function setGdsPaymentPurposeId($gdsPaymentPurposeId) 559 | { 560 | $this->gdsPaymentPurposeId = $gdsPaymentPurposeId; 561 | 562 | return $this; 563 | } 564 | 565 | /** 566 | * @param string $iData 567 | * @return $this 568 | */ 569 | public function setIData($iData) 570 | { 571 | $this->iData = $iData; 572 | 573 | return $this; 574 | } 575 | 576 | /** 577 | * @param string $ip 578 | * @return $this 579 | */ 580 | public function setIp($ip) 581 | { 582 | $this->ip = $ip; 583 | 584 | return $this; 585 | } 586 | 587 | /** 588 | * @param string $lastName 589 | * @return $this 590 | */ 591 | public function setLastName($lastName) 592 | { 593 | $this->lastName = $lastName; 594 | 595 | return $this; 596 | } 597 | 598 | /** 599 | * @param string $loanId 600 | * @return $this 601 | */ 602 | public function setLoanId($loanId) 603 | { 604 | $this->loanId = $loanId; 605 | 606 | return $this; 607 | } 608 | 609 | /** 610 | * @param string $message 611 | * @return $this 612 | */ 613 | public function setMessage($message) 614 | { 615 | $this->message = $message; 616 | 617 | return $this; 618 | } 619 | 620 | /** 621 | * @param string $middleName 622 | * @return $this 623 | */ 624 | public function setMiddleName($middleName) 625 | { 626 | $this->middleName = $middleName; 627 | 628 | return $this; 629 | } 630 | 631 | /** 632 | * @param bool $needConfirm 633 | * @return $this 634 | */ 635 | public function setNeedConfirm($needConfirm) 636 | { 637 | $this->needConfirm = (bool) $needConfirm; 638 | 639 | return $this; 640 | } 641 | 642 | /** 643 | * @param string $orderNumber 644 | * @return $this 645 | */ 646 | public function setOrderNumber($orderNumber) 647 | { 648 | $this->orderNumber = $orderNumber; 649 | 650 | return $this; 651 | } 652 | 653 | /** 654 | * @param string $parentOrderNumber 655 | * @return $this 656 | */ 657 | public function setParentOrderNumber($parentOrderNumber) 658 | { 659 | $this->parentOrderNumber = $parentOrderNumber; 660 | 661 | return $this; 662 | } 663 | 664 | /** 665 | * @param int $paymentType 666 | * @return $this 667 | */ 668 | public function setPaymentType($paymentType) 669 | { 670 | $this->paymentType = $paymentType; 671 | 672 | return $this; 673 | } 674 | 675 | /** 676 | * @param string $phone 677 | * @return $this 678 | */ 679 | public function setPhone($phone) 680 | { 681 | $this->phone = $phone; 682 | 683 | return $this; 684 | } 685 | 686 | /** 687 | * @param string $ptCode 688 | * @return $this 689 | */ 690 | public function setPtCode($ptCode) 691 | { 692 | $this->ptCode = $ptCode; 693 | 694 | return $this; 695 | } 696 | 697 | /** 698 | * @param string $recommendation 699 | * @return $this 700 | */ 701 | public function setRecommendation($recommendation) 702 | { 703 | $this->recommendation = $recommendation; 704 | 705 | return $this; 706 | } 707 | 708 | /** 709 | * @param string $responseCode 710 | * @return $this 711 | */ 712 | public function setResponseCode($responseCode) 713 | { 714 | $this->responseCode = $responseCode; 715 | 716 | return $this; 717 | } 718 | 719 | /** 720 | * @param string $status 721 | * @return $this 722 | */ 723 | public function setStatus($status) 724 | { 725 | $this->status = Status::resolve($status); 726 | 727 | return $this; 728 | } 729 | 730 | /** 731 | * @param string $total 732 | * @return $this 733 | */ 734 | public function setTotal($total) 735 | { 736 | $this->total = $total; 737 | 738 | return $this; 739 | } 740 | 741 | /** 742 | * @return string 743 | */ 744 | public function getAddress() 745 | { 746 | return $this->address; 747 | } 748 | 749 | /** 750 | * @return string 751 | */ 752 | public function getApprovalCode() 753 | { 754 | return $this->approvalCode; 755 | } 756 | 757 | /** 758 | * @return string 759 | */ 760 | public function getBankName() 761 | { 762 | return $this->bankName; 763 | } 764 | 765 | /** 766 | * @return int 767 | */ 768 | public function getBillNumber() 769 | { 770 | return $this->billNumber; 771 | } 772 | 773 | /** 774 | * @return string 775 | */ 776 | public function getBookingcomId() 777 | { 778 | return $this->bookingcomId; 779 | } 780 | 781 | /** 782 | * @return string 783 | */ 784 | public function getBookingcomPincode() 785 | { 786 | return $this->bookingcomPincode; 787 | } 788 | 789 | /** 790 | * @return string 791 | */ 792 | public function getCardIdp() 793 | { 794 | return $this->cardIdp; 795 | } 796 | 797 | /** 798 | * @return string 799 | */ 800 | public function getCardHolder() 801 | { 802 | return $this->cardHolder; 803 | } 804 | 805 | /** 806 | * @return string 807 | */ 808 | public function getCardNumber() 809 | { 810 | return $this->cardNumber; 811 | } 812 | 813 | /** 814 | * @return string 815 | */ 816 | public function getCardType() 817 | { 818 | return $this->cardType; 819 | } 820 | 821 | /** 822 | * @return string 823 | */ 824 | public function getComment() 825 | { 826 | return $this->comment; 827 | } 828 | 829 | /** 830 | * @return string 831 | */ 832 | public function getCurrency() 833 | { 834 | return $this->currency; 835 | } 836 | 837 | /** 838 | * @return int 839 | */ 840 | public function isCvc2() 841 | { 842 | return $this->cvc2; 843 | } 844 | 845 | /** 846 | * @return DateTime 847 | */ 848 | public function getDate() 849 | { 850 | return $this->date; 851 | } 852 | 853 | /** 854 | * @return string 855 | */ 856 | public function getEmail() 857 | { 858 | return $this->email; 859 | } 860 | 861 | /** 862 | * @return string 863 | */ 864 | public function getEMoneyType() 865 | { 866 | return $this->eMoneyType; 867 | } 868 | 869 | /** 870 | * @return array 871 | */ 872 | public function getEOrderData() 873 | { 874 | return $this->eOrderData; 875 | } 876 | 877 | /** 878 | * @return int 879 | */ 880 | public function getErrorCode() 881 | { 882 | return $this->errorCode; 883 | } 884 | 885 | /** 886 | * @return string 887 | */ 888 | public function getErrorComment() 889 | { 890 | return $this->errorComment; 891 | } 892 | 893 | /** 894 | * @return string 895 | */ 896 | public function getFirstName() 897 | { 898 | return $this->firstName; 899 | } 900 | 901 | /** 902 | * @return int 903 | */ 904 | public function getGdsPaymentPurposeId() 905 | { 906 | return $this->gdsPaymentPurposeId; 907 | } 908 | 909 | /** 910 | * @return string 911 | */ 912 | public function getIData() 913 | { 914 | return $this->iData; 915 | } 916 | 917 | /** 918 | * @return string 919 | */ 920 | public function getIp() 921 | { 922 | return $this->ip; 923 | } 924 | 925 | /** 926 | * @return string 927 | */ 928 | public function getLastName() 929 | { 930 | return $this->lastName; 931 | } 932 | 933 | /** 934 | * @return string 935 | */ 936 | public function getLoanId() 937 | { 938 | return $this->loanId; 939 | } 940 | 941 | /** 942 | * @return string 943 | */ 944 | public function getMessage() 945 | { 946 | return $this->message; 947 | } 948 | 949 | /** 950 | * @return string 951 | */ 952 | public function getMiddleName() 953 | { 954 | return $this->middleName; 955 | } 956 | 957 | /** 958 | * @return bool 959 | */ 960 | public function isNeedConfirm() 961 | { 962 | return $this->needConfirm; 963 | } 964 | 965 | /** 966 | * @return string 967 | */ 968 | public function getOrderNumber() 969 | { 970 | return $this->orderNumber; 971 | } 972 | 973 | /** 974 | * @return string 975 | */ 976 | public function getParentOrderNumber() 977 | { 978 | return $this->parentOrderNumber; 979 | } 980 | 981 | /** 982 | * @return int 983 | */ 984 | public function getPaymentType() 985 | { 986 | return $this->paymentType; 987 | } 988 | 989 | /** 990 | * @return string 991 | */ 992 | public function getPhone() 993 | { 994 | return $this->phone; 995 | } 996 | 997 | /** 998 | * @return string 999 | */ 1000 | public function getPtCode() 1001 | { 1002 | return $this->ptCode; 1003 | } 1004 | 1005 | /** 1006 | * @return string 1007 | */ 1008 | public function getRecommendation() 1009 | { 1010 | return $this->recommendation; 1011 | } 1012 | 1013 | /** 1014 | * @return string 1015 | */ 1016 | public function getResponseCode() 1017 | { 1018 | return $this->responseCode; 1019 | } 1020 | 1021 | /** 1022 | * @return string 1023 | */ 1024 | public function getResponseMessage() 1025 | { 1026 | return ResponseCode::message($this->responseCode); 1027 | } 1028 | 1029 | /** 1030 | * @return string 1031 | */ 1032 | public function getStatus() 1033 | { 1034 | return $this->status; 1035 | } 1036 | 1037 | /** 1038 | * @return string 1039 | */ 1040 | public function getTotal() 1041 | { 1042 | return $this->total; 1043 | } 1044 | 1045 | /** 1046 | * @param DateTime $packetDate 1047 | * @return $this 1048 | */ 1049 | public function setPacketDate($packetDate) 1050 | { 1051 | if (empty($packetDate)) { 1052 | return $this; 1053 | } 1054 | 1055 | // 10.03.2017 15:42:42 - o_O 1056 | // 2018.07.16 10:54:05 - O_o 1057 | $packetDate = str_replace('.', '-', $packetDate); 1058 | $this->packetDate = DateTime::createFromFormat('U', strtotime($packetDate)); 1059 | 1060 | return $this; 1061 | } 1062 | 1063 | /** 1064 | * @return DateTime 1065 | */ 1066 | public function getPacketDate() 1067 | { 1068 | return $this->packetDate; 1069 | } 1070 | 1071 | /** 1072 | * @return string 1073 | */ 1074 | public function getSignature() 1075 | { 1076 | return $this->signature; 1077 | } 1078 | 1079 | /** 1080 | * @param string $signature 1081 | * @return Order 1082 | */ 1083 | public function setSignature($signature) 1084 | { 1085 | $this->signature = $signature; 1086 | 1087 | return $this; 1088 | } 1089 | 1090 | /** 1091 | * @return array 1092 | */ 1093 | public function toArray() 1094 | { 1095 | return [ 1096 | 'Address' => $this->getAddress(), 1097 | 'ApprovalCode' => $this->getApprovalCode(), 1098 | 'BankName' => $this->getBankName(), 1099 | 'BillNumber' => $this->getBillNumber(), 1100 | 'bookingcom_id' => $this->getBookingcomId(), 1101 | 'bookingcom_pincode' => $this->getBookingcomPincode(), 1102 | 'Card_IDP' => $this->getCardIdp(), 1103 | 'CardHolder' => $this->getCardHolder(), 1104 | 'CardNumber' => $this->getCardNumber(), 1105 | 'CardType' => $this->getCardType(), 1106 | 'Comment' => $this->getComment(), 1107 | 'Currency' => $this->getCurrency(), 1108 | 'CVC2' => $this->isCvc2(), 1109 | 'Date' => $this->getDate(), 1110 | 'PacketDate' => $this->getPacketDate(), 1111 | 'Email' => $this->getEmail(), 1112 | 'EMoneyType' => $this->getEMoneyType(), 1113 | 'EOrderData' => $this->getEOrderData(), 1114 | 'Error_Code' => $this->getErrorCode(), 1115 | 'Error_Comment' => $this->getErrorComment(), 1116 | 'FirstName' => $this->getFirstName(), 1117 | 'gds_payment_purpose_id' => $this->getGdsPaymentPurposeId(), 1118 | 'IData' => $this->getIData(), 1119 | 'IPAddress' => $this->getIp(), 1120 | 'LastName' => $this->getLastName(), 1121 | 'LoanID' => $this->getLoanId(), 1122 | 'Message' => $this->getMessage(), 1123 | 'MiddleName' => $this->getMiddleName(), 1124 | 'need_confirm' => $this->isNeedConfirm(), 1125 | 'OrderNumber' => $this->getOrderNumber(), 1126 | 'parent_order_number' => $this->getParentOrderNumber(), 1127 | 'PaymentType' => $this->getPaymentType(), 1128 | 'Phone' => $this->getPhone(), 1129 | 'PT_Code' => $this->getPtCode(), 1130 | 'Recommendation' => $this->getRecommendation(), 1131 | 'Response_Code' => $this->getResponseCode(), 1132 | 'Response_Message' => $this->getResponseMessage(), 1133 | 'Status' => $this->getStatus(), 1134 | 'Total' => $this->getTotal(), 1135 | 'Signature' => $this->getSignature(), 1136 | ]; 1137 | } 1138 | } --------------------------------------------------------------------------------