1
0
Fork 0
mirror of synced 2025-04-19 17:01:00 +00:00

Compare commits

..

37 commits

Author SHA1 Message Date
Ichern
0adfb9edda
Merge pull request #18 from retailcrm/resolve-logger-version-conflict
Resolve conflict with psr/log
2024-07-17 12:44:28 +03:00
Nikolay Parshakov
aecbdeafcc Resolve conflict with psr/log 2024-07-17 12:12:51 +03:00
Ilyas Salikhov
a857ba561a
Merge pull request #17 from retailcrm/sf6
compability with symfony 6
2024-06-17 18:29:03 +03:00
Ilyas Salikhov
f570c20839 compability with symfony 6 2024-06-17 18:28:35 +03:00
Ilyas Salikhov
726b51151c Check deprecations in tests 2024-01-27 19:40:29 +03:00
Ilyas Salikhov
1b75a04cf9 Compability with guzzlehttp/guzzle:^7.0 2024-01-27 19:37:04 +03:00
Ilyas Salikhov
68df0e1f49 Environment to run test locally 2024-01-27 19:36:37 +03:00
Alexey
5158cf22b3
bump "symfony/validator" version in composer.json (#16)
* bump "symfony/validator" version in composer.json
2022-09-05 13:45:33 +03:00
Alexey
c68448954b
fix jms/serializer versions (#15)
* fix jms/serializer versions
* add php 7.4 to travis
* fix travis badge
2021-10-29 12:39:14 +03:00
raulleo
c9f5fa92af
change quantity type from int to float (#14) 2021-05-27 12:06:51 +03:00
Alexey
b09a4259dd
Update README.md 2019-12-26 11:36:49 +03:00
Vragov Roman
5e6ad7544c Update jms serializer (#12) 2019-12-26 11:36:15 +03:00
Alexey
c209002c5a
Update composer.json
fix "symfony/validator" version
2019-11-25 18:18:41 +03:00
Vragov Roman
ae80596b33 Fix vats request (#11) 2019-08-26 12:04:00 +03:00
Vragov Roman
ffd5b85fe0 Remove V3 (#8) 2019-08-22 17:04:26 +03:00
CrazyProgg
6e0adffb46 add fns_site payload property support (#10) 2019-08-22 16:49:05 +03:00
Alexey
ed84369c2c
fix types (#9) 2019-08-08 11:01:53 +03:00
Alexey
5791ca5107
Update README.md 2019-07-24 17:48:07 +03:00
Alexey Chelnakov
1c0d39725d add version badge 2019-07-24 17:46:59 +03:00
Alexey Chelnakov
cdc570e0cd phpunit ~7.0 2019-07-24 17:39:11 +03:00
Alexey Chelnakov
7cfd902db2 add build status 2019-07-24 17:29:56 +03:00
Vragov Roman
342d15ba5b Update guzzle to guzzlehttp/guzzle": "~6.3" 2019-07-24 17:13:55 +03:00
Alexey Chelnakov
9c300796b0 Добавлены параметры:
- Код товара receipt -> items -> nomenclature_code (тег 1162);
  - Покупатель (клиент), (receipt → client → name), (тег 1227);
  - ИНН покупателя (клиента), (receipt → client → inn), (тег 1228).
2019-06-21 12:16:41 +03:00
Alexey
f21bf18401
Merge pull request #2 from raulleo/fix-debug-typo
fix typo
2019-04-11 14:58:36 +03:00
raulleo
21c0f804a5 fix typo 2019-04-11 14:47:05 +03:00
Alexey Chelnakov
73dc060297 fix возвращаем response с ошибкой 2019-04-10 19:22:43 +03:00
Alexey
aa735cff8d
fix Request serialization sno field 2018-12-04 17:47:41 +03:00
Ilyas Salikhov
df1543abd3
Merge pull request #1 from linniksa/sf
add symfony versions other that 2.8
2018-11-26 19:41:01 +03:00
Sergey Linnik
ef14e9a109 add symfony versions other that 2.8 2018-11-26 14:57:28 +03:00
Alexey Chelnakov
6feaaf3fa6 fix type string VatReceiptRequest.sum 2018-08-06 17:33:40 +03:00
Ilyas Salikhov
becb2f5d11 Updated README 2018-06-28 10:44:21 +03:00
Alexey Chelnakov
397fa42b72 удалил composer.lock 2018-06-28 10:33:34 +03:00
Alexey Chelnakov
e9645633cb убрал поля ofd, enabled у конфигурации Connection 2018-06-26 12:44:54 +03:00
Alexey Chelnakov
9f3dbdddcb fix ignore 2018-06-25 18:25:00 +03:00
Alexey Chelnakov
40d328f617 Merge branch 'master' of github.com:addfs/atol-online-client 2018-06-25 18:23:54 +03:00
Alexey Chelnakov
dee349220c is version methods 2018-06-25 18:23:39 +03:00
Ilyas Salikhov
d3a0cf88eb
Added LICENSE 2018-06-22 12:38:35 +03:00
48 changed files with 1756 additions and 3439 deletions

5
.gitignore vendored
View file

@ -1,2 +1,5 @@
./idea
/.idea
/vendor
composer.lock
.phpunit.result.cache
.env

16
.travis.yml Normal file
View file

@ -0,0 +1,16 @@
language: php
php:
- 7.2
- 7.3
- 7.4
env:
- JMS=0.12
- JMS=1.0
- JMS=2.0
- JMS=3.0
before_install:
- composer selfupdate
- composer require jms/serializer=^$JMS --no-interaction --prefer-dist

6
Dockerfile Normal file
View file

@ -0,0 +1,6 @@
ARG PHP_IMAGE_TAG
FROM php:${PHP_IMAGE_TAG}-cli-alpine
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /opt/test

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 retailCRM
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

16
Makefile Normal file
View file

@ -0,0 +1,16 @@
ifneq (,$(shell (type docker-compose 2>&1 >/dev/null && echo 1) || true))
PHP=docker-compose run --rm --no-deps php
else
PHP=php
endif
PHP_CONSOLE_DEPS=vendor
vendor: composer.json
@$(PHP) composer install -o -n --no-ansi
@touch vendor || true
phpunit: $(PHP_CONSOLE_DEPS)
@$(PHP) vendor/bin/phpunit --color=always
check: phpunit

View file

@ -1,28 +1,38 @@
# API клиент для сервиса фискализации платежей АТОЛ Онлайн
[![Build Status](https://app.travis-ci.com/retailcrm/atol-online-client.svg?branch=master)](https://app.travis-ci.com/retailcrm/atol-online-client)
[![Latest stable](https://img.shields.io/packagist/v/retailcrm/atol-online-client.svg?style=flat-square)](https://packagist.org/packages/retailcrm/atol-online-client)
Пример использования:
# API-клиент для АТОЛ.Онлайн
API-клиент на PHP для сервиса онлайн-фискализации платежей АТОЛ.Онлайн
## Требования
* PHP 7.2 и выше
* PHP extension cURL
## Пример использования
```php
$atol = new \AtolOnlineClient\AtolOnline();
$client = new \Guzzle\Http\Client();
$connection = new \AtolOnlineClient\Configuration\Connection();
$connection->version = \AtolOnlineClient\AtolOnlineApi::API_VERSION_V4;
$connection->login = 'login';
$connection->pass = 'pass';
$connection->enabled = true;
$connection->group = 'group';
$config = new \AtolOnlineClient\Configuration();
$config->connections = [$connection];
$api = $this->atol->createApi($client, $connection);
//$api->setLogger();
//$api->setCache();
$client = new \Guzzle\Http\Client();
$api = $atol->createApi($client, $connection);
// $api->setLogger(...);
// $api->setCache(...);
// собираем объект запроса
$request = new \AtolOnlineClient\Request\V4\PaymentReceiptRequest();
/// ...
/// собираем объект запроса
///
$paymentReceiptRequest = $atol->serializeOperationRequest($request);
$response = $atol->getApi()->sell($paymentReceiptRequest);

View file

@ -1,13 +0,0 @@
<?php
use Composer\Autoload\ClassLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;
/**
* @var ClassLoader $loader
*/
$loader = require __DIR__.'/vendor/autoload.php';
AnnotationRegistry::registerLoader('class_exists');
return $loader;

View file

@ -2,7 +2,7 @@
"name": "retailcrm/atol-online-client",
"type": "library",
"license": "proprietary",
"description": "Api client for Atol Online",
"description": "PHP API client for ATOL Online",
"authors": [
{
"name": "retailCRM",
@ -10,21 +10,29 @@
}
],
"require": {
"php": ">=7.1",
"php": ">=7.2",
"ext-curl": "*",
"guzzle/guzzle": "~3.7",
"jms/serializer": "~0.12 || ~1.4.2",
"symfony/validator": "2.8.*",
"ext-json": "*",
"guzzlehttp/guzzle": "~6.3|^7.0",
"jms/serializer": "^0.12|^1.0|^2.0|^3.0",
"symfony/validator": "~2.8|~3.0|~4.0|^5.0|^6.0",
"doctrine/cache": "~1.6",
"psr/log": "~1.0"
"psr/log": "~1.0|^2.0|^3.0"
},
"require-dev": {
"phpunit/phpunit": "7.1."
"phpunit/phpunit": "~8.0"
},
"support": {
"email": "support@retailcrm.ru"
},
"autoload": {
"psr-0": { "AtolOnlineClient\\": "src/" }
"psr-0": {
"AtolOnlineClient\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"AtolOnlineClient\\": "tests/"
}
}
}

2389
composer.lock generated

File diff suppressed because it is too large Load diff

11
docker-compose.yml Normal file
View file

@ -0,0 +1,11 @@
version: '3.4'
services:
php:
build:
context: .
args:
PHP_IMAGE_TAG: ${PHP_IMAGE_TAG:-7.4}
volumes:
- "./:/opt/test"

View file

@ -1,24 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- http://www.phpunit.de/manual/current/en/appendixes.configuration.html -->
<phpunit
backupGlobals = "false"
backupStaticAttributes = "false"
colors = "true"
convertErrorsToExceptions = "true"
convertNoticesToExceptions = "true"
convertWarningsToExceptions = "true"
processIsolation = "true"
stopOnFailure = "false"
syntaxCheck = "false"
bootstrap = "bootstrap.php" >
<phpunit bootstrap="tests/bootstrap.php"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
verbose="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
convertDeprecationsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="php">
<directory>./tests/*/Tests</directory>
<directory>./tests/AtolOnlineClient</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>src</directory>
</whitelist>
</filter>
</phpunit>

View file

@ -3,18 +3,25 @@
namespace AtolOnlineClient;
use AtolOnlineClient\Configuration\Connection;
use AtolOnlineClient\Exception\InvalidResponseException;
use AtolOnlineClient\Response\OperationResponse;
use Guzzle\Http\Client;
use GuzzleHttp\Client;
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\Exception\RuntimeException;
use JMS\Serializer\SerializationContext;
use JMS\Serializer\SerializerBuilder;
use JMS\Serializer\SerializerInterface;
class AtolOnline
{
/**
* @var SerializerInterface
*/
private $serializer;
/** @var AtolOnlineApi */
/**
* @var AtolOnlineApi
*/
private $api;
public function __construct()
@ -22,37 +29,48 @@ class AtolOnline
$this->serializer = SerializerBuilder::create()->build();
}
public function createConfiguration()
/**
* @return Configuration
*/
public function createConfiguration(): ConfigurationInterface
{
return new Configuration();
}
/**
* @param $response
* @param string $response
* @return OperationResponse
*/
public function deserializeOperationResponse($response)
public function deserializeOperationResponse(string $response): OperationResponse
{
return $this->serializer->deserialize(
$response,
OperationResponse::class,
'json',
DeserializationContext::create()->setGroups(['post'])
);
try {
return $this->serializer->deserialize(
$response,
OperationResponse::class,
'json',
DeserializationContext::create()->setGroups(['post'])
);
} catch (RuntimeException $exception) {
throw $this->createInvalidResponseException($response, $exception);
}
}
/**
* @param $response
* @return OperationResponse
*/
public function deserializeCheckStatusResponse($response)
public function deserializeCheckStatusResponse($response): OperationResponse
{
return $this->serializer->deserialize(
$response,
OperationResponse::class,
'json',
DeserializationContext::create()->setGroups(['get'])
);
try {
return $this->serializer->deserialize(
$response,
OperationResponse::class,
'json',
DeserializationContext::create()->setGroups(['get'])
);
} catch (RuntimeException $exception) {
throw $this->createInvalidResponseException($response, $exception);
}
}
/**
@ -69,11 +87,11 @@ class AtolOnline
}
/**
* @param $client
* @param Client $client
* @param Connection $connection
* @return AtolOnlineApi
*/
public function createApi(Client $client, Connection $connection)
public function createApi(Client $client, Connection $connection): AtolOnlineApi
{
if (!$this->api) {
$this->api = new AtolOnlineApi($client, $connection);
@ -85,8 +103,30 @@ class AtolOnline
/**
* @return AtolOnlineApi
*/
public function getApi()
public function getApi(): AtolOnlineApi
{
return $this->api;
}
}
/**
* @param string $response
* @param RuntimeException $previous
* @return InvalidResponseException
*/
private function createInvalidResponseException(string $response, RuntimeException $previous): InvalidResponseException
{
$exception = new InvalidResponseException($previous->getMessage());
preg_match('/<head><title>(\d+) ([\w ]+)<\/title><\/head>/m', $response, $matches);
if (count($matches) === 3) {
[, $code, $message] = $matches;
$exception
->setCodeError($code)
->setMessageError($message);
}
return $exception;
}
}

View file

@ -4,36 +4,28 @@ namespace AtolOnlineClient;
use AtolOnlineClient\Configuration\Connection;
use Doctrine\Common\Cache\Cache;
use Guzzle\Http\Client;
use Guzzle\Http\Exception\BadResponseException;
use Guzzle\Http\Message\Response;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
class AtolOnlineApi
{
public const API_VERSION_V4 = 'v4';
const API_VERSION_V4 = 'v4';
const API_VERSION_V3 = 'v3';
const TOKEN_CACHE_KEY = 'crm_fiscal_atol_online_token';
const TOKEN_CACHE_TIME = 60 * 60 * 24;
public const TOKEN_CACHE_KEY = 'crm_fiscal_atol_online_token';
public const TOKEN_CACHE_TIME = 86400;
private $baseApiUrl = 'https://online.atol.ru/possystem';
private $login;
private $pass;
private $groupCode;
private $debug;
private $testApiUrl = 'https://testonline.atol.ru/possystem';
/**
* @var LoggerInterface
* @var LoggerInterface|null
*/
private $logger;
/**
* @var Cache
* @var Cache|null
*/
private $cache;
@ -42,6 +34,11 @@ class AtolOnlineApi
*/
private $client;
/**
* @var Connection
*/
private $connection;
/**
* @var int
*/
@ -52,9 +49,6 @@ class AtolOnlineApi
*/
private $attemptsCheckStatus;
/** @var string */
private $version;
/**
* @param Client $client
* @param Connection $connectionConfig
@ -62,18 +56,44 @@ class AtolOnlineApi
public function __construct(Client $client, Connection $connectionConfig)
{
$this->client = $client;
$this->login = $connectionConfig->login;
$this->pass = $connectionConfig->pass;
$this->groupCode = $connectionConfig->group;
if (!$connectionConfig->version) {
$connectionConfig->version = self::API_VERSION_V3;
}
$this->version = $connectionConfig->version;
$this->debug = $connectionConfig->isDebug();
$this->connection = $connectionConfig;
$this->attempts = 0;
if ($connectionConfig->isTestMode()) {
$this->baseApiUrl = 'https://testonline.atol.ru/possystem';
}
}
/**
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
/**
* @param Cache $cache
*/
public function setCache(Cache $cache): void
{
$this->cache = $cache;
}
/**
* @param string $version
* @return AtolOnlineApi
*/
public function setVersion(string $version): AtolOnlineApi
{
$this->connection->version = $version;
return $this;
}
/**
* @return string
*/
public function getVersion(): string
{
return $this->connection->version ?: self::API_VERSION_V4;
}
/**
@ -84,9 +104,7 @@ class AtolOnlineApi
*/
public function sell($paymentReceiptRequest)
{
if ($response = $this->sendOperationRequest('sell', $paymentReceiptRequest)) {
return $response->getBody()->__toString();
};
return $this->sendOperationRequest('sell', $paymentReceiptRequest);
}
/**
@ -97,73 +115,48 @@ class AtolOnlineApi
*/
public function sellRefund($paymentReceiptRequest)
{
if ($response = $this->sendOperationRequest('sell_refund', $paymentReceiptRequest)) {
return $response->getBody()->__toString();
};
return $this->sendOperationRequest('sell_refund', $paymentReceiptRequest);
}
/**
* Запрос для проверки статуса
*
* @param $uuid
* @param string $uuid
* @return mixed
*/
public function checkStatus($uuid)
{
$token = $this->getToken();
$url = $this->buildUrl('report/'.$uuid, $token);
$request = $this->client->get($url);
$response = false;
$url = $this->buildUrl('report/'.$uuid, $token);
try {
$this->attemptsCheckStatus++;
$response = $request->send();
} catch (BadResponseException $e) {
$this->cache->delete($this->getTokenCacheKey());
$body = json_decode($e->getResponse()->getBody());
if ($this->isTokenExpired($body) && $this->attemptsCheckStatus <= 1) {
$response = $this->client->get($url);
} catch (BadResponseException $exception) {
if ($this->cache) {
$this->cache->delete($this->getTokenCacheKey());
}
$response = $exception->getResponse();
$body = json_decode($response->getBody(), false);
if ($this->attemptsCheckStatus <= 1 && $this->isTokenExpired($body)) {
return $this->checkStatus($uuid);
}
$this->logDebug($url, $uuid, $e->getResponse());
}
if ($response) {
return $response->getBody()->__toString();
}
$this->logDebug($url, $uuid, $response);
return false;
}
/**
* @param $logger
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* @param Cache $cache
*/
public function setCache(Cache $cache)
{
$this->cache = $cache;
}
/**
* @param mixed $version
*/
public function setVersion($version): void
{
$this->version = $version;
return $response->getBody()->__toString();
}
/**
* @return string
*/
public function getVersion(): string
protected function getUri(): string
{
return $this->version;
return $this->connection->isTestMode() ? $this->testApiUrl : $this->baseApiUrl;
}
/**
@ -173,134 +166,137 @@ class AtolOnlineApi
protected function getToken()
{
$data = [
'login' => $this->login,
'pass' => $this->pass,
'login' => $this->connection->login,
'pass' => $this->connection->pass,
];
if ($token = $this->cache->fetch($this->getTokenCacheKey())) {
if ($this->cache && ($token = $this->cache->fetch($this->getTokenCacheKey()))) {
return $token;
}
$dataJson = json_encode((object)$data, JSON_UNESCAPED_UNICODE);
$url = $this->baseApiUrl
.'/'.$this->version
.'/getToken';
$request = $this->client->createRequest('POST', $url, null, $dataJson);
$url = $this->getUri().'/'.$this->connection->version.'/getToken';
$response = false;
try {
$response = $this->client->send($request);
} catch (BadResponseException $e) {
if ($this->logger) {
$this->logger->error($e->getResponse()->getBody());
$response = $this->client->post($url, ['body' => $dataJson]);
} catch (BadResponseException $exception) {
if ($this->logger && $exception->getResponse()) {
$this->logger->error((string) $exception->getResponse()->getBody());
}
}
if ($response) {
$response = json_decode($response->getBody());
$response = json_decode($response->getBody(), true);
if ($this->version === self::API_VERSION_V4) {
if (!isset ($response->error)) {
$this->cache->save($this->getTokenCacheKey(), $response->token, self::TOKEN_CACHE_TIME);
return $response->token;
} else {
$this->logger->error($response->error->code . ' '. $response->error->text);
}
} else {
if (isset($response->code) && ($response->code == 1 || $response->code == 0)) {
$this->cache->save($this->getTokenCacheKey(), $response->token, self::TOKEN_CACHE_TIME);
return $response->token;
}
if ($this->cache && !isset($response['error'])) {
$this->cache->save($this->getTokenCacheKey(), $response['token'], self::TOKEN_CACHE_TIME);
}
}
if ($this->logger && isset($response['error'])) {
$this->logger->error($response['error']['code'] . ' '. $response['error']['text']);
}
if (!isset($response['error'])) {
return $response['token'];
}
}
return false;
}
/**
* @return string
*/
protected function getTokenCacheKey()
{
return self::TOKEN_CACHE_KEY.'_'.md5($this->login.$this->pass).'_'.$this->version;
}
/**
* @param string $operation
* @param null $token
* @param string|null $token
* @return string
*/
protected function buildUrl($operation, $token = null)
protected function buildUrl(string $operation, string $token = null): string
{
$url = $this->baseApiUrl
.'/'.$this->version
.'/'.$this->groupCode
$url = $this->getUri()
.'/'.$this->connection->version
.'/'.$this->connection->group
.'/'.$operation;
if ($token) {
if ($this->version === self::API_VERSION_V4) {
$url .= '?token='.$token;
} elseif ($this->version === self::API_VERSION_V3) {
$url .= '?tokenid='.$token;
}
if (!$token) {
return $url;
}
return $url;
return $url.'?token='.$token;
}
/**
* @param string $operation
* @param string $data
* @return Response|bool
* @param mixed $data
* @return string
*/
protected function sendOperationRequest($operation, $data)
protected function sendOperationRequest(string $operation, $data): string
{
$token = $this->getToken();
$url = $this->buildUrl($operation, $token);
$request = $this->client->createRequest('POST', $url, null, $data);
$response = false;
try {
$this->attempts++;
$response = $this->client->send($request);
$response = $this->client->post($url, ['body' => $data]);
} catch (BadResponseException $e) {
$this->cache->delete($this->getTokenCacheKey());
$body = json_decode($e->getResponse()->getBody());
if ($this->cache) {
$this->cache->delete($this->getTokenCacheKey());
}
$response = $e->getResponse();
$body = json_decode($response->getBody()->__toString(), false);
if ($this->isTokenExpired($body) && $this->attempts <= 1) {
return $this->sendOperationRequest($operation, $data);
}
$this->logDebug($url, $data, $e->getResponse());
}
if ($response) {
$this->logDebug($url, $data, $response);
}
return $response;
return $response->getBody()->__toString();
}
protected function logDebug($url, $data, Response $response)
/**
* @param string $url
* @param string $data
* @param ResponseInterface $response
*/
protected function logDebug(string $url, string $data, ResponseInterface $response): void
{
if ($this->debug && $this->logger) {
$v = "* URL: ".$url;
$v .= "\n * POSTFILEDS: ".$data;
$v .= "\n * RESPONSE HEADERS: ".$response->getRawHeaders();
if ($this->logger && $this->connection->isDebug()) {
$headers = [];
foreach ($response->getHeaders() as $key => $value) {
$headers[] = implode(': ', [$key, $value[0]]);
}
$v = '* URL: '.$url;
$v .= "\n * POSTFIELDS: ".$data;
$v .= "\n * RESPONSE HEADERS: ".implode(', ', $headers);
$v .= "\n * RESPONSE BODY: ".$response->getBody();
$v .= "\n * ATTEMPTS: ".$this->attempts;
$this->logger->debug($v);
}
}
protected function isTokenExpiredCode($code)
/**
* @return string
*/
protected function getTokenCacheKey(): string
{
return in_array($code, [4, 5, 6, 12, 13, 14]);
return self::TOKEN_CACHE_KEY.'_'.md5($this->connection->login.$this->connection->pass).'_'.$this->connection->version;
}
private function isTokenExpired($body)
/**
* @param object $body
* @return bool
*/
private function isTokenExpired($body): bool
{
return isset($body->error) && $this->isTokenExpiredCode($body->error->code);
return isset($body->error) && in_array($body->error->code, [4, 5, 6, 12, 13, 14], true);
}
}

View file

@ -8,14 +8,20 @@ use Symfony\Component\Validator\Mapping\ClassMetadata;
class Configuration implements ConfigurationInterface
{
/** @var Connection[] */
/**
* @var Connection[]
*/
public $connections;
/** @var boolean */
public $enabled;
/**
* @var boolean
*/
public $enabled = true;
/** @var bool */
public $debug;
/**
* @var bool
*/
public $debug = false;
/**
* @param ClassMetadata $metadata

View file

@ -2,6 +2,7 @@
namespace AtolOnlineClient\Configuration;
use AtolOnlineClient\AtolOnlineApi;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Mapping\ClassMetadata;
@ -14,13 +15,6 @@ class Connection
const SNO_ESN = 'esn';
const SNO_PATENT = 'patent';
const PLATFORMA_OFD = 'platforma_ofd';
const PERVIY_OFD = 'perviy_ofd';
const TAXCOM_OFD = 'taxcom_ofd';
const V3 = 'v3';
const V4 = 'v4';
const snoTypes = [
self::SNO_GENERAL,
self::SNO_USN_INCOME,
@ -30,20 +24,10 @@ class Connection
self::SNO_PATENT,
];
const ofdList = [
self::PLATFORMA_OFD,
self::PERVIY_OFD,
self::TAXCOM_OFD,
];
const versions = [
self::V3,
self::V4,
AtolOnlineApi::API_VERSION_V4,
];
/** @var string */
protected $url;
/** @var bool */
protected $debug = false;
@ -56,17 +40,11 @@ class Connection
/** @var string */
public $group;
/** @var boolean */
public $enabled;
/** @var string */
public $sno;
/** @var string */
public $ofd;
/** @var string */
public $version;
public $version = AtolOnlineApi::API_VERSION_V4;
/** @var bool */
public $testMode = false;
@ -74,7 +52,7 @@ class Connection
/**
* @return bool
*/
public function isTestMode()
public function isTestMode(): bool
{
return $this->testMode;
}
@ -82,7 +60,7 @@ class Connection
/**
* @param bool $testMode
*/
public function setTestMode(bool $testMode)
public function setTestMode(bool $testMode): void
{
$this->testMode = $testMode;
}
@ -106,7 +84,7 @@ class Connection
/**
* @param ClassMetadata $metadata
*/
public static function loadValidatorMetadata(ClassMetadata $metadata)
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
$metadata->addPropertyConstraint('login', new Assert\NotBlank());
$metadata->addPropertyConstraint('pass', new Assert\NotBlank());
@ -116,11 +94,7 @@ class Connection
'choices' => self::snoTypes,
])
]);
$metadata->addPropertyConstraints('ofd', [
new Assert\Choice([
'choices' => self::ofdList,
])
]);
$metadata->addPropertyConstraints('version', [
new Assert\Choice([
'choices' => self::versions,
@ -128,23 +102,6 @@ class Connection
]);
}
/**
* @return string
*/
public function getOfdAddress()
{
switch ($this->ofd) {
case self::PLATFORMA_OFD:
return 'platformaofd.ru';
case self::PERVIY_OFD:
return 'www.1-ofd.ru';
case self::TAXCOM_OFD:
return 'taxcom.ru/ofd/';
}
return null;
}
/**
* @return string
*/
@ -152,4 +109,12 @@ class Connection
{
return $this->version;
}
/**
* @return bool
*/
public function isVersion4()
{
return $this->getVersion() === AtolOnlineApi::API_VERSION_V4;
}
}

View file

@ -2,7 +2,8 @@
namespace AtolOnlineClient\Exception;
class AtolException extends \Exception
{
use Exception;
class AtolException extends Exception
{
}

View file

@ -0,0 +1,59 @@
<?php
namespace AtolOnlineClient\Exception;
/**
* Class InvalidResponseException
*
* @package AtolOnlineClient\Exception
*/
class InvalidResponseException extends AtolException
{
/**
* @var int|null
*/
protected $codeError;
/**
* @var string|null
*/
protected $messageError;
/**
* @return int|null
*/
public function getCodeError(): ?int
{
return $this->codeError;
}
/**
* @param int|null $codeError
* @return InvalidResponseException
*/
public function setCodeError(?int $codeError): InvalidResponseException
{
$this->codeError = $codeError;
return $this;
}
/**
* @return string|null
*/
public function getMessageError(): ?string
{
return $this->messageError;
}
/**
* @param string|null $messageError
* @return InvalidResponseException
*/
public function setMessageError(?string $messageError): InvalidResponseException
{
$this->messageError = $messageError;
return $this;
}
}

View file

@ -1,129 +0,0 @@
<?php
namespace AtolOnlineClient\Request\V3;
use JMS\Serializer\Annotation as Serializer;
class PaymentReceiptRequest
{
/**
* @var string
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("timestamp")
* @Serializer\Type("string")
*/
private $timestamp;
/**
* @var string
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("external_id")
* @Serializer\Type("string")
*/
private $externalId;
/**
* @var ServiceRequest
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("service")
* @Serializer\Type("AtolOnlineClient\Request\V3\ServiceRequest")
*/
private $service;
/**
* @var ReceiptRequest
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("receipt")
* @Serializer\Type("AtolOnlineClient\Request\V3\ReceiptRequest")
*/
private $receipt;
public function __construct()
{
$this->timestamp = (new \DateTime())->format('d.m.Y H:i:s');
}
/**
* @return string
*/
public function getTimestamp()
{
return $this->timestamp;
}
/**
* @param string $timestamp
*
* @return $this
*/
public function setTimestamp($timestamp)
{
$this->timestamp = $timestamp;
return $this;
}
/**
* @return string
*/
public function getExternalId()
{
return $this->externalId;
}
/**
* @param string $externalId
*
* @return $this
*/
public function setExternalId($externalId)
{
$this->externalId = $externalId;
return $this;
}
/**
* @return \AtolOnlineClient\Request\V3\ServiceRequest
*/
public function getService()
{
return $this->service;
}
/**
* @param \AtolOnlineClient\Request\V3\ServiceRequest $service
*
* @return $this
*/
public function setService($service)
{
$this->service = $service;
return $this;
}
/**
* @return ReceiptRequest
*/
public function getReceipt()
{
return $this->receipt;
}
/**
* @param ReceiptRequest $receipt
*
* @return $this
*/
public function setReceipt($receipt)
{
$this->receipt = $receipt;
return $this;
}
}

View file

@ -1,98 +0,0 @@
<?php
namespace AtolOnlineClient\Request\V3;
use JMS\Serializer\Annotation as Serializer;
class ReceiptAttributesRequest
{
/**
* @var string
*
* "enum": [
* "osn",
* "usn_income",
* "usn_income_outcome",
* "envd",
* "esn",
* "patent"
* ]
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("sno")
* @Serializer\Type("string")
*/
private $sno;
/**
* @var string
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("email")
* @Serializer\Type("string")
*/
private $email;
/**
* @var string
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("phone")
* @Serializer\Type("string")
*/
private $phone;
/**
* ReceiptAttributesRequest constructor.
* @param string $email
* @param string $phone
*/
public function __construct($email, $phone)
{
$this->email = $email;
if ($email == null) {
$this->email = '';
}
$this->phone = $phone;
if ($phone == null) {
$this->phone = '';
}
}
/**
* @return string
*/
public function getSno()
{
return $this->sno;
}
/**
* @param string $sno
*
* @return $this
*/
public function setSno($sno)
{
$this->sno = $sno;
return $this;
}
/**
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* @return string
*/
public function getPhone()
{
return $this->phone;
}
}

View file

@ -1,163 +0,0 @@
<?php
namespace AtolOnlineClient\Request\V3;
use JMS\Serializer\Annotation as Serializer;
class ReceiptItemRequest
{
/**
* @var string
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("name")
* @Serializer\Type("string")
*/
private $name;
/**
* @var float
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("price")
* @Serializer\Type("float")
*/
private $price;
/**
* @var integer
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("quantity")
* @Serializer\Type("integer")
*/
private $quantity;
/**
* @var float
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("sum")
* @Serializer\Type("float")
*/
private $sum;
/**
* @var string
*
* "enum": [
* "none",
* "vat0",
* "vat10",
* "vat18",
* "vat110",
* "vat118"
* ]
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("tax")
* @Serializer\Type("string")
*/
private $tax;
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*
* @return $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return float
*/
public function getPrice()
{
return $this->price;
}
/**
* @param float $price
*
* @return $this
*/
public function setPrice($price)
{
$this->price = $price;
return $this;
}
/**
* @return integer
*/
public function getQuantity()
{
return $this->quantity;
}
/**
* @param int $quantity
*
* @return $this
*/
public function setQuantity($quantity)
{
$this->quantity = $quantity;
return $this;
}
/**
* @return float
*/
public function getSum()
{
return $this->sum;
}
/**
* @param float $sum
*
* @return $this
*/
public function setSum($sum)
{
$this->sum = $sum;
return $this;
}
/**
* @return string
*/
public function getTax()
{
return $this->tax;
}
/**
* @param string $tax
*
* @return $this
*/
public function setTax($tax)
{
$this->tax = $tax;
return $this;
}
}

View file

@ -1,59 +0,0 @@
<?php
namespace AtolOnlineClient\Request\V3;
use JMS\Serializer\Annotation as Serializer;
class ReceiptPaymentRequest
{
const TYPE_ELECTRON = 1;
const TYPE_PREPAY = 2;
const TYPE_CREDIT = 3;
const TYPE_OTHER = 4;
/**
* @var int
* "enum": 0..9
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("type")
* @Serializer\Type("integer")
*/
private $type;
/**
* @var float
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("sum")
* @Serializer\Type("float")
*/
private $sum;
/**
* @param int $type
* @param float $sum
*/
public function __construct($type, $sum)
{
$this->type = $type;
$this->sum = $sum;
}
/**
* @return int
*/
public function getType()
{
return $this->type;
}
/**
* @return float
*/
public function getSum()
{
return $this->sum;
}
}

View file

@ -1,127 +0,0 @@
<?php
namespace AtolOnlineClient\Request\V3;
use JMS\Serializer\Annotation as Serializer;
class ReceiptRequest
{
/**
* @var ReceiptAttributesRequest
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("attributes")
* @Serializer\Type("AtolOnlineClient\Request\V3\ReceiptAttributesRequest")
*/
private $attributes;
/**
* @var ReceiptItemRequest[]
* "minItems": 1, "maxItems": 100,
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("items")
* @Serializer\Type("array<AtolOnlineClient\Request\V3\ReceiptItemRequest>")
*/
private $items;
/**
* @var float
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("total")
* @Serializer\Type("float")
*/
private $total;
/**
* @var \AtolOnlineClient\Request\V3\ReceiptPaymentRequest[]
* "minItems": 1, "maxItems": 10,
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("payments")
* @Serializer\Type("array<AtolOnlineClient\Request\V3\ReceiptPaymentRequest>")
*/
private $payments;
/**
* @return ReceiptAttributesRequest
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* @param ReceiptAttributesRequest $attributes
*
* @return $this
*/
public function setAttributes($attributes)
{
$this->attributes = $attributes;
return $this;
}
/**
* @return ReceiptItemRequest[]
*/
public function getItems()
{
return $this->items;
}
/**
* @param ReceiptItemRequest[] $items
*
* @return $this
*/
public function setItems($items)
{
$this->items = $items;
return $this;
}
/**
* @return float
*/
public function getTotal()
{
return $this->total;
}
/**
* @param float $total
*
* @return $this
*/
public function setTotal($total)
{
$this->total = $total;
return $this;
}
/**
* @return \AtolOnlineClient\Request\V3\ReceiptPaymentRequest[]
*/
public function getPayments()
{
return $this->payments;
}
/**
* @param \AtolOnlineClient\Request\V3\ReceiptPaymentRequest[] $payments
*
* @return $this
*/
public function setPayments($payments)
{
$this->payments = $payments;
return $this;
}
}

View file

@ -1,106 +0,0 @@
<?php
namespace AtolOnlineClient\Request\V3;
use JMS\Serializer\Annotation as Serializer;
class ServiceRequest
{
/**
* @var string
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("inn")
* @Serializer\Type("string")
*/
private $inn;
/**
* @var string
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("callback_url")
* @Serializer\Type("string")
*/
private $callbackUrl;
/**
* @var string
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("payment_address")
* @Serializer\Type("string")
*/
private $paymentAddress;
/**
* Service constructor.
* @param string $inn
* @param string $paymentAddress
*/
public function __construct($inn, $paymentAddress)
{
$this->inn = $inn;
$this->paymentAddress = $paymentAddress;
}
/**
* @return string
*/
public function getCallbackUrl()
{
return $this->callbackUrl;
}
/**
* @param string $callbackUrl
*
* @return $this
*/
public function setCallbackUrl($callbackUrl)
{
$this->callbackUrl = $callbackUrl;
return $this;
}
/**
* @return string
*/
public function getInn()
{
return $this->inn;
}
/**
* @param string $inn
*
* @return $this
*/
public function setInn($inn)
{
$this->inn = $inn;
return $this;
}
/**
* @return string
*/
public function getPaymentAddress()
{
return $this->paymentAddress;
}
/**
* @param string $paymentAddress
*
* @return $this
*/
public function setPaymentAddress($paymentAddress)
{
$this->paymentAddress = $paymentAddress;
return $this;
}
}

View file

@ -7,12 +7,12 @@ use JMS\Serializer\Annotation as Serializer;
/**
* В запросе обязательно должно быть заполнено хотя бы одно из полей: email или phone.
* Если заполнены оба поля, ОФД отправит электронный чек только на email.
* @Serializer\AccessType("public_method")
*/
class ClientReceiptRequest
{
/**
* @var string
* required
* @var string|null
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("email")
* @Serializer\Type("string")
@ -20,8 +20,7 @@ class ClientReceiptRequest
private $email;
/**
* @var string
* required
* @var string|null
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("phone")
* @Serializer\Type("string")
@ -29,25 +28,91 @@ class ClientReceiptRequest
private $phone;
public function __construct($email, $phone)
/**
* @var string|null
*
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("name")
* @Serializer\Type("string")
*/
private $name;
/**
* @var string|null
*
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("inn")
* @Serializer\Type("string")
*/
private $inn;
public function __construct(string $email = null, string $phone = null)
{
$this->email = $email;
$this->phone = $phone;
}
/**
* @return string
* @return string|null
*/
public function getEmail(): string
public function getEmail(): ?string
{
return $this->email;
}
/**
* @return string
* @param string|null $email
*/
public function getPhone(): string
public function setEmail(?string $email): void
{
$this->email = $email;
}
/**
* @return string|null
*/
public function getPhone(): ?string
{
return $this->phone;
}
/**
* @param string|null $phone
*/
public function setPhone(?string $phone): void
{
$this->phone = $phone;
}
/**
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* @param string|null $name
*/
public function setName(?string $name): void
{
$this->name = $name;
}
/**
* @return string|null
*/
public function getInn(): ?string
{
return $this->inn;
}
/**
* @param string|null $inn
*/
public function setInn(?string $inn): void
{
$this->inn = $inn;
}
}

View file

@ -4,6 +4,9 @@ namespace AtolOnlineClient\Request\V4;
use JMS\Serializer\Annotation as Serializer;
/**
* @Serializer\AccessType("public_method")
*/
class CompanyReceiptRequest
{
/**
@ -16,10 +19,10 @@ class CompanyReceiptRequest
private $email;
/**
* @var string
* @var string|null
* Поле необязательно, если у организации один тип налогообложения
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("phone")
* @Serializer\SerializedName("sno")
* @Serializer\Type("string")
*/
private $sno;
@ -59,17 +62,17 @@ class CompanyReceiptRequest
}
/**
* @return string
* @return string|null
*/
public function getSno(): string
public function getSno(): ?string
{
return $this->sno;
}
/**
* @param string $sno
* @param string|null $sno
*/
public function setSno(string $sno): void
public function setSno(?string $sno): void
{
$this->sno = $sno;
}

View file

@ -4,6 +4,9 @@ namespace AtolOnlineClient\Request\V4;
use JMS\Serializer\Annotation as Serializer;
/**
* @Serializer\AccessType("public_method")
*/
class PaymentReceiptRequest
{
/**
@ -44,7 +47,6 @@ class PaymentReceiptRequest
private $service;
public function __construct()
{
$this->timestamp = (new \DateTime())->format('d.m.Y H:i:s');
@ -90,6 +92,14 @@ class PaymentReceiptRequest
return $this->timestamp;
}
/**
* @param string $timestamp
*/
public function setTimestamp(string $timestamp): void
{
$this->timestamp = $timestamp;
}
/**
* @return ServiceRequest
*/

View file

@ -4,6 +4,9 @@ namespace AtolOnlineClient\Request\V4;
use JMS\Serializer\Annotation as Serializer;
/**
* @Serializer\AccessType("public_method")
*/
class ReceiptItemRequest
{
@ -50,11 +53,11 @@ class ReceiptItemRequest
private $price;
/**
* @var integer
* @var float
* required
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("quantity")
* @Serializer\Type("integer")
* @Serializer\Type("float")
*/
private $quantity;
@ -68,7 +71,7 @@ class ReceiptItemRequest
private $sum;
/**
* @var string
* @var string|null
*
* required
* @Serializer\Groups({"set", "get"})
@ -88,7 +91,7 @@ class ReceiptItemRequest
* «partial_payment» частичный расчет и кредит. Частичная оплата предмета расчета в момент его передачи с последующей оплатой в кредит.
* «credit» передача в кредит. Передача предмета расчета без его оплаты в момент его передачи с последующей оплатой в кредит.
* «credit_payment» оплата кредита. Оплата предмета расчета после его передачи с оплатой в кредит (оплата кредита).
* @var string
* @var string|null
*
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("payment_method")
@ -113,7 +116,7 @@ class ReceiptItemRequest
* «composite» составной предмет расчета. О предмете расчета, состоящем из предметов, каждому из которых может быть присвоено значение выше перечисленных признаков.
* «another» иной предмет расчета. О предмете расчета, не относящемуся к выше перечисленным предметам расчета
*
* @var string
* @var string|null
*
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("payment_object")
@ -131,6 +134,14 @@ class ReceiptItemRequest
*/
private $vat;
/**
* @var string|null
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("nomenclature_code")
* @Serializer\Type("string")
*/
private $nomenclatureCode;
/**
* @return string
*/
@ -164,17 +175,17 @@ class ReceiptItemRequest
}
/**
* @return int
* @return float
*/
public function getQuantity(): int
public function getQuantity(): float
{
return $this->quantity;
}
/**
* @param int $quantity
* @param float $quantity
*/
public function setQuantity(int $quantity): void
public function setQuantity(float $quantity): void
{
$this->quantity = $quantity;
}
@ -196,49 +207,49 @@ class ReceiptItemRequest
}
/**
* @return string
* @return string|null
*/
public function getMeasurementUnit(): string
public function getMeasurementUnit(): ?string
{
return $this->measurementUnit;
}
/**
* @param string $measurementUnit
* @param string|null $measurementUnit
*/
public function setMeasurementUnit(string $measurementUnit): void
public function setMeasurementUnit(?string $measurementUnit): void
{
$this->measurementUnit = $measurementUnit;
}
/**
* @return string
* @return string|null
*/
public function getPaymentMethod(): string
public function getPaymentMethod(): ?string
{
return $this->paymentMethod;
}
/**
* @param string $paymentMethod
* @param string|null $paymentMethod
*/
public function setPaymentMethod(string $paymentMethod): void
public function setPaymentMethod(?string $paymentMethod): void
{
$this->paymentMethod = $paymentMethod;
}
/**
* @return string
* @return string|null
*/
public function getPaymentObject(): string
public function getPaymentObject(): ?string
{
return $this->paymentObject;
}
/**
* @param string $paymentObject
* @param string|null $paymentObject
*/
public function setPaymentObject(string $paymentObject): void
public function setPaymentObject(?string $paymentObject): void
{
$this->paymentObject = $paymentObject;
}
@ -258,4 +269,20 @@ class ReceiptItemRequest
{
$this->vat = $vat;
}
/**
* @return string|null
*/
public function getNomenclatureCode(): ?string
{
return $this->nomenclatureCode;
}
/**
* @param string|null $nomenclatureCode
*/
public function setNomenclatureCode(?string $nomenclatureCode): void
{
$this->nomenclatureCode = $nomenclatureCode;
}
}

View file

@ -4,6 +4,9 @@ namespace AtolOnlineClient\Request\V4;
use JMS\Serializer\Annotation as Serializer;
/**
* @Serializer\AccessType("public_method")
*/
class ReceiptPaymentRequest
{
const TYPE_ELECTRON = 1;
@ -49,6 +52,14 @@ class ReceiptPaymentRequest
return $this->type;
}
/**
* @param int $type
*/
public function setType(int $type): void
{
$this->type = $type;
}
/**
* @return float
*/
@ -56,4 +67,12 @@ class ReceiptPaymentRequest
{
return $this->sum;
}
/**
* @param float $sum
*/
public function setSum(float $sum): void
{
$this->sum = $sum;
}
}

View file

@ -4,6 +4,9 @@ namespace AtolOnlineClient\Request\V4;
use JMS\Serializer\Annotation as Serializer;
/**
* @Serializer\AccessType("public_method")
*/
class ReceiptRequest
{
@ -47,9 +50,9 @@ class ReceiptRequest
private $payments;
/**
* @var \AtolOnlineClient\Request\V4\VatReceiptRequest[]
* @var \AtolOnlineClient\Request\V4\VatReceiptRequest[]|null
* "minItems": 1, "maxItems": 6,
* required
*
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("vats")
* @Serializer\Type("array<AtolOnlineClient\Request\V4\VatReceiptRequest>")
@ -130,17 +133,17 @@ class ReceiptRequest
}
/**
* @return VatReceiptRequest[]
* @return VatReceiptRequest[]|null
*/
public function getVats(): array
public function getVats(): ?array
{
return $this->vats;
}
/**
* @param VatReceiptRequest[] $vats
* @param VatReceiptRequest[]|null $vats
*/
public function setVats(array $vats): void
public function setVats(?array $vats): void
{
$this->vats = $vats;
}

View file

@ -4,6 +4,9 @@ namespace AtolOnlineClient\Request\V4;
use JMS\Serializer\Annotation as Serializer;
/**
* @Serializer\AccessType("public_method")
*/
class ServiceRequest
{

View file

@ -4,6 +4,9 @@ namespace AtolOnlineClient\Request\V4;
use JMS\Serializer\Annotation as Serializer;
/**
* @Serializer\AccessType("public_method")
*/
class VatReceiptRequest
{
/**
@ -12,9 +15,9 @@ class VatReceiptRequest
* "none",
* "vat0",
* "vat10",
* "vat18",
* "vat20",
* "vat110",
* "vat118"
* "vat120"
* ]
*
* @var string
@ -26,8 +29,8 @@ class VatReceiptRequest
private $type;
/**
* @var float
* required
* @var float|null
*
* @Serializer\Groups({"set", "get"})
* @Serializer\SerializedName("sum")
* @Serializer\Type("float")
@ -39,10 +42,10 @@ class VatReceiptRequest
* @param string $type
* @param float $sum
*/
public function __construct(string $type, string $sum)
public function __construct(string $type, $sum)
{
$this->type = $type;
$this->sum = $sum;
$this->sum = (float) $sum;
}
/**
@ -53,11 +56,27 @@ class VatReceiptRequest
return $this->type;
}
/**
* @param string $type
*/
public function setType(string $type): void
{
$this->type = $type;
}
/**
* @return float
*/
public function getSum(): string
public function getSum()
{
return $this->sum;
}
}
/**
* @param float $sum
*/
public function setSum(float $sum): void
{
$this->sum = $sum;
}
}

View file

@ -4,6 +4,9 @@ namespace AtolOnlineClient\Response;
use JMS\Serializer\Annotation as Serializer;
/**
* @Serializer\AccessType("public_method")
*/
class Error
{

View file

@ -4,6 +4,9 @@ namespace AtolOnlineClient\Response;
use JMS\Serializer\Annotation as Serializer;
/**
* @Serializer\AccessType("public_method")
*/
class OperationResponse
{
@ -223,7 +226,7 @@ class OperationResponse
/**
* @param string $groupCode
*/
public function setGroupCode(string $groupCode)
public function setGroupCode(?string $groupCode)
{
$this->groupCode = $groupCode;
}
@ -239,7 +242,7 @@ class OperationResponse
/**
* @param string $daemonCode
*/
public function setDaemonCode(string $daemonCode)
public function setDaemonCode(?string $daemonCode)
{
$this->daemonCode = $daemonCode;
}
@ -255,7 +258,7 @@ class OperationResponse
/**
* @param string $deviceCode
*/
public function setDeviceCode(string $deviceCode)
public function setDeviceCode(?string $deviceCode)
{
$this->deviceCode = $deviceCode;
}

View file

@ -4,6 +4,9 @@ namespace AtolOnlineClient\Response;
use JMS\Serializer\Annotation as Serializer;
/**
* @Serializer\AccessType("public_method")
*/
class Payload
{
/**
@ -86,6 +89,16 @@ class Payload
*/
private $fiscalDocumentAttribute;
/**
* Адрес сайта ФНС
*
* @var string
* @Serializer\Groups({"get"})
* @Serializer\SerializedName("fns_site")
* @Serializer\Type("string")
*/
private $fnsSite;
/**
* @return mixed
*/
@ -245,4 +258,24 @@ class Payload
return $this;
}
/**
* @return string
*/
public function getFnsSite()
{
return $this->fnsSite;
}
/**
* @param $string $fnsSite
*
* @return $this
*/
public function setFnsSite($fnsSite)
{
$this->fnsSite = $fnsSite;
return $this;
}
}

View file

@ -0,0 +1,415 @@
<?php
namespace AtolOnlineClient\AtolOnlineClient;
use AtolOnlineClient\AtolOnline;
use AtolOnlineClient\AtolOnlineApi;
use AtolOnlineClient\AtolOnlineClient\Traits\PaymentReceiptRequestTrait;
use AtolOnlineClient\Configuration\Connection;
use Doctrine\Common\Cache\ArrayCache;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Psr\Log\Test\TestLogger;
/**
* Class AtolOnlineApiTest
*
* @package AtolOnlineClient\AtolOnlineClient
*/
class AtolOnlineApiTest extends TestCase
{
use PaymentReceiptRequestTrait;
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::__construct
* @covers \AtolOnlineClient\AtolOnlineApi::setLogger
* @covers \AtolOnlineClient\AtolOnlineApi::setCache
* @covers \AtolOnlineClient\AtolOnlineApi::setVersion
* @covers \AtolOnlineClient\AtolOnlineApi::getVersion
*/
public function testCreateApi(): void
{
$api = new AtolOnlineApi(new Client(), new Connection());
$this->assertInstanceOf(AtolOnlineApi::class, $api);
$api->setVersion(AtolOnlineApi::API_VERSION_V4);
$this->assertSame(AtolOnlineApi::API_VERSION_V4, $api->getVersion());
$api->setLogger(new NullLogger());
$this->assertInstanceOf(NullLogger::class, $this->getProperty($api, 'logger'));
$api->setCache(new ArrayCache());
$this->assertInstanceOf(ArrayCache::class, $this->getProperty($api, 'cache'));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::sell
*/
public function testSell(): void
{
$responses = [
new Response(200, [], $this->getTokenSuccessResponseV4()),
new Response(200, [], $this->getReportSuccessReportV4()),
];
$request = (new AtolOnline())->serializeOperationRequest($this->getPaymentReceiptRequest());
$response = $this->getApi($responses)->sell($request);
$this->assertSame('2ea26f1708844f08b120306fc096a58f', json_decode($response, true)['uuid']);
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::sellRefund
*/
public function testSellRefund(): void
{
$responses = [
new Response(200, [], $this->getTokenSuccessResponseV4()),
new Response(200, [], $this->getReportSuccessReportV4()),
];
$request = (new AtolOnline())->serializeOperationRequest($this->getPaymentReceiptRequest());
$response = $this->getApi($responses)->sellRefund($request);
$this->assertSame('2ea26f1708844f08b120306fc096a58f', json_decode($response, true)['uuid']);
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::checkStatus
*/
public function testCheckStatusSuccessResponse(): void
{
$responses = [
new Response(200, [], $this->getTokenSuccessResponseV4()),
new Response(200, [], $this->getReportSuccessReportV4()),
];
$response = $this->callMethod($this->getApi($responses), 'checkStatus', [
'uuid' => '2ea26f1708844f08b120306fc096a58f',
]);
$this->assertSame('2ea26f1708844f08b120306fc096a58f', json_decode($response, true)['uuid']);
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::checkStatus
*/
public function testCheckStatusBadResponse(): void
{
$responses = [
new Response(200, [], $this->getTokenSuccessResponseV4()),
new BadResponseException('', new Request('GET', 'test'), new Response(200, [], $this->getErrorResponseV4())),
new Response(200, [], $this->getTokenSuccessResponseV4()),
new Response(200, [], $this->getReportSuccessReportV4()),
];
$api = $this->getApi($responses);
$api->setCache(new ArrayCache());
$response = $this->callMethod($api, 'checkStatus', [
'uuid' => '2ea26f1708844f08b120306fc096a58f',
]);
$this->assertSame('2ea26f1708844f08b120306fc096a58f', json_decode($response, true)['uuid']);
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::getUri
*/
public function testGetUri(): void
{
$this->assertSame('https://online.atol.ru/possystem', $this->callMethod($this->getApi(), 'getUri'));
$this->assertSame('https://testonline.atol.ru/possystem', $this->callMethod($this->getApi([], true, true), 'getUri'));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::getToken
*/
public function testGetTokenBadResponse(): void
{
$api = $this->getApi([new BadResponseException('', new Request('GET', 'test'), new Response())]);
$this->assertFalse($this->callMethod($api, 'getToken'));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::getToken
*/
public function testGetTokenSuccessResponse(): void
{
$api = $this->getApi([new Response(200, [], $this->getTokenSuccessResponseV4())]);
$this->assertSame('fj45u923j59ju42395iu9423i59243u0', $this->callMethod($api, 'getToken'));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::getToken
*/
public function testGetTokenErrorResponseWithLogger(): void
{
$logger = new TestLogger();
$api = $this->getApi([new Response(200, [], $this->getErrorResponseV4())]);
$api->setLogger($logger);
$this->assertFalse($this->callMethod($api, 'getToken'));
$this->assertTrue($logger->hasError('12 Неверный логин или пароль'));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::getToken
*/
public function testGetTokenBadResponseWithLogger(): void
{
$logger = new TestLogger();
$api = $this->getApi([new BadResponseException('', new Request('GET', 'test'), new Response(200, [], $this->getErrorResponseV4()))]);
$api->setLogger($logger);
$this->assertFalse($this->callMethod($api, 'getToken'));
$this->assertTrue($logger->hasError('{"error":{"error_id":"4475d6d8d-844d-4d05-aa8b-e3dbdf3defd5","code":12,"text":"\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043b\u043e\u0433\u0438\u043d \u0438\u043b\u0438 \u043f\u0430\u0440\u043e\u043b\u044c","type":"system"},"timestamp":"30.11.2017 17:58:53"}'));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::getToken
*/
public function testGetTokenSuccessResponseWithCache(): void
{
$cache = new ArrayCache();
$api = $this->getApi([new Response(200, [], $this->getTokenSuccessResponseV4())]);
$api->setCache($cache);
$this->assertSame('fj45u923j59ju42395iu9423i59243u0', $this->callMethod($api, 'getToken'));
$this->assertTrue($cache->contains($this->callMethod($api, 'getTokenCacheKey')));
$this->assertSame('fj45u923j59ju42395iu9423i59243u0', $this->callMethod($api, 'getToken'));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::buildUrl
*/
public function testBuildUrlWithoutToken(): void
{
$args = ['operation' => 'test'];
$this->assertSame('https://online.atol.ru/possystem/v4/group/test', $this->callMethod($this->getApi(), 'buildUrl', $args));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::buildUrl
*/
public function testBuildUrlWithToken(): void
{
$args = ['operation' => 'test', 'token' => 'test'];
$this->assertSame('https://online.atol.ru/possystem/v4/group/test?token=test', $this->callMethod($this->getApi(), 'buildUrl', $args));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::sendOperationRequest
*/
public function testSendOperationRequestSuccessResponse(): void
{
$responses = [
new Response(200, [], $this->getTokenSuccessResponseV4()),
new Response(200, [], $this->getTokenSuccessResponseV4())
];
$request = (new AtolOnline())->serializeOperationRequest($this->getPaymentReceiptRequest());
$response = $this->callMethod($this->getApi($responses), 'sendOperationRequest', [
'operation' => 'sell',
'data' => $request,
]);
$this->assertSame('fj45u923j59ju42395iu9423i59243u0', json_decode($response, true)['token']);
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::sendOperationRequest
*/
public function testSendOperationRequestBadResponse(): void
{
$responses = [
new Response(200, [], $this->getTokenSuccessResponseV4()),
new BadResponseException('', new Request('GET', 'test'), new Response(200, [], $this->getErrorResponseV4())),
new Response(200, [], $this->getTokenSuccessResponseV4()),
new Response(200, [], $this->getTokenSuccessResponseV4()),
];
$request = (new AtolOnline())->serializeOperationRequest($this->getPaymentReceiptRequest());
$api = $this->getApi($responses);
$api->setCache(new ArrayCache());
$response = $this->callMethod($api, 'sendOperationRequest', [
'operation' => 'sell',
'data' => $request,
]);
$this->assertSame('fj45u923j59ju42395iu9423i59243u0', json_decode($response, true)['token']);
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::logDebug
*/
public function testLogDebug(): void
{
$logger = new TestLogger();
$api = $this->getApi();
$api->setLogger($logger);
$this->callMethod($api, 'logDebug', [
'url' => '/test',
'data' => 'test',
'response' => new Response(200, ['X-Foo' => 'Bar', 'X-Foo2' => 'Bar2'], 'test'),
]);
$this->assertTrue($logger->hasDebug('* URL: /test
* POSTFIELDS: test
* RESPONSE HEADERS: X-Foo: Bar, X-Foo2: Bar2
* RESPONSE BODY: test
* ATTEMPTS: 0'));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::getTokenCacheKey
*/
public function testGetTokenCacheKey(): void
{
$this->assertSame('crm_fiscal_atol_online_token_68526766e6751745b52ae70b7bd3c6fe_v4', $this->callMethod($this->getApi(), 'getTokenCacheKey'));
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnlineApi::isTokenExpired
*/
public function testIsTokenExpired(): void
{
$body = new \stdClass();
$body->error = new \stdClass();
$body->error->code = 4;
$this->assertTrue($this->callMethod($this->getApi(), 'isTokenExpired', ['body' => $body]));
$body = new \stdClass();
$this->assertFalse($this->callMethod($this->getApi(), 'isTokenExpired', ['body' => $body]));
}
/**
* @param array $responses
* @param bool $debug
* @param bool $test
* @return AtolOnlineApi
*/
private function getApi(array $responses = [], bool $debug = true, bool $test = false): AtolOnlineApi
{
$connection = new Connection();
$connection->login = 'login';
$connection->pass = 'pass';
$connection->group = 'group';
$connection->sno = Connection::SNO_GENERAL;
$connection->version = AtolOnlineApi::API_VERSION_V4;
$connection->setDebug($debug);
$connection->setTestMode($test);
$mock = new MockHandler($responses);
$handler = HandlerStack::create($mock);
return new AtolOnlineApi(new Client(['handler' => $handler]), $connection);
}
/**
* @param mixed $object
* @param string $name
* @param array $args
* @return mixed
*/
private function callMethod($object, string $name, array $args = [])
{
$reflection = new \ReflectionClass($object);
$method = $reflection->getMethod($name);
$method->setAccessible(true);
return $method->invokeArgs($object, $args);
}
/**
* @param mixed $object
* @param string $name
* @return mixed
*/
private function getProperty($object, string $name)
{
$reflection = new \ReflectionClass($object);
$property = $reflection->getProperty($name);
$property->setAccessible(true);
return $property->getValue($object);
}
/**
* @return false|string
*/
private function getTokenSuccessResponseV4()
{
return json_encode([
'error' => null,
'token' => 'fj45u923j59ju42395iu9423i59243u0',
'timestamp' => '30.11.2017 17:58:53',
]);
}
/**
* @return false|string
*/
private function getErrorResponseV4()
{
return json_encode([
'error' => [
'error_id' => '4475d6d8d-844d-4d05-aa8b-e3dbdf3defd5',
'code' => 12,
'text' => 'Неверный логин или пароль',
'type' => 'system',
],
'timestamp' => '30.11.2017 17:58:53',
]);
}
/**
* @return string
*/
private function getReportSuccessReportV4(): string
{
return file_get_contents(__DIR__.'/../fixtures/success_response_v4_3.json');
}
}

View file

@ -0,0 +1,259 @@
<?php
namespace AtolOnlineClient\Tests;
use AtolOnlineClient\AtolOnline;
use AtolOnlineClient\AtolOnlineClient\Traits\PaymentReceiptRequestTrait;
use AtolOnlineClient\Configuration\Connection;
use AtolOnlineClient\ConfigurationInterface;
use AtolOnlineClient\Exception\InvalidResponseException;
use GuzzleHttp\Client;
use JMS\Serializer\Exception\RuntimeException;
use JMS\Serializer\SerializerInterface;
use PHPUnit\Framework\TestCase;
class AtolOnlineTest extends TestCase
{
use PaymentReceiptRequestTrait;
/**
* @var AtolOnline
*/
protected $atol;
/**
* @return void
*/
public function setUp(): void
{
$this->atol = new AtolOnline();
}
/**
* @return void
* @covers \AtolOnlineClient\AtolOnline::__construct
*/
public function testConstructor(): void
{
$reflection = new \ReflectionClass(get_class($this->atol));
$property = $reflection->getProperty('serializer');
$property->setAccessible(true);
$this->assertInstanceOf(SerializerInterface::class, $property->getValue($this->atol));
}
/**
* @covers \AtolOnlineClient\AtolOnline::createConfiguration
* @return void
*/
public function testCreateConfiguration(): void
{
$this->assertInstanceOf(ConfigurationInterface::class, $this->atol->createConfiguration());
}
/**
* @param string $file
* @covers \AtolOnlineClient\AtolOnline::deserializeOperationResponse
* @dataProvider dataSuccessResponse
*/
public function testSuccessDeserializeOperationResponse(string $file): void
{
$response = file_get_contents(__DIR__.'/../fixtures/'. $file);
$operationResponse = $this->atol->deserializeOperationResponse($response);
$this->assertEquals('12.04.2017 06:15:06', $operationResponse->getTimestamp());
}
/**
* @param string $file
* @covers \AtolOnlineClient\AtolOnline::deserializeOperationResponse
* @dataProvider dataErrorResponse
*/
public function testErrorDeserializeOperationResponse(string $file): void
{
$response = file_get_contents(__DIR__.'/../fixtures/'. $file);
$operationResponse = $this->atol->deserializeOperationResponse($response);
$this->assertEquals('12.04.2017 06:15:06', $operationResponse->getTimestamp());
$this->assertEquals('fail', $operationResponse->getStatus());
$this->assertEquals(30, $operationResponse->getError()->getCode());
$this->assertEquals('system', $operationResponse->getError()->getType());
$this->assertEquals(
' Передан некорректный UUID : "{0}". Необходимо повторить запрос с корректными данными ',
$operationResponse->getError()->getText()
);
}
/**
* @covers \AtolOnlineClient\AtolOnline::deserializeOperationResponse
*/
public function testFailDeserializeOperationResponse(): void
{
$this->expectException(InvalidResponseException::class);
$this->atol->deserializeOperationResponse('<html></html>');
}
/**
* @param string $file
* @covers \AtolOnlineClient\AtolOnline::deserializeCheckStatusResponse
* @dataProvider dataSuccessResponse
*/
public function testSuccessDeserializeCheckStatusResponse(string $file): void
{
$response = file_get_contents(__DIR__.'/../fixtures/'. $file);
$operationResponse = $this->atol->deserializeCheckStatusResponse($response);
$this->assertEquals('12.04.2017 06:15:06', $operationResponse->getTimestamp());
}
/**
* @covers \AtolOnlineClient\AtolOnline::deserializeCheckStatusResponse
*/
public function testFailDeserializeCheckStatusResponse(): void
{
$this->expectException(InvalidResponseException::class);
$this->atol->deserializeCheckStatusResponse('<html></html>');
}
/**
* @param int|null $code
* @param string|null $message
* @param string $html
* @covers \AtolOnlineClient\AtolOnline::createInvalidResponseException
* @covers \AtolOnlineClient\Exception\InvalidResponseException
* @dataProvider dataInvalidResponse
*/
public function testCreateInvalidResponseException(?int $code, ?string $message, string $html): void
{
/** @var InvalidResponseException $exception */
$exception = $this->callMethod($this->atol, 'createInvalidResponseException', [
'response' => $html,
'previous' => new RuntimeException()
]);
$this->assertInstanceOf(InvalidResponseException::class, $exception);
$this->assertEquals($code, $exception->getCodeError());
$this->assertEquals($message, $exception->getMessageError());
}
/**
* @covers \AtolOnlineClient\AtolOnline::serializeOperationRequest
* @covers \AtolOnlineClient\Request\V4\ClientReceiptRequest
* @covers \AtolOnlineClient\Request\V4\CompanyReceiptRequest
* @covers \AtolOnlineClient\Request\V4\PaymentReceiptRequest
* @covers \AtolOnlineClient\Request\V4\ReceiptItemRequest
* @covers \AtolOnlineClient\Request\V4\ReceiptPaymentRequest
* @covers \AtolOnlineClient\Request\V4\ReceiptRequest
* @covers \AtolOnlineClient\Request\V4\ServiceRequest
* @covers \AtolOnlineClient\Request\V4\VatReceiptRequest
*/
public function testSerializeOperationRequest(): void
{
$request = $this->getPaymentReceiptRequest();
$request->setTimestamp('17.07.2019 10:14:22');
$this->assertEquals(
'{"external_id":"test","receipt":{"client":{"email":"test@test.local"},"company":{"email":"test@test.local","inn":"11111111","payment_address":"address"},"items":[{"name":"test item","price":100.1,"quantity":1.1,"sum":100.1,"measurement_unit":"kg","payment_method":"advance","payment_object":"agent_commission","vat":{"type":"vat20","sum":20.2},"nomenclature_code":"00"}],"payments":[{"type":0,"sum":100.1}],"vats":[{"type":"vat20","sum":20.2}],"total":100.1},"timestamp":"17.07.2019 10:14:22","service":{"callback_url":"test.local"}}',
$this->atol->serializeOperationRequest($request)
);
}
/**
* @return void
*
* @covers \AtolOnlineClient\AtolOnline::createApi
*/
public function testCreateApi(): void
{
$api1 = $this->atol->createApi(new Client(), new Connection());
$api2 = $this->atol->createApi(new Client(), new Connection());
$this->assertSame($api1, $api2);
}
/**
* @return void
*
* @covers \AtolOnlineClient\AtolOnline::getApi
*/
public function testGetApi(): void
{
$api1 = $this->atol->createApi(new Client(), new Connection());
$api2 = $this->atol->getApi();
$this->assertSame($api1, $api2);
}
/**
* @return array
*/
public function dataSuccessResponse(): array
{
return [
['success_response_v4_1.json'],
['success_response_v4_2.json'],
['success_response_v4_3.json'],
];
}
/**
* @return array
*/
public function dataErrorResponse(): array
{
return [
['error_response_v4.json']
];
}
/**
* @return array
*/
public function dataInvalidResponse(): array
{
return [
[
'code' => 404,
'message' => 'Not Found',
'html' => '<html><head><title>404 Not Found</title></head><body><center><h1>404 Not Found</h1></center><hr><center>openresty/1.15.8.1rc1</center></body></html>',
],
[
'code' => 502,
'message' => 'Bad Gateway',
'html' => '<html><head><title>502 Bad Gateway</title></head><body><center><h1>502 Bad Gateway</h1></center><hr><center>nginx/1.15.8</center></body></html>',
],
[
'code' => null,
'message' => null,
'html' => '<html></html>',
],
[
'code' => null,
'message' => null,
'html' => '',
],
];
}
/**
* @param mixed $object
* @param string $name
* @param array $args
* @return mixed
*/
private function callMethod($object, string $name, array $args = [])
{
$reflection = new \ReflectionClass($object);
$method = $reflection->getMethod($name);
$method->setAccessible(true);
return $method->invokeArgs($object, $args);
}
}

View file

@ -0,0 +1,85 @@
<?php
namespace AtolOnlineClient\AtolOnlineClient\Connection;
use AtolOnlineClient\AtolOnlineApi;
use AtolOnlineClient\Configuration\Connection;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Validation;
class ConnectionTest extends TestCase
{
/**
* @var Connection
*/
private $connection;
/**
* @return void
*/
public function setUp(): void
{
$this->connection = new Connection();
}
/**
* @return void
* @covers \AtolOnlineClient\Configuration\Connection::setDebug
* @covers \AtolOnlineClient\Configuration\Connection::isDebug
*/
public function testDebug(): void
{
$this->assertFalse($this->connection->isDebug());
$this->connection->setDebug(true);
$this->assertTrue($this->connection->isDebug());
}
/**
* @return void
* @covers \AtolOnlineClient\Configuration\Connection::isTestMode
* @covers \AtolOnlineClient\Configuration\Connection::setTestMode
*/
public function testTestMode(): void
{
$this->assertFalse($this->connection->isTestMode());
$this->connection->setTestMode(true);
$this->assertTrue($this->connection->isTestMode());
}
/**
* @return void
* @covers \AtolOnlineClient\Configuration\Connection::getVersion
* @covers \AtolOnlineClient\Configuration\Connection::isVersion4
*/
public function testVersion(): void
{
$this->connection->version = AtolOnlineApi::API_VERSION_V4;
$this->assertTrue($this->connection->isVersion4());
}
/**
* @return void
* @covers \AtolOnlineClient\Configuration\Connection::loadValidatorMetadata
*/
public function testValidator(): void
{
$validator = Validation::createValidatorBuilder()
->addMethodMapping('loadValidatorMetadata')
->getValidator();
$list = $validator->validate($this->connection);
$this->assertEquals(3, $list->count());
$this->connection->login = 'login';
$this->connection->pass = 'login';
$this->connection->group = 'group';
$list = $validator->validate($this->connection);
$this->assertEquals(0, $list->count());
}
}

View file

@ -0,0 +1,78 @@
<?php
namespace AtolOnlineClient\AtolOnlineClient;
use AtolOnlineClient\Configuration;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Validation;
/**
* Class ConfigurationTest
*
* @package AtolOnlineClient\AtolOnlineClient
*/
class ConfigurationTest extends TestCase
{
/**
* @var Configuration
*/
private $configuration;
public function setUp(): void
{
$this->configuration = new Configuration();
}
/**
* @return void
* @covers \AtolOnlineClient\Configuration::isDebug
* @covers \AtolOnlineClient\Configuration::setDebug
*/
public function testDebug(): void
{
$this->assertFalse($this->configuration->isDebug());
$this->configuration->setDebug(true);
$this->assertTrue($this->configuration->isDebug());
}
/**
* @return void
* @covers \AtolOnlineClient\Configuration::isEnabled
* @covers \AtolOnlineClient\Configuration::setEnabled
*/
public function testEnabled(): void
{
$this->assertTrue($this->configuration->isEnabled());
$this->configuration->setEnabled(false);
$this->assertFalse($this->configuration->isEnabled());
}
/**
* @return void
* @covers \AtolOnlineClient\Configuration::loadValidatorMetadata
*/
public function testValidator(): void
{
$validator = Validation::createValidatorBuilder()
->addMethodMapping('loadValidatorMetadata')
->getValidator();
$list = $validator->validate($this->configuration);
$this->assertEquals(1, $list->count());
$connection = new Configuration\Connection();
$connection->login = 'login';
$connection->pass = 'login';
$connection->group = 'group';
$this->configuration->connections[] = $connection;
$list = $validator->validate($this->configuration);
$this->assertEquals(0, $list->count());
}
}

View file

@ -0,0 +1,102 @@
<?php
namespace AtolOnlineClient\AtolOnlineClient\Response;
use AtolOnlineClient\Request\V4\PaymentReceiptRequest;
use JMS\Serializer\SerializerBuilder;
use PHPUnit\Framework\TestCase;
/**
*/
class RequestTest extends TestCase
{
/**
* @return void
* @covers \AtolOnlineClient\Request\V4\ClientReceiptRequest
* @covers \AtolOnlineClient\Request\V4\CompanyReceiptRequest
* @covers \AtolOnlineClient\Request\V4\PaymentReceiptRequest
* @covers \AtolOnlineClient\Request\V4\ReceiptItemRequest
* @covers \AtolOnlineClient\Request\V4\ReceiptPaymentRequest
* @covers \AtolOnlineClient\Request\V4\ReceiptRequest
* @covers \AtolOnlineClient\Request\V4\ServiceRequest
* @covers \AtolOnlineClient\Request\V4\VatReceiptRequest
*/
public function testRequest(): void
{
$serializer = SerializerBuilder::create()->build();
$request = '{
"external_id": "17052917561851307",
"receipt": {
"client": {
"name": "name",
"email": "kkt@kkt.ru",
"phone": "88002000600",
"inn": "111111111"
},
"company": {
"email": "chek@romashka.ru",
"sno": "osn",
"inn": "1234567891",
"payment_address": "http://magazin.ru/"
},
"items": [
{
"name": "колбаса Клинский Брауншвейгская с/к в/с ",
"price": 1000,
"quantity": 0.3,
"sum": 300,
"measurement_unit": "кг",
"payment_method": "full_payment",
"payment_object": "commodity",
"vat": {
"type": "vat18"
}
},
{
"name": "яйцоОкскоекуриноеС0 белое",
"price": 100,
"quantity": 1,
"sum": 100,
"measurement_unit": "Упаковка10 шт.",
"payment_method": "full_payment",
"payment_object": "commodity",
"vat": {
"type": "vat10"
}
}
],
"payments": [
{
"type": 1,
"sum": 400
}
],
"vats": [
{
"type": "vat18",
"sum": 45.76
},
{
"type": "vat10",
"sum": 9.09
}
],
"total": 400
},
"service": {
"callback_url": "http://testtest"
},
"timestamp": "01.02.1713:45:00"
}';
/** @var PaymentReceiptRequest $request */
$request = $serializer->deserialize(
$request,
PaymentReceiptRequest::class,
'json'
);
$this->assertSame('17052917561851307', $request->getExternalId());
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace AtolOnlineClient\AtolOnlineClient\Response;
use AtolOnlineClient\Response\OperationResponse;
use JMS\Serializer\SerializerBuilder;
use PHPUnit\Framework\TestCase;
/**
*/
class ResponseTest extends TestCase
{
/**
* @return void
* @covers \AtolOnlineClient\Response\OperationResponse
* @covers \AtolOnlineClient\Response\Error
* @covers \AtolOnlineClient\Response\Payload
*/
public function testResponse(): void
{
$serializer = SerializerBuilder::create()->build();
$response = '{
"uuid": "4355",
"timestamp": "12.04.2017 06:15:06",
"status": "fail",
"error": {
"error_id": "475d6d8d-844d-4d05-aa8b-e3dbdf4defd6",
"code": 30,
"text": " Передан некорректный UUID : \"{0}\". Необходимо повторить запрос с корректными данными ",
"type": "system"
},
"payload": {
"total": 1598,
"fns_site": "www.nalog.ru",
"fn_number": "1110000100238211",
"shift_number": 23,
"receipt_datetime": "12.04.2017 20:16:00",
"fiscal_receipt_number": 6,
"fiscal_document_number": 133,
"ecr_registration_number": "0000111118041361",
"fiscal_document_attribute": 3449555941
},
"group_code": " MyCompany_MyShop",
"daemon_code": "prodagent1",
"device_code": "KSR13.00111",
"external_id": "TRF10601_1",
"callback_url": ""
}';
/** @var OperationResponse $response */
$response = $serializer->deserialize(
$response,
OperationResponse::class,
'json'
);
$serializer->serialize(
$response,
'json'
);
$this->assertSame('4355', $response->getUuid());
}
}

View file

@ -1,46 +0,0 @@
<?php
namespace AtolOnlineClient\Tests;
use AtolOnlineClient\AtolOnline;
use AtolOnlineClient\Configuration;
use AtolOnlineClient\Response\OperationResponse;
use PHPUnit\Framework\TestCase;
class AtolOnlineTest extends TestCase
{
public function testCreateConfiguration()
{
$atol = new AtolOnline();
$this->assertInstanceOf(Configuration::class, $atol->createConfiguration());
}
/**
* @dataProvider dataSellErrorResponse
*/
public function testDeserializeOperationResponse($file)
{
$response = file_get_contents(__DIR__ . '/data/'. $file);
$atol = new AtolOnline();
$operationResponse = $atol->deserializeOperationResponse($response);
$this->assertInstanceOf(OperationResponse::class, $operationResponse);
$this->assertEquals('12.04.2017 06:15:06', $operationResponse->getTimestamp());
$this->assertEquals('fail', $operationResponse->getStatus());
$this->assertEquals(30, $operationResponse->getError()->getCode());
$this->assertEquals('system', $operationResponse->getError()->getType());
$this->assertEquals(
' Передан некорректный UUID : "{0}". Необходимо повторить запрос с корректными данными ',
$operationResponse->getError()->getText()
);
}
public function dataSellErrorResponse()
{
return [
['sell_error_response_v3.json'],
['sell_error_response_v3.json']
];
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace AtolOnlineClient\AtolOnlineClient\Traits;
use AtolOnlineClient\Request\V4\ClientReceiptRequest;
use AtolOnlineClient\Request\V4\CompanyReceiptRequest;
use AtolOnlineClient\Request\V4\PaymentReceiptRequest;
use AtolOnlineClient\Request\V4\ReceiptItemRequest;
use AtolOnlineClient\Request\V4\ReceiptPaymentRequest;
use AtolOnlineClient\Request\V4\ReceiptRequest;
use AtolOnlineClient\Request\V4\ServiceRequest;
use AtolOnlineClient\Request\V4\VatReceiptRequest;
trait PaymentReceiptRequestTrait
{
/**
* @return PaymentReceiptRequest
*/
protected function getPaymentReceiptRequest(): PaymentReceiptRequest
{
$service = new ServiceRequest();
$service->setCallbackUrl('test.local');
$client = new ClientReceiptRequest('test@test.local');
$company = new CompanyReceiptRequest();
$company->setEmail('test@test.local');
$company->setInn('11111111');
$company->setPaymentAddress('address');
$item = new ReceiptItemRequest();
$item->setName('test item');
$item->setPrice(100.1);
$item->setQuantity(1.1);
$item->setSum(100.1);
$item->setMeasurementUnit('kg');
$item->setPaymentMethod(ReceiptItemRequest::PAYMENT_METHOD_ADVANCE);
$item->setPaymentObject(ReceiptItemRequest::PAYMENT_OBJECT_AGENT_COMMISSION);
$item->setVat(new VatReceiptRequest('vat20', 20.2));
$item->setNomenclatureCode('00');
$receipt = new ReceiptRequest();
$receipt->setTotal(100.1);
$receipt->setClient($client);
$receipt->setCompany($company);
$receipt->setItems([$item]);
$receipt->setPayments([new ReceiptPaymentRequest(0, 100.1)]);
$receipt->setVats([new VatReceiptRequest('vat20', 20.2)]);
/** @var PaymentReceiptRequest $request */
$request = new PaymentReceiptRequest();
$request->setExternalId('test');
$request->setService($service);
$request->setReceipt($receipt);
return $request;
}
}

3
tests/bootstrap.php Normal file
View file

@ -0,0 +1,3 @@
<?php
return require __DIR__.'/../vendor/autoload.php';

View file

@ -0,0 +1,5 @@
{
"error": null,
"token": "fj45u923j59ju42395iu9423i59243u0",
"timestamp": "12.04.2017 06:15:06"
}

View file

@ -0,0 +1,6 @@
{
"uuid": "2ea26f1708844f08b120306fc096a58f",
"timestamp": "12.04.2017 06:15:06",
"error": null,
"status": "wait"
}

View file

@ -0,0 +1,22 @@
{
"uuid": "2ea26f1708844f08b120306fc096a58f",
"error": null,
"status": "done",
"payload": {
"total": 1598,
"fns_site": "www.nalog.ru",
"fn_number": "1110000100238211",
"shift_number": 23,
"receipt_datetime": "12.04.2017 20:16:00",
"fiscal_receipt_number": 6,
"fiscal_document_number": 133,
"ecr_registration_number": "0000111118041361",
"fiscal_document_attribute": 3449555941
},
"timestamp": "12.04.2017 06:15:06",
"group_code": "MyCompany_MyShop",
"daemon_code": "prodagent1",
"device_code": "KSR13.00111",
"external_id": "TRF10601_1",
"callback_url": ""
}