33 Commits

Author SHA1 Message Date
bb05f0c752 Merge branch 'master' into dev 2021-05-25 00:58:53 +08:00
1c76608468 Github Actions разграничены для веток master и dev 2021-05-24 23:40:46 +08:00
bd6e208216 Обновление composer.json и первичных текстовок 2021-05-24 23:00:34 +08:00
7b4411ec01 Обновление зависимостей 2021-05-24 22:45:03 +08:00
23fa1f7eb9 Update FUNDING.yml 2021-05-24 12:38:39 +08:00
929bf84c97 Merge pull request #3 from komantnick/develop
Исправление багов при отправке сырого JSON для чека коррекции с исправлениями
2021-05-24 10:55:09 +08:00
Nikita Saiapin
5c1c4dba12 Фикс размера НДС 2021-05-23 22:35:45 +08:00
Nikita Saiapin
4c40bebe14 Исправление багов при отправке сырого JSON чека коррекции 2021-05-22 21:48:12 +08:00
d321205ac9 Фикс выброса AtolAuthFailedException 2020-10-13 02:06:13 +08:00
387e6e445f Удалён сервер discord и его упоминание в README 2020-06-14 13:52:07 +08:00
3bd043bde7 Kkt::setCallbackUrl() - Фикс проверки callback_url по регулярке 2020-06-14 13:44:35 +08:00
7c8ee85b89 Фикс регулярки callback_url 2020-06-14 13:44:04 +08:00
aab68646e6 Фикс сообщений в исключениях 2020-06-14 13:43:32 +08:00
e1fb74ac01 Доудалено упоминание о Schemas 2020-06-07 20:28:14 +08:00
8d9f218280 Мелкофиксы по документации и roadmap 2020-06-07 20:12:50 +08:00
9ed999f9fc Улучшен класс Kkt
+ setCallbackUrl(): валидация
* setLogin() и setPassword() более не зависят от тестового режима
* setPassword(): выброс AtolKktPasswordTooLongException
* auth(): выброс AtolAuthFailedException
* valid_strlen()
* Constraints::MAX_LENGTH_LOGIN
* Constraints::MAX_LENGTH_PASSWORD
* Constraints::PATTERN_CALLBACK_URL
* phpdoc
* исправление опечаток при выбросе некоторых исключений
2020-06-07 20:12:50 +08:00
989c06a383 Улучшен класс Item
* valid_strlen()
* Constraints::MAX_LENGTH_ITEM_NAME
* Constraints::MAX_LENGTH_MEASUREMENT_UNIT
* Constraints::MAX_LENGTH_USER_DATA
2020-06-07 20:12:23 +08:00
9bd99c81a9 Улучшен класс Document
* валидация в setCashier() только если передан не-null
* valid_strlen()
* Constraints::MAX_LENGTH_CASHIER_NAME
* phpdoc
2020-06-07 20:12:23 +08:00
2943d93962 Улучшен класс Company
* valid_strlen()
* Constraints::MAX_LENGTH_PAYMENT_ADDRESS
* phpdoc
2020-06-07 20:12:23 +08:00
8eb309bc58 Улучшен класс Сlient
* valid_strlen()
* Constraints::MAX_LENGTH_CLIENT_NAME
* Constraints::MAX_LENGTH_CLIENT_PHONE
* phpdoc
2020-06-07 20:12:23 +08:00
b74f652127 Улучшен trait HasInn
* Constraints::PATTERN_INN
* phpdoc
2020-06-07 20:12:23 +08:00
1061914a5f Улучшен trait HasEmail
* valid_strlen()
* Constraints::MAX_LENGTH_EMAIL
* phpdoc
2020-06-07 20:12:23 +08:00
5424726a97 Мелкофиксы в исключениях
* использование valid_strlen() в AtolInnWrongLengthException и AtolTooLongException
* фикс опечатки в AtolEmailTooLongException
2020-06-07 20:12:23 +08:00
2b3713db69 Новые исключения
+ AtolAuthFailedException
+ AtolCallbackUrlTooLongException
+ AtolInvalidCallbackUrlException
+ AtolKktPasswordTooLongException
2020-06-07 20:12:23 +08:00
a6b57115b6 Новый класс Constraints 2020-06-07 20:12:23 +08:00
12e0e49c9b Удалёно всё, что связано со схемами 2020-06-07 20:12:22 +08:00
3ffab562f8 Новый файл helpers 2020-06-07 20:12:22 +08:00
c05e013a5a Освежил документацию в части работы с объектом Kkt 2020-06-04 22:29:36 +08:00
e282de7e08 Вернул назад переопределение параметров компании при тестовом режиме: ИНН, адрес места расчётов
Также появился новый класс с константами тестовых параметров
2020-06-04 22:12:34 +08:00
c14b680be4 Проброс external_id на регистрацию документа 2020-06-01 04:12:23 +08:00
7558cb6638 Убрал схему Атола валидации количества НДС, оплат и предметов 2020-06-01 03:58:31 +08:00
d3fe2cba9a Удалена перезапись компании в тестовом режиме. Новый метод Kkt::setAuthToken() 2020-06-01 03:26:25 +08:00
e70a65fa44 Улучшен метод Kkt::registerDocument
+ теперь принимает любой собственного external_id, если пусто - сгенерирует uuid
* исправлен недостаток полей компании в тестовом режиме
* уточнения phpdoc
2020-05-30 04:29:44 +08:00
39 changed files with 1269 additions and 537 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
custom: ['https://money.yandex.ru/to/41001685237530']
custom: ['https://yoomoney.ru/to/41001685237530']

25
.github/workflows/dev.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Dev build
on:
push:
branches: [ dev ]
pull_request:
branches: [ dev ]
jobs:
Tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Validate composer.json and composer.lock
uses: php-actions/composer@40-env
with:
version: 2
php_version: 7.4
only_args: --prefer-dist --no-progress
- name: Run phpunit tests
uses: php-actions/phpunit@v8
with:
configuration: ./phpunit.xml

25
.github/workflows/master.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Master build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
Tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Validate composer.json and composer.lock
uses: php-actions/composer@40-env
with:
version: 2
php_version: 7.4
only_args: --prefer-dist --no-progress
- name: Run phpunit tests
uses: php-actions/phpunit@v8
with:
configuration: ./phpunit.xml

View File

@@ -1,26 +0,0 @@
name: Build
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Validate composer.json and composer.lock
run: composer validate
- name: Install composer dependencies
run: composer install --prefer-dist --no-progress --no-suggest
- name: Run phpunit tests
uses: php-actions/phpunit@v1.0.0
with:
# Configuration file location
config: ./phpunit.xml

View File

@@ -10,7 +10,7 @@
## Системные требования
* PHP 7.2+
* PHP 7.4+
* composer
* php-json
@@ -103,8 +103,6 @@
## Дополнительные ресурсы
**Discord-сервер для обсуждения этой библиотеки: [discord.gg/mFYTQmp](https://discord.gg/mFYTQmp)**
Функционал, находящийся в разработке: [ROADMAP.md](ROADMAP.md)
Официальные ресурсы АТОЛ Онлайн:

View File

@@ -25,9 +25,8 @@
- [ ] Тесты для регистрации документа расхода
- [ ] Тесты для регистрации документа возврата расхода
- [ ] Тесты для регистрации документа коррекции расхода
- [ ] Вообще все расчёты вообще везде должны быть строго в копейках. Рубли (дроби) должны быть только в JSON-представлениях
- [ ] Валидатор схемы для документов прихода, возврата прихода, расхода, возврата расхода
- [ ] Валидатор схемы для документов коррекции прихода, коррекции расхода
- [ ] Вообще все расчёты вообще везде должны быть строго в копейках.
Рубли (дроби) должны быть только в JSON-представлениях
## Поддержка методов API (регистрация документов)

View File

@@ -5,16 +5,21 @@
"type": "library",
"keywords": [
"54-fz",
"54-фз",
"kkt",
"ккт",
"e-commerce",
"cash",
"cash register",
"payment",
"payment system",
"atol",
"atol-online"
"атол",
"atol-online",
"атол онлайн"
],
"homepage": "https://github.com/anthonyaxenov/atol-online",
"readme": "https://github.com/anthonyaxenov/atol-online/blob/master/README.md",
"authors": [
{
"name": "Anthony Axenov",
@@ -25,10 +30,16 @@
"support": {
"source": "https://github.com/anthonyaxenov/atol-online",
"issues": "https://github.com/anthonyaxenov/atol-online/issues",
"chat": "https://discord.gg/mFYTQmp"
"docs": "https://github.com/anthonyaxenov/atol-online/blob/master/docs/readme.md"
},
"funding": [
{
"type": "Yoomoney",
"url": "https://yoomoney.ru/to/41001685237530"
}
],
"require": {
"php": ">=7.2",
"php": ">=7.4",
"ext-json": "*",
"guzzlehttp/guzzle": "^6.5",
"psr/log": "^1.1",
@@ -40,12 +51,14 @@
"autoload": {
"classmap": [
"src/AtolOnline/Api/",
"src/AtolOnline/Api/Schemas/",
"src/AtolOnline/Exceptions/",
"src/AtolOnline/Entities/",
"src/AtolOnline/Traits/",
"src/AtolOnline/Constants/",
"tests/"
],
"files": [
"src/helpers.php"
]
}
}

927
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -46,10 +46,10 @@ $customer = (new AtolOnline\Entities\Client())
* `AtolEmailValidateException` (если email невалиден).
Метод `setInn()` чистит входную строку от всех символов, кроме цифр, и проверяет длину (либо 10, либо 12 цифр).
Выбрасывает исключение `AtolInnWrongLengthException` (если длина строка ИНН некорректна).
Выбрасывает исключение `AtolInnWrongLengthException` (если длина ИНН некорректна).
Метод `setName()` проверяет входную строку на длину (до 256 символов).
Выбрасывает исключение `AtolNameTooLongException` (если слишком длинное наименование).
Выбрасывает исключение `AtolNameTooLongException` (если слишком длинное имя).
Метод `setPhone()` чистит входную строку от всех символов, кроме цифр и знака `+`, и проверяет длину (до 64 символов).
Выбрасывает исключение `AtolPhoneTooLongException` (если слишком длинный номер телефона).

View File

@@ -46,7 +46,7 @@ $company = (new AtolOnline\Entities\Company())
* `AtolEmailValidateException` (если email невалиден).
Метод `setInn()` чистит входную строку от всех символов, кроме цифр, и проверяет длину (либо 10, либо 12 цифр).
Выбрасывает исключение `AtolInnWrongLengthException` (если длина строка ИНН некорректна).
Выбрасывает исключение `AtolInnWrongLengthException` (если длина ИНН некорректна).
Метод `setPaymentAddress()` проверяет длину (до 256 символов).
Выбрасывает исключение `AtolPaymentAddressTooLongException` (если слишком длинный адрес места расчётов).

View File

@@ -164,4 +164,4 @@ $json_array = $doc->jsonSerialize();
---
[Вернуться к содержанию](readme.md)
[Вернуться к содержанию](readme.md)

View File

@@ -49,12 +49,18 @@ $kkt->getGroup();
Эти параметры нужно задать [объекту компании](/docs/company.md), который будет передаваться в документах через эту ККТ.
<a name='testmode'></a>
## Тестовый режим
На самом деле, в АТОЛ Онлайн нет понятия *тестовая операция* или чего-то в этом духе.
АТОЛ предоставляет нам отдельную тестовую ККТ.
АТОЛ предоставляет нам отдельную тестовую среду (ККТ).
[Её настройки](https://online.atol.ru/files/ffd/test_sreda.txt) уже указаны в коде библиотеки.
*Под тестовым режимом работы подразумевается использование этой тестовой ККТ.*
*Под тестовым режимом работы подразумевается использование другой (тестовой) ККТ.*
При включенном тестовом режиме:
* меняется логин, пароль и группа (для обращения на тестовую ККТ)
* между авторизацией и операцией над документом, в `Company` документа переопределяется ИНН, СНО и адрес места
расчётов на те, что указаны в [параметрах тестовой среды](https://online.atol.ru/files/ffd/test_sreda.txt).
В библиотеке есть переключатель настроек ККТ.
С его помощью можете поменять вашу боевую ККТ на тестовую и наоборот.
@@ -70,13 +76,43 @@ $kkt->setTestMode(false); // выключить
> Если вы включили тестовый режим (как показано выше), то используются именно эта ККТ, а не ваша.
> После выключения тестового режима настройки доступа к ККТ меняются на ваши (используется уже ваша ККТ).
Если по каким-то причинам у вас не получится использовать тестовый режим, вы можете проводить свои тесты в боевом режиме (на собственной ККТ).
Для включения тестового режима необязательно задавать параметры боевой ККТ.
Если по каким-то причинам у вас не получится использовать тестовый режим, вы можете проводить свои тесты в боевом
режиме (на собственной ККТ).
В этом случае важно понимать следующее:
1. сразу после оформления документа **прихода** необходимо оформлять точно такой же документ **возврата прихода**;
2. [вы обязательно забудете о пункте 1](http://murphy-law.net.ru/basics.html);
3. пп. 1 и 2 в любом случае скажутся на ваших финансовых отчётах;
4. вся ответственность за пп. 1-3 и последствия ложится только на вас.
## Авторизация на ККТ
Перед первым запросом на ККТ происходит аутентификация на сервере по логину и паролю.
В ответ приходит авторизационный токен, срок жизни коего равен **24 часам**.
После первой успешной операции возможно получить этот токен следующим образом:
```php
$kkt->getAuthToken(); // вернёт строку длиной 128 символа
```
Этот токен можно сохранить и переиспользовать в течение всего срока его жизни.
Спустя это время следует получить новый токен.
Для дальнейшего использования однажды полученный токен следует указывать следующим образом:
```php
$kkt->setAuthToken($token_string);
```
Если токен был установлен перед выполнением операции, то при выполнении операции будет использоваться именно он, а новый
запрашиваться не будет. Если операция завершится ошибочно из-за истёкшего токена, следует повторить операцию без
использования метода `setAuthToken()`, либо обнулив его следующим образом:
```php
$kkt->setAuthToken(null);
```
## Регистрация документа
Для регистрации документа **прихода** необходимо вызвать метод `sell()`:
@@ -106,29 +142,64 @@ $result = $kkt->buyRefund($document);
Для операций, перечисленных выше, документы не должны содержать [данных коррекции](/docs/documents.md#correction).
Тогда как для операций коррекции, которые описаны ниже, эти данные должны присутствовать.
Для регистрации документа **коррекции прихода** необходимо вызвать метод `sellRefund()`:
Для регистрации документа **коррекции прихода** необходимо вызвать метод `sellCorrection()`:
```php
$result = $kkt->sellCorrection($document);
```
Для регистрации документа **коррекции расхода** необходимо вызвать метод `buyRefund()`:
Для регистрации документа **коррекции расхода** необходимо вызвать метод `buyCorrection()`:
```php
$result = $kkt->buyCorrection($document);
```
Любой из перечисленных выше шести методов может выбросить исключение `AtolAuthFailedException` при ошибке
аутентификации или авторизации.
### Собственный идентификатор документа
Каждый документ, переданный на ККТ для регистрации, всегда имеет свой идентификатор, абсолютно уникальный среди всех
документов когда-либо регистрировавшихся на ККТ, даже если при регистрации были ошибки.
По умолчанию это UUID версии 4.
Чтобы использовать собственный идентификатор, следует передать нужное строковое значение вторым параметром в любой из
шести описанных выше методов, например:
```php
$kkt->sell($document, $my_unique_id);
$kkt->sellRefund($document, $my_unique_id);
$kkt->buy($document, $my_unique_id);
$kkt->buyRefund($document, $my_unique_id);
$kkt->sellCorrection($document, $my_unique_id);
$kkt->buyCorrection($document, $my_unique_id);
```
Если `$my_unique_id` равен `null` или пустой строке, то будет сгенерирован новый UUID.
Узнать его можно будет только в ответе от ККТ.
### Передача callback_url
Перед регистрацией документа можно указать `callback_url`.
АТОЛ отправит на указанный URL результат регистрации.
Вам необходимо расположить по этому адресу скрипт, обрабатывающий этот результат.
По этому адресу должен располагаться код, который будет обрабатывать этот результат.
```php
$kkt->setCallbackUrl('http://example.com/process-kkt-result');
$kkt->getCallbackUrl();
```
Метод `setCallbackUrl()` проверяет входную строку на длину (до 256 символов) и валидность формата url по
регулярному выражению:
```
^http(s?)\:\/\/[0-9a-zA-Zа-яА-Я]([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-\.\?\,\'\/\\\+&=%$#_]*)?$
```
Выбрасывает исключения:
* `AtolCallbackUrlTooLongException` (если слишком длинный url);
* `AtolInvalidCallbackUrlException` (если url невалиден).
## Обработка результата регистрации
Методы `sell()`, `sellRefund()`, `sellCorrection()`, `buy()`, `buyRefund()` и `buyCorrection()` возвращают объект `AtolOnline\Api\KktResponse`.
@@ -138,7 +209,7 @@ $kkt->getCallbackUrl();
Этот объект содержит в себе HTTP-код ответа, массив заголовков и JSON-декодированные данные тела ответа.
```php
$result = $kkt->getLastResponse();
$result = $kkt->getLastResponse(); // вернёт последний ответ от API
$headers = $result->getHeaders(); // вернёт заголовки
$code = $result->getCode(); // вернёт код ответа
$body = $result->getContent(); // вернёт JSON-декодированное тело ответа
@@ -158,7 +229,7 @@ $err_text = $result->error->text;
Проверка корректности ответа (отсутствия ошибок) работает через метод `isValid()`:
```php
$kkt->isValid(); // вернёт true, если ошибок нет
$kkt->getLastResponse()->isValid(); // вернёт true, если ошибок нет
```
## Проверка статуса документа

View File

@@ -9,13 +9,19 @@
namespace AtolOnline\Api;
use AtolOnline\{Entities\Company,
use AtolOnline\{Constants\Constraints,
Constants\TestEnvParams,
Entities\Company,
Entities\Document,
Exceptions\AtolAuthFailedException,
Exceptions\AtolCallbackUrlTooLongException,
Exceptions\AtolCorrectionInfoException,
Exceptions\AtolInvalidCallbackUrlException,
Exceptions\AtolInvalidUuidException,
Exceptions\AtolKktLoginEmptyException,
Exceptions\AtolKktLoginTooLongException,
Exceptions\AtolKktPasswordEmptyException,
Exceptions\AtolKktPasswordTooLongException,
Exceptions\AtolWrongDocumentTypeException
};
use GuzzleHttp\Client;
@@ -59,6 +65,7 @@ class Kkt extends Client
* @throws \AtolOnline\Exceptions\AtolKktLoginEmptyException Логин ККТ не может быть пустым
* @throws \AtolOnline\Exceptions\AtolKktLoginTooLongException Слишком длинный логин ККТ
* @throws \AtolOnline\Exceptions\AtolKktPasswordEmptyException Пароль ККТ не может быть пустым
* @throws \AtolOnline\Exceptions\AtolKktPasswordTooLongException Слишком длинный пароль ККТ
* @see https://guzzle.readthedocs.io/en/latest/request-options.html
*/
public function __construct(
@@ -116,12 +123,10 @@ class Kkt extends Client
*/
public function setLogin(string $login)
{
if (!$this->isTestMode()) {
if (empty($login)) {
throw new AtolKktLoginEmptyException();
} elseif ((function_exists('mb_strlen') ? mb_strlen($login) : strlen($login)) > 100) {
throw new AtolKktLoginTooLongException($login, 100);
}
if (empty($login)) {
throw new AtolKktLoginEmptyException();
} elseif (valid_strlen($login) > Constraints::MAX_LENGTH_LOGIN) {
throw new AtolKktLoginTooLongException($login, Constraints::MAX_LENGTH_LOGIN);
}
$this->kkt_config['prod']['login'] = $login;
return $this;
@@ -143,13 +148,14 @@ class Kkt extends Client
* @param string $password
* @return $this
* @throws \AtolOnline\Exceptions\AtolKktPasswordEmptyException Пароль ККТ не может быть пустым
* @throws \AtolOnline\Exceptions\AtolKktPasswordTooLongException Слишком длинный пароль ККТ
*/
public function setPassword(string $password)
{
if (!$this->isTestMode()) {
if (empty($password)) {
throw new AtolKktPasswordEmptyException();
}
if (empty($password)) {
throw new AtolKktPasswordEmptyException();
} elseif (valid_strlen($password) > Constraints::MAX_LENGTH_PASSWORD) {
throw new AtolKktPasswordTooLongException($password, Constraints::MAX_LENGTH_PASSWORD);
}
$this->kkt_config['prod']['pass'] = $password;
return $this;
@@ -170,9 +176,16 @@ class Kkt extends Client
*
* @param string $url
* @return $this
* @throws \AtolOnline\Exceptions\AtolCallbackUrlTooLongException Слишком длинный Callback URL
* @throws \AtolOnline\Exceptions\AtolInvalidCallbackUrlException Невалидный Callback URL
*/
public function setCallbackUrl(string $url)
{
if (valid_strlen($url) > Constraints::MAX_LENGTH_CALLBACK_URL) {
throw new AtolCallbackUrlTooLongException($url, Constraints::MAX_LENGTH_CALLBACK_URL);
} elseif (!preg_match(Constraints::PATTERN_CALLBACK_URL, $url)) {
throw new AtolInvalidCallbackUrlException('Callback URL not matches with pattern');
}
$this->kkt_config[$this->isTestMode() ? 'test' : 'prod']['callback_url'] = $url;
return $this;
}
@@ -222,105 +235,135 @@ class Kkt extends Client
/**
* Регистрирует документ прихода
*
* @param \AtolOnline\Entities\Document $document
* @param \AtolOnline\Entities\Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sell(Document $document)
public function sell(Document $document, ?string $external_id = null)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Некорректная операция над документом коррекции');
}
return $this->registerDocument('sell', 'receipt', $document);
return $this->registerDocument('sell', 'receipt', $document, $external_id);
}
/**
* Регистрирует документ возврата прихода
*
* @param \AtolOnline\Entities\Document $document
* @param \AtolOnline\Entities\Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sellRefund(Document $document)
public function sellRefund(Document $document, ?string $external_id = null)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Некорректная операция над документом коррекции');
throw new AtolCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('sell_refund', 'receipt', $document->clearVats());
return $this->registerDocument('sell_refund', 'receipt', $document->clearVats(), $external_id);
}
/**
* Регистрирует документ коррекции прихода
*
* @param \AtolOnline\Entities\Document $document
* @param \AtolOnline\Entities\Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе отсутствуют данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sellCorrection(Document $document)
public function sellCorrection(Document $document, ?string $external_id = null)
{
if (!$document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException();
}
$document->setClient(null)->setItems([]);
return $this->registerDocument('sell_correction', 'correction', $document);
return $this->registerDocument('sell_correction', 'correction', $document, $external_id);
}
/**
* Регистрирует документ расхода
*
* @param \AtolOnline\Entities\Document $document
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function buy(Document $document)
public function buy(Document $document, ?string $external_id = null)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Некорректная операция над документом коррекции');
throw new AtolCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('buy', 'receipt', $document);
return $this->registerDocument('buy', 'receipt', $document, $external_id);
}
/**
* Регистрирует документ возврата расхода
*
* @param \AtolOnline\Entities\Document $document
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolPriceTooHighException Слишком большая сумма
* @throws \AtolOnline\Exceptions\AtolTooManyVatsException Слишком много ставок НДС
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе есть данные коррекции
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function buyRefund(Document $document)
public function buyRefund(Document $document, ?string $external_id = null)
{
if ($document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException('Некорректная операция над документом коррекции');
throw new AtolCorrectionInfoException('Invalid operation on correction document');
}
return $this->registerDocument('buy_refund', 'receipt', $document->clearVats());
return $this->registerDocument('buy_refund', 'receipt', $document->clearVats(), $external_id);
}
/**
* Регистрирует документ коррекции расхода
*
* @param Document $document
* @param \AtolOnline\Entities\Document $document
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolCorrectionInfoException В документе отсутствуют данные коррекции
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длтина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \AtolOnline\Exceptions\AtolTooManyItemsException Слишком много предметов расчёта
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function buyCorrection(Document $document)
public function buyCorrection(Document $document, ?string $external_id = null)
{
if (!$document->getCorrectionInfo()) {
throw new AtolCorrectionInfoException();
}
$document->setClient(null)->setItems([]);
return $this->registerDocument('buy_correction', 'correction', $document);
return $this->registerDocument('buy_correction', 'correction', $document, $external_id);
}
/**
@@ -328,7 +371,9 @@ class Kkt extends Client
*
* @param string $uuid UUID регистрации
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolInvalidUuidException Некорректный UUID документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getDocumentStatus(string $uuid)
{
@@ -348,7 +393,9 @@ class Kkt extends Client
* @param int $retry_count Количество попыток
* @param int $timeout Таймаут в секундах между попытками
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolException Некорректный UUID документа
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolInvalidUuidException Некорректный UUID документа
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function pollDocumentStatus(string $uuid, int $retry_count = 5, int $timeout = 1)
{
@@ -365,6 +412,28 @@ class Kkt extends Client
return $response;
}
/**
* Возвращает текущий токен авторизации
*
* @return string
*/
public function getAuthToken(): ?string
{
return $this->auth_token;
}
/**
* Устанавливает заранее известный токен авторизации
*
* @param string|null $auth_token
* @return $this
*/
public function setAuthToken(?string $auth_token)
{
$this->auth_token = $auth_token;
return $this;
}
/**
* Сбрасывает настройки ККТ по умолчанию
*/
@@ -375,9 +444,9 @@ class Kkt extends Client
$this->kkt_config['prod']['pass'] = '';
$this->kkt_config['prod']['url'] = 'https://online.atol.ru/possystem/v4';
$this->kkt_config['prod']['callback_url'] = '';
$this->kkt_config['test']['group'] = 'v4-online-atol-ru_4179';
$this->kkt_config['test']['login'] = 'v4-online-atol-ru';
$this->kkt_config['test']['pass'] = 'iGFFuihss';
$this->kkt_config['test']['group'] = TestEnvParams::GROUP;
$this->kkt_config['test']['login'] = TestEnvParams::LOGIN;
$this->kkt_config['test']['pass'] = TestEnvParams::PASSWORD;
$this->kkt_config['test']['url'] = 'https://testonline.atol.ru/possystem/v4';
$this->kkt_config['test']['callback_url'] = '';
}
@@ -451,6 +520,7 @@ class Kkt extends Client
* Производит авторизацию на ККТ и получает токен доступа для дальнейших HTTP-запросов
*
* @return bool
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \GuzzleHttp\Exception\GuzzleException
*/
protected function auth()
@@ -461,7 +531,7 @@ class Kkt extends Client
'pass' => $this->getPassword(),
]);
if (!$result->isValid() || !$result->getContent()->token) {
return false;
throw new AtolAuthFailedException($result);
}
$this->auth_token = $result->getContent()->token;
}
@@ -471,14 +541,18 @@ class Kkt extends Client
/**
* Отправляет документ на регистрацию
*
* @param string $api_method Метод API
* @param string $type Тип документа: receipt, correction
* @param \AtolOnline\Entities\Document $document Объект документа
* @param string $api_method Метод API
* @param string $type Тип документа: receipt, correction
* @param \AtolOnline\Entities\Document $document Объект документа
* @param string|null $external_id Уникальный код документа (если не указан, то будет создан UUID)
* @return \AtolOnline\Api\KktResponse
* @throws \AtolOnline\Exceptions\AtolAuthFailedException Ошибка авторизации
* @throws \AtolOnline\Exceptions\AtolWrongDocumentTypeException Некорректный тип документа
* @throws \Exception
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
* @throws \GuzzleHttp\Exception\GuzzleException
*/
protected function registerDocument(string $api_method, string $type, Document $document)
protected function registerDocument(string $api_method, string $type, Document $document, ?string $external_id = null)
{
$type = trim($type);
if (!in_array($type, ['receipt', 'correction'])) {
@@ -486,27 +560,17 @@ class Kkt extends Client
}
$this->auth();
if ($this->isTestMode()) {
$document->setCompany((new Company())
->setInn('5544332219')
->setPaymentAddress('https://v4.online.atol.ru')
);
$document->setCompany(($document->getCompany() ?: new Company())
->setInn(TestEnvParams::INN)
->setSno(TestEnvParams::SNO)
->setPaymentAddress(TestEnvParams::PAYMENT_ADDRESS));
}
$data['timestamp'] = date('d.m.y H:i:s');
$data['external_id'] = $external_id ?: Uuid::uuid4()->toString();
$data[$type] = $document;
if ($this->getCallbackUrl()) {
$data['service'] = ['callback_url' => $this->getCallbackUrl()];
}
$data = [
'timestamp' => date('d.m.y H:i:s'),
'external_id' => Uuid::uuid4()->toString(),
'service' => ['callback_url' => $this->getCallbackUrl()],
$type => $document,
];
return $this->sendAtolRequest('POST', trim($api_method), $data);
}
/**
* Возвращает текущий токен авторизации
*
* @return string
*/
public function getAuthToken(): ?string
{
return $this->auth_token;
}
}

View File

@@ -1,23 +0,0 @@
<?php
namespace AtolOnline\Api;
abstract class AtolSchema
{
/**
* @return mixed
*/
public static function get()
{
return static::$json
?? static::$json = json_decode(file_get_contents(static::$URL));
}
/**
* @return false|string
*/
public static function json()
{
return json_encode(static::get());
}
}

View File

@@ -1,17 +0,0 @@
<?php
namespace AtolOnline\Api;
class CorrectionSchema extends AtolSchema
{
/**
* @var
*/
protected static $json;
/**
* Адрес схемы
*/
protected static $URL = 'https://online.atol.ru/possystem/v4/schema/correction';
}

View File

@@ -1,17 +0,0 @@
<?php
namespace AtolOnline\Api;
class SellSchema extends AtolSchema
{
/**
* @var
*/
protected static $json;
/**
* Адрес схемы
*/
protected static $URL = 'https://online.atol.ru/possystem/v4/schema/sell';
}

View File

@@ -0,0 +1,76 @@
<?php
namespace AtolOnline\Constants;
/**
* Класс с константами ограничений: максимальные длины, правила валидации значений
*
* @package AtolOnline\Constants
*/
class Constraints
{
/**
* Максимальная длина Callback URL
*/
const MAX_LENGTH_CALLBACK_URL = 256;
/**
* Максимальная длина email
*/
const MAX_LENGTH_EMAIL = 64;
/**
* Максимальная длина логина ККТ
*/
const MAX_LENGTH_LOGIN = 100;
/**
* Максимальная длина пароля ККТ
*/
const MAX_LENGTH_PASSWORD = 100;
/**
* Максимальная длина имени покупателя
*/
const MAX_LENGTH_CLIENT_NAME = 256;
/**
* Максимальная длина телефона покупателя
*/
const MAX_LENGTH_CLIENT_PHONE = 64;
/**
* Максимальная длина адреса места расчётов
*/
const MAX_LENGTH_PAYMENT_ADDRESS = 256;
/**
* Максимальная длина имени кассира
*/
const MAX_LENGTH_CASHIER_NAME = 64;
/**
* Максимальная длина наименования предмета расчётов
*/
const MAX_LENGTH_ITEM_NAME = 128;
/**
* Максимальная длина единицы измерения предмета расчётов
*/
const MAX_LENGTH_MEASUREMENT_UNIT = 16;
/**
* Максимальная длина пользовательских данных для предмета расчётов
*/
const MAX_LENGTH_USER_DATA = 64;
/**
* Регулярное выражание для валидации строки ИНН
*/
const PATTERN_INN = "/(^[0-9]{10}$)|(^[0-9]{12}$)/";
/**
* Регулярное выражание для валидации строки Callback URL
*/
const PATTERN_CALLBACK_URL = "/^http(s?)\:\/\/[0-9a-zA-Zа-яА-Я]([-.\w]*[0-9a-zA-Zа-яА-Я])*(:(0-9)*)*(\/?)([a-zAZ0-9а-яА-Я\-\.\?\,\'\/\\\+&=%\$#_]*)?$/";
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Constants;
/**
* Константы, определяющие параметры тестовой среды
*
* @see https://online.atol.ru/files/ffd/test_sreda.txt
* @package AtolOnline\Constants
*/
class TestEnvParams
{
/**
* Логин
*/
const LOGIN = 'v4-online-atol-ru';
/**
* Пароль
*/
const PASSWORD = 'iGFFuihss';
/**
* Группа
*/
const GROUP = 'v4-online-atol-ru_4179';
/**
* Система налогообложения
*/
const SNO = SnoTypes::OSN;
/**
* ИНН
*/
const INN = '5544332219';
/**
* Адрес места расчётов
*/
const PAYMENT_ADDRESS = 'https://v4.online.atol.ru';
}

View File

@@ -9,7 +9,7 @@
namespace AtolOnline\Entities;
use AtolOnline\{Exceptions\AtolNameTooLongException, Exceptions\AtolPhoneTooLongException, Traits\HasEmail, Traits\HasInn};
use AtolOnline\{Constants\Constraints, Exceptions\AtolNameTooLongException, Exceptions\AtolPhoneTooLongException, Traits\HasEmail, Traits\HasInn};
/**
* Класс Client, описывающий сущность покупателя
@@ -46,11 +46,11 @@ class Client extends Entity
* @param string|null $phone Телефон
* @param string|null $email Email
* @param string|null $inn ИНН
* @throws \AtolOnline\Exceptions\AtolEmailTooLongException
* @throws \AtolOnline\Exceptions\AtolEmailValidateException
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException
* @throws \AtolOnline\Exceptions\AtolNameTooLongException
* @throws \AtolOnline\Exceptions\AtolPhoneTooLongException
* @throws \AtolOnline\Exceptions\AtolEmailTooLongException Слишком длинный email
* @throws \AtolOnline\Exceptions\AtolEmailValidateException Невалидный email
* @throws \AtolOnline\Exceptions\AtolInnWrongLengthException Некорректная длина ИНН
* @throws \AtolOnline\Exceptions\AtolNameTooLongException Слишком длинное имя
* @throws \AtolOnline\Exceptions\AtolPhoneTooLongException СЛишком длинный номер телефона
*/
public function __construct(?string $name = null, ?string $phone = null, ?string $email = null, ?string $inn = null)
{
@@ -89,8 +89,8 @@ class Client extends Entity
public function setName(string $name)
{
$name = trim($name);
if ((function_exists('mb_strlen') ? mb_strlen($name) : strlen($name)) > 256) {
throw new AtolNameTooLongException($name, 256);
if (valid_strlen($name) > Constraints::MAX_LENGTH_CLIENT_NAME) {
throw new AtolNameTooLongException($name, Constraints::MAX_LENGTH_CLIENT_NAME);
}
$this->name = $name;
return $this;
@@ -119,8 +119,8 @@ class Client extends Entity
public function setPhone(string $phone)
{
$phone = preg_replace("/[^0-9+]/", '', $phone);
if ((function_exists('mb_strlen') ? mb_strlen($phone) : strlen($phone)) > 64) {
throw new AtolPhoneTooLongException($phone, 64);
if (valid_strlen($phone) > Constraints::MAX_LENGTH_CLIENT_PHONE) {
throw new AtolPhoneTooLongException($phone, Constraints::MAX_LENGTH_CLIENT_PHONE);
}
$this->phone = $phone;
return $this;

View File

@@ -9,7 +9,8 @@
namespace AtolOnline\Entities;
use AtolOnline\{Exceptions\AtolEmailTooLongException,
use AtolOnline\{Constants\Constraints,
Exceptions\AtolEmailTooLongException,
Exceptions\AtolEmailValidateException,
Exceptions\AtolInnWrongLengthException,
Exceptions\AtolPaymentAddressTooLongException,
@@ -52,10 +53,10 @@ class Company extends Entity
* @param string|null $inn
* @param string|null $paymentAddress
* @param string|null $email
* @throws AtolEmailTooLongException
* @throws AtolEmailValidateException
* @throws AtolInnWrongLengthException
* @throws AtolPaymentAddressTooLongException
* @throws AtolEmailTooLongException Слишком длинный email
* @throws AtolEmailValidateException Невалидный email
* @throws AtolInnWrongLengthException Некорректная длина ИНН
* @throws AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
*/
public function __construct(string $sno = null, string $inn = null, string $paymentAddress = null, string $email = null)
{
@@ -110,13 +111,13 @@ class Company extends Entity
*
* @param string $payment_address
* @return $this
* @throws AtolPaymentAddressTooLongException
* @throws AtolPaymentAddressTooLongException Слишком длинный адрес места расчётов
*/
public function setPaymentAddress(string $payment_address)
{
$payment_address = trim($payment_address);
if ((function_exists('mb_strlen') ? mb_strlen($payment_address) : strlen($payment_address)) > 256) {
throw new AtolPaymentAddressTooLongException($payment_address, 256);
if (valid_strlen($payment_address) > Constraints::MAX_LENGTH_PAYMENT_ADDRESS) {
throw new AtolPaymentAddressTooLongException($payment_address, Constraints::MAX_LENGTH_PAYMENT_ADDRESS);
}
$this->payment_address = $payment_address;
return $this;

View File

@@ -68,7 +68,7 @@ class CorrectionInfo extends Entity
*/
public function getNumber(): ?string
{
return $this->base_name;
return $this->base_number;
}
/**
@@ -165,7 +165,7 @@ class CorrectionInfo extends Entity
'type' => $this->getType() ?? '', // обязателен
'base_date' => $this->getDate() ?? '', // обязателен
'base_number' => $this->getNumber() ?? '', // обязателен
'base_name' => $this->getName() ?? '' // обязателен
'base_name' => $this->getName() ?? '' // не обязателен
];
}
}
}

View File

@@ -9,6 +9,7 @@
namespace AtolOnline\Entities;
use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\AtolCashierTooLongException;
use AtolOnline\Exceptions\AtolException;
use AtolOnline\Exceptions\AtolInvalidJsonException;
@@ -259,9 +260,11 @@ class Document extends Entity
*/
public function setCashier(?string $cashier)
{
$cashier = trim($cashier);
if ((function_exists('mb_strlen') ? mb_strlen($cashier) : strlen($cashier)) > 64) {
throw new AtolCashierTooLongException($cashier, 64);
if ($cashier !== null) {
$cashier = trim($cashier);
if (valid_strlen($cashier) > Constraints::MAX_LENGTH_CASHIER_NAME) {
throw new AtolCashierTooLongException($cashier, Constraints::MAX_LENGTH_CASHIER_NAME);
}
}
$this->cashier = $cashier;
return $this;
@@ -359,6 +362,14 @@ class Document extends Entity
$array['client']['inn'] ?? null
));
}
if (isset($array['correction_info'])) {
$doc->setCorrectionInfo(new CorrectionInfo(
$array['correction_info']['type'] ?? null,
$array['correction_info']['base_date'] ?? null,
$array['correction_info']['base_number'] ?? null,
$array['correction_info']['base_name'] ?? null,
));
}
if (isset($array['items'])) {
foreach ($array['items'] as $ar_item) {
$item = new Item(
@@ -388,6 +399,18 @@ class Document extends Entity
$doc->payments->add($payment);
}
}
if (isset($array['vats'])) {
foreach ($array['vats'] as $vat_payment) {
$vat = new Vat();
if (isset($vat_payment['type'])) {
$vat->setType($vat_payment['type']);
}
if (isset($vat_payment['sum'])) {
$vat->setSum($vat_payment['sum']);
}
$doc->vats->add($vat);
}
}
if (isset($array['total']) && $array['total'] != $doc->calcTotal()) {
throw new AtolException('Real total sum not equals to provided in JSON one');
}

View File

@@ -9,12 +9,14 @@
namespace AtolOnline\Entities;
use AtolOnline\{Exceptions\AtolNameTooLongException,
use AtolOnline\{Constants\Constraints,
Exceptions\AtolNameTooLongException,
Exceptions\AtolPriceTooHighException,
Exceptions\AtolTooManyException,
Exceptions\AtolUnitTooLongException,
Exceptions\AtolUserdataTooLongException,
Traits\RublesKopeksConverter};
Traits\RublesKopeksConverter
};
/**
* Предмет расчёта (товар, услуга)
@@ -137,8 +139,8 @@ class Item extends Entity
public function setName(string $name)
{
$name = trim($name);
if ((function_exists('mb_strlen') ? mb_strlen($name) : strlen($name)) > 128) {
throw new AtolNameTooLongException($name, 128);
if (valid_strlen($name) > Constraints::MAX_LENGTH_ITEM_NAME) {
throw new AtolNameTooLongException($name, Constraints::MAX_LENGTH_ITEM_NAME);
}
$this->name = $name;
return $this;
@@ -225,8 +227,8 @@ class Item extends Entity
public function setMeasurementUnit(string $measurement_unit)
{
$measurement_unit = trim($measurement_unit);
if ((function_exists('mb_strlen') ? mb_strlen($measurement_unit) : strlen($measurement_unit)) > 16) {
throw new AtolUnitTooLongException($measurement_unit, 16);
if (valid_strlen($measurement_unit) > Constraints::MAX_LENGTH_MEASUREMENT_UNIT) {
throw new AtolUnitTooLongException($measurement_unit, Constraints::MAX_LENGTH_MEASUREMENT_UNIT);
}
$this->measurement_unit = $measurement_unit;
return $this;
@@ -328,8 +330,8 @@ class Item extends Entity
public function setUserData(string $user_data)
{
$user_data = trim($user_data);
if ((function_exists('mb_strlen') ? mb_strlen($user_data) : strlen($user_data)) > 64) {
throw new AtolUserdataTooLongException($user_data, 64);
if (valid_strlen($user_data) > Constraints::MAX_LENGTH_USER_DATA) {
throw new AtolUserdataTooLongException($user_data, Constraints::MAX_LENGTH_USER_DATA);
}
$this->user_data = $user_data;
return $this;

View File

@@ -106,7 +106,7 @@ class ItemArray extends Entity
protected function validateCount(?array $items = null): bool
{
if ((!empty($items) && count($items) >= self::MAX_COUNT) || count($this->items) >= self::MAX_COUNT) {
throw new AtolTooManyItemsException(self::MAX_COUNT);
throw new AtolTooManyItemsException(count($items), self::MAX_COUNT);
}
return true;
}

View File

@@ -9,7 +9,6 @@
namespace AtolOnline\Entities;
use AtolOnline\Api\SellSchema;
use AtolOnline\Exceptions\AtolTooManyPaymentsException;
/**
@@ -19,6 +18,11 @@ use AtolOnline\Exceptions\AtolTooManyPaymentsException;
*/
class PaymentArray extends Entity
{
/**
* Максимальное количество элементов массива
*/
public const MAX_COUNT = 10;
/**
* @var Payment[] Массив оплат
*/
@@ -99,9 +103,8 @@ class PaymentArray extends Entity
*/
protected function validateCount(?array $payments = null): bool
{
$max_items = SellSchema::get()->properties->receipt->properties->payments->maxItems;
if ((!empty($payments) && count($payments) >= $max_items) || count($this->payments) >= $max_items) {
throw new AtolTooManyPaymentsException($max_items);
if ((!empty($payments) && count($payments) >= self::MAX_COUNT) || count($this->payments) >= self::MAX_COUNT) {
throw new AtolTooManyPaymentsException(count($payments), self::MAX_COUNT);
}
return true;
}

View File

@@ -50,7 +50,8 @@ class Vat extends Entity
}
/**
* Устанавливает размер НДС от суммы в копейках
* Устанавливает:
* размер НДС от суммы в копейках
*
* @param string $type Тип ставки НДС
* @param int $kopeks Копейки

View File

@@ -9,7 +9,6 @@
namespace AtolOnline\Entities;
use AtolOnline\Api\SellSchema;
use AtolOnline\Exceptions\AtolTooManyVatsException;
/**
@@ -19,6 +18,11 @@ use AtolOnline\Exceptions\AtolTooManyVatsException;
*/
class VatArray extends Entity
{
/**
* Максимальное количество элементов массива
*/
public const MAX_COUNT = 6;
/**
* @var Vat[] Массив ставок НДС
*/
@@ -103,9 +107,8 @@ class VatArray extends Entity
*/
protected function validateCount(?array $vats = null): bool
{
$max_items = SellSchema::get()->properties->receipt->properties->vats->maxItems;
if ((!empty($vats) && count($vats) >= $max_items) || count($this->vats) >= $max_items) {
throw new AtolTooManyVatsException(count($vats), $max_items);
if ((!empty($vats) && count($vats) >= self::MAX_COUNT) || count($this->vats) >= self::MAX_COUNT) {
throw new AtolTooManyVatsException(count($vats), self::MAX_COUNT);
}
return true;
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
use AtolOnline\Api\KktResponse;
use Exception;
use Throwable;
/**
* Исключение, возникающее при работе с АТОЛ Онлайн
*
* @package AtolOnline\Exceptions
*/
class AtolAuthFailedException extends Exception
{
/**
* AtolAuthFailedException constructor.
*
* @param \AtolOnline\Api\KktResponse $last_response
* @param string $message
* @param int $code
* @param \Throwable|null $previous
*/
public function __construct(KktResponse $last_response, $message = "", $code = 0, Throwable $previous = null)
{
$message = $last_response->isValid()
? $message
: '['.$last_response->error->code.'] '.$last_response->error->text.
'. ERROR_ID: '.$last_response->error->error_id.
'. TYPE: '.$last_response->error->type;
$code = $last_response->isValid() ? $code : $last_response->error->code;
parent::__construct($message, $code, $previous);
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать слишком длинный callback_url
*
* @package AtolOnline\Exceptions
*/
class AtolCallbackUrlTooLongException extends AtolTooLongException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Callback URL is too long';
}

View File

@@ -27,5 +27,5 @@ class AtolEmailTooLongException extends AtolTooLongException
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Email is is too long';
protected $message = 'Email is too long';
}

View File

@@ -33,6 +33,7 @@ class AtolException extends Exception
*/
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
$message = $message ?: $this->message;
if ($this->getFfdTags()) {
$message .= ' [FFD tags: '.implode(', ', $this->getFfdTags()).']';
}

View File

@@ -39,6 +39,6 @@ class AtolInnWrongLengthException extends AtolException
public function __construct($inn, $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: 'INN length must be 10 or 12 digits only, but actual is '.
(function_exists('mb_strlen') ? mb_strlen($inn) : strlen($inn)).')', $code, $previous);
valid_strlen($inn), $code, $previous);
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать невалидный callback_url
*
* @package AtolOnline\Exceptions
*/
class AtolInvalidCallbackUrlException extends AtolException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'Invalid callback URL';
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Copyright (c) Антон Аксенов (aka Anthony Axenov)
*
* This code is licensed under MIT.
* Этот код распространяется по лицензии MIT.
* https://github.com/anthonyaxenov/atol-online/blob/master/LICENSE
*/
namespace AtolOnline\Exceptions;
/**
* Исключение, возникающее при попытке указать слишком длинный пароль ККТ
*
* @package AtolOnline\Exceptions
*/
class AtolKktPasswordTooLongException extends AtolTooLongException
{
/**
* @var string Сообщение об ошибке
*/
protected $message = 'KKT password is too long';
}

View File

@@ -35,6 +35,6 @@ class AtolTooLongException extends AtolException
public function __construct($string, $max, $message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message ?: $this->message.' (max length - '.$max.', actual length - '.
(function_exists('mb_strlen') ? mb_strlen($string) : strlen($string)).')', $code, $previous);
valid_strlen($string), $code, $previous);
}
}

View File

@@ -9,7 +9,7 @@
namespace AtolOnline\Traits;
use AtolOnline\{Exceptions\AtolEmailTooLongException, Exceptions\AtolEmailValidateException};
use AtolOnline\{Constants\Constraints, Exceptions\AtolEmailTooLongException, Exceptions\AtolEmailValidateException};
/**
* Добавляет объекту функционал для работы с email
@@ -38,16 +38,15 @@ trait HasEmail
*
* @param string $email
* @return $this
* @throws AtolEmailTooLongException
* @throws AtolEmailValidateException
* @throws \AtolOnline\Exceptions\AtolEmailTooLongException Слишком длинный email
* @throws \AtolOnline\Exceptions\AtolEmailValidateException Невалидный email
*/
public function setEmail(string $email)
{
$email = trim($email);
if ((function_exists('mb_strlen') ? mb_strlen($email) : strlen($email)) > 64) {
throw new AtolEmailTooLongException($email, 64);
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
if (valid_strlen($email) > Constraints::MAX_LENGTH_EMAIL) {
throw new AtolEmailTooLongException($email, Constraints::MAX_LENGTH_EMAIL);
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new AtolEmailValidateException($email);
}
$this->email = $email;

View File

@@ -9,6 +9,7 @@
namespace AtolOnline\Traits;
use AtolOnline\Constants\Constraints;
use AtolOnline\Exceptions\AtolInnWrongLengthException;
/**
@@ -39,12 +40,12 @@ trait HasInn
*
* @param string $inn
* @return $this
* @throws AtolInnWrongLengthException
* @throws AtolInnWrongLengthException Некорректная длина ИНН
*/
public function setInn(string $inn)
{
$inn = preg_replace("/[^0-9]/", '', $inn);
if (preg_match_all("/(^[0-9]{10}$)|(^[0-9]{12}$)/", $inn) == 0) {
if (preg_match_all(Constraints::PATTERN_INN, $inn) == 0) {
throw new AtolInnWrongLengthException($inn);
}
$this->inn = $inn;

16
src/helpers.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
if (!function_exists('valid_strlen')) {
/**
* Возвращает корректную длину строки
*
* @param string $value
* @return int
*/
function valid_strlen(string $value): int
{
return function_exists('mb_strlen')
? mb_strlen($value)
: strlen($value);
}
}

View File

@@ -1,30 +0,0 @@
<?php
namespace Unit;
use AtolOnline\Api\CorrectionSchema;
use AtolOnline\Api\SellSchema;
use PHPUnit\Framework\TestCase;
class SchemaTest extends TestCase
{
/**
* Тестирует корректность работы объекта схемы документа
* прихода, возврата прихода, расхода, возврата расхода
*/
public function testSellSchema()
{
$this->assertIsObject(SellSchema::get());
$this->assertJson(SellSchema::json());
}
/**
* Тестирует корректность работы объекта схемы документа
* коррекции прихода, коррекции расхода
*/
public function testCorrectionSchema()
{
$this->assertIsObject(CorrectionSchema::get());
$this->assertJson(CorrectionSchema::json());
}
}