Привет жителям Хабра.
В данной статье хотелось бы рассказать про API для получения чеков, которое нам не предоставила всеми любимая ФНС.
Когда только появились QR-коды на чеках я подумал «Вау, как круто! Ты сканируешь код и видишь если не всю инфу по чеку, то ссылку на него». И какого же было мое разочарование, когда просканировав такой код я увидел что-то вроде
t=20180518T220500&s=975.88&fn=8710000101125654&i=99456&fp=1250448795&n=1
Но расстраиваться я не стал и подумал, что ФНС позаботилась о нас и предоставила API для получения такой информации. Погуглив некоторое время я понял, что ФНС нам предоставила только мобильное приложение для проверки чека и просмотра той информации, что поступила к ним от магазина.
Но! Между магазином и налоговой имеется ещё одно звено — ОФД — те, кто обрабатывают информацию по чекам, полученную от магазинов, и отправляют в налоговую. Вот они то и предоставляют API для получения нужной нам информации. Не все. И не всегда бесплатно.
Судя по информации из википедии по состоянию на 1 марта 2018 зарегистрировано 17 ОФД. Допустим 10 из них предоставляют открытое и бесплатное API. Учитывая то, что мы не знаем с каким ОФД работает конкретный магазин, нужно будет пройтись по API 10 операторов фискальных данных. Далеко не лучший вариант.
Спустя какое-то время, я случайно наткнулся на приложение (не от ФНС), которое по QR-коду с чека получает информацию по чеку. Не будут же они «пробегать» по всем ОФД и собирать оттуда информацию — подумал я. Снова отправился в гугл и наткнулся на такой ответ.
Казалось, после этого ответа можно заканчивать импровизированное расследование, но у меня оставались ещё вопросы:
- Что будет, если использовать другие заголовки?
- Что делать, если пользователь не зарегистрирован? Скачивать мобильное приложение и регистрироваться? (Сайт ФНС не предоставляет возможности зарегистрироваться в этом контексте)
- А если забыл пароль?
Запустив Android Device Monitor и SoapUI я начал разбираться. Выкладываю здесь всю обобщенную информацию, что удалось получить. ФНС предоставляет следующее публичное API:
https://proverkacheka.nalog.ru:9999/v1/mobile/users/signup
Content-Type: application/json; charset=UTF-8
Содержимое:
{"email":"some@mail.com","name":"SomeName","phone":"+79991234567"}
Все параметры обязательные.
Если результат успешен, то пользователь создается, СМС с паролем отправляется на указанный номер, а в ответ возвращается 204 No content.
Если пользователь уже существует, то возвращается 409 Conflict и сообщение «user exists».
Если номер телефона некорректный, то возвращается 500 Internal Server Error и сообщение «failed with code 20101».
Если адрес электронной почты некорректный, то возвращается 400 Bad Request и сообщение "[«Object didn't pass validation for format email: <адрес электронной почты, который вы указали>»]".
Если адрес электронной почты уже используется, а телефон нет, то ошибок не возникает и регистрация проходит успешно.
https://proverkacheka.nalog.ru:9999/v1/mobile/users/login
В заголовке передается Pre-emptive Basic Authorization, где в качестве username передается номер телефона, в виде "+79991234567", а в качестве пароля — код, полученный в смс при регистрации или восстановлении пароля.
Если все хорошо, то вернется 200 OK и сообщение в виде json
{
"email": "<адрес электронной почты, указанный при регистрации>",
"name": "<имя, указанное при регистрации>"
}
Если указать некорректный номер телефона или пароль, то вернется 403 Forbidden и сообщение «the user was not found or the specified password was not correct».
Если не указать номер телефона и/или пароль, то не вернется ничего.
https://proverkacheka.nalog.ru:9999/v1/mobile/users/restore
Content-Type: application/json; charset=UTF-8
Содержимое:
{"phone":"+79991234567"}
Если номер телефона найден, то возвращается 204 No Content и на телефон приходит СМС с новым паролем.
Если номер телефона не найден или номер некорректный, то возвращается 404 Not Found и сообщение «the user was not found».
https://proverkacheka.nalog.ru:9999/v1/ofds/*/inns/*/fss/<номер ФН>/operations/1/tickets/<номер ФД>?fiscalSign=<номер ФПД>&date=2018-05-17T17:57:00&sum=3900
Где
- Номер ФН (Фискальный Номер) — 16-значный номер. Например 8710000100518392
- Номер ФД (Фискальный документ) — до 10 знаков. Например 54812
- Номер ФПД (Фискальный Признак Документа, также известный как ФП) — до 10 знаков. Например 3522207165
- (Мои догадки) В качестве единицы используется параметр с QR-кода в чеке, помеченный в начале статьи, как n=1
- Дата — дата с чека. Формат может отличаться. Я пробовал переворачивать дату (т.е. 17-05-2018), ставить вместо Т пробел, удалять секунды
- Сумма — сумма с чека в копейках
Если чек найден, то вернется 204 No Content.
Если чек не найден, то вернется 406 Not Acceptable.
Если дата/сумма некорректная или не совпадает с датой/суммой, указанной в чеке, то возвращается 406 Not Acceptable. При этом секунды не учитываются.
Если не указать параметр дата/сумма, то возвращается 400 Bad Request и сообщение "[«Missing required property: <property_name>»]".
https://proverkacheka.nalog.ru:9999/v1/inns/*/kkts/*/fss/<Номер ФН>/tickets/<Номер ФД>?fiscalSign=<Номер ФПД>&sendToEmail=no
Где
- Номер ФН (Фискальный Номер) — 16-значный номер. Например 8710000100518392
- Номер ФД (Фискальный документ) — до 10 знаков. Например 54812
- Номер ФПД (Фискальный Признак Документа, также известный как ФП) — до 10 знаков. Например 3522207165
- (Мои догадки) В качестве единицы используется параметр с QR-кода в чеке, помеченный в начале статьи, как n=1
Также обязательно указать хотя бы пустые заголовки device-id и device-os
Если указаны некорректные данные пользователя, то возвращается 403 Forbidden и сообщение «the user was not found or the specified password was not correct».
Если не указать номер телефона и/или пароль, то ничего не вернется.
Если перед вызовом данного метода не происходила проверка существования чека, то вернется 202 Accepted (без сообщений и любого содержимого). При повторном вызове информация по чеку вернется.
Если в параметре «sendToEmail» попытаться подставить значение «yes», то вернется 500 Internal Server Error и сообщение «connect ECONNREFUSED 127.0.0.1:465». При попытке подставить другие значения («true», 1 и т.д.) вернется 400 Bad Request и сообщение "[«No enum match for: <значение, которое пытались передать>»]".
Если всё хорошо, то вернется 200 ОК и содержимое в формате json примерно такого вида:
{"document": {"receipt": {
"operationType": 1,
"fiscalSign": 3522207165,
"dateTime": "2018-05-17T17:57:00",
"rawData": "AwAzAREEEAA4NzEwMDAwMTAwNTE4MzEzDQQUADAwMDExOTM1MTQwNDE0MDUgICAg+gMMADc4MjU3MDYwODYgIBAEBAAJ2gAA9AMEAGzC/Vo1BAYAMQTSDyLSDgQEABYBAAASBAQAogAAAB4EAQAB/AMCADwPPAQPAD0EAwCKrqQ+BAQARzYzNyMERQAGBCcAKjM0OTIyNzcgTkVTVC6MruAuTUFYSUIukZKQgJeAkoWLLjE0MKyrNwQCAJ8P/wMEAAZAQg8TBAIAnw9PBAIAbAH9Aw4AhK6ro+PopaKgIICtraAHBAIAPA85BAEAAE8EAgBsARgEDACAo+Cu4q7goyCOjo7xAyoANjIwMDE3LCCjLiCFqqDipeCoraHj4KMsIOOrLiCAp6itoCwgpC4gMTimHwQBAAE=",
"totalSum": 3900,
"nds10": 364,
"userInn": "7825706086",
"taxationType": 1,
"operator": "<Данные кассира>",
"fiscalDocumentNumber": 54812,
"properties": [ {
"value": "G637",
"key": "Код"
}],
"receiptCode": 3,
"requestNumber": 162,
"user": "Агроторг ООО",
"kktRegId": "0001193514041405",
"fiscalDriveNumber": "8710000100518392",
"items": [ {
"sum": 3999,
"price": 3999,
"name": "*3492277 NEST.Мор.MAXIB.СТРАЧАТЕЛ.140мл",
"quantity": 1,
"nds10": 364
}],
"ecashTotalSum": 0,
"retailPlaceAddress": "620017, г. Екатеринбург, ул. Азина, д. 18ж",
"cashTotalSum": 3900,
"shiftNumber": 278
}}}
Где
- все суммы указаны в копейках
- данные кассира в разных магазинах имеют разные форматы (в одном случае может вернуться «Фамилия Имя», в другом «Фамилия И. должность»
- порядок элементов может меняться
- разные магазины используют разные наборы параметров и, если какой-то параметр возвращается в чеке от одного магазина, то не факт, ачможет не бытьот параметр будет в чеке от другого магазина
- формат адреса магазина может различаться
{"document": {"receipt": {
"cashTotalSum": 0,
"fiscalSign": 1301551154,
"nds18": 4859,
"operationType": 1,
"userInn": "7728029110",
"dateTime": "2018-05-18T22:05:00",
"fiscalDocumentNumber": 12654,
"receiptCode": 3,
"ecashTotalSum": 97588,
"nds10": 5976,
"requestNumber": 395,
"retailPlaceAddress": "г.Екатеринбург, ул.Сулимова, д.50",
"fiscalDriveNumber": "871000010459859",
"taxationType": 1,
"user": "АО ТД Перекресток",
"operator": "<Данные кассира>",
"items": [
{
"sum": 3799,
"quantity": 1,
"price": 3799,
"name": "18074 Укроп пакет 100г",
"nds10": 345
},
{
"sum": 7490,
"quantity": 0.872,
"nds18": 1143,
"name": "2000339 Яблоки СЕЗОН.ПРЕДЛОЖЕНИЕ 1кг",
"price": 8590
}
],
"totalSum": 97588,
"rawData": "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",
"shiftNumber": 262,
"kktRegId": "0001248888049341"
}}}
Особого смысла в логине я не вижу, но он используется в их мобильном приложении. Возможно, в дальнейшем он для чего-нибудь пригодится.
Кому интересен пример реализации подключения к этому API, вот ссылка на гитхаб проект библиотеки, написанной на C#.
По всем вопросам или замечаниям прошу в комментарии.
Автор: Дмитрий Бобровский