Быстрый старт
Регистрация продажи одним POST-запросом.
curl https://vcr.am/api/v1/sales \
-H "X-API-Key: $VCR_AM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"cashier": { "id": 1 },
"items": [{
"offer": { "externalId": "SKU-001" },
"department": { "id": 1 },
"quantity": "1",
"price": "20000",
"unit": "pc"
}],
"amount": { "nonCash": "20000" },
"buyer": {
"type": "individual",
"receipt": { "email": "buyer@example.com", "language": "en" }
}
}'Начните без налоговых реквизитов
Тестовая касса получает регистрационный номер сразу при создании — без обмена сертификатами с налоговой и без ожидания. Тот же базовый URL, те же эндпоинты, та же работа с ключом; отличаются только чеки: они помечаются TEST и никуда не подаются.
- 1В личном кабинете откройте «Кассы», нажмите «Создать кассу» и выберите «Тестовая касса». Создавайте столько, сколько нужно.
- 2Выпустите API-ключ на этой кассе. Ключ привязан к одной кассе, поэтому ключ от боевой так и будет отвечать 403.
- 3Вызывайте те же эндпоинты по тому же базовому URL. Когда будете готовы к реальной фискализации, создайте боевую кассу и замените ключ.
Создано для боевых интеграций
Что должна предоставить ваша система
Перед интеграцией убедитесь, что каждая продажа несёт данные, которые требует налоговая.
Аутентификация
Каждый запрос должен содержать ваш API-ключ в заголовке X-API-Key. Единственное исключение — GET /exchange-rate: он публичный и ключа не требует.
X-API-Key: your_api_keyСоздавайте и обновляйте API-ключи в настройках вашей кассы.
Храните API-ключ в секрете
Идемпотентность
Фискальный чек нельзя исправить — только вернуть и выпустить заново. Поэтому дубль здесь обходится дороже всего, а повторный вызов вебхука от платёжной системы — это норма, а не исключение. Передавайте заголовок Idempotency-Key в каждом POST: повтор того же запроса вернёт исходный чек, а не создаст второй.
Idempotency-Key: 3f6b2c1e-9a84-4d77-9f2a-1c5e0b7d8a63Без заголовка защиты нет
UUID, сгенерированный один раз на одну фискальную операцию и сохранённый рядом с заказом до первого вызова, а затем повторяемый при каждой попытке. Не номер заказа сам по себе — один заказ может законно породить несколько фискальных документов. И не новый UUID на каждую попытку — это лишает смысла весь механизм.
Повторный ответ побайтово совпадает с исходным и содержит заголовок Idempotent-Replay: true. Проверяйте его, прежде чем считать ответ новым проведённым документом.
У обработчика без состояния ещё ничего не сохранено, поэтому ключ нужно вычислять, а не генерировать. Тогда обе доставки получат один и тот же ключ, и вторая вернёт повтор. Не вычисляйте ключ из идентификатора события платёжной системы: другое событие по тому же заказу даст другой ключ и приведёт к повторной фискализации.
import { v5 as uuidv5 } from "uuid";
const NAMESPACE = "6f1d1a58-0d0c-4a1e-9a1a-2f9a7b3c4d5e";
const key = uuidv5(`${order.id}:sale`, NAMESPACE);
await fetch("https://vcr.am/api/v1/sales", {
method: "POST",
headers: {
"X-API-Key": process.env.VCR_API_KEY,
"Idempotency-Key": key,
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});Ключ остаётся повторяемым 30 дней — достаточно, чтобы месячная сверка при перезапуске вернула повтор, а не создала новый документ. Тот же ключ с другим телом запроса даёт 422; тот же ключ, пока первый запрос ещё выполняется, даёт 409 — это значит подождать и повторить с тем же ключом, а не выпускать новый. Тело, отклонённое схемой, ключ не расходует: запрос не дошёл до операции, поэтому исправьте поле и повторите с тем же ключом.
Ошибки
Ошибки возвращаются стандартными HTTP-кодами и JSON-телом с подробностями.
| Статус | Название | Когда возникает |
|---|---|---|
400 | Неверный запрос | Неверный формат тела или ошибка валидации схемы. |
401 | Не авторизован | API-ключ отсутствует или недействителен. |
403 | Доступ запрещён | Касса ещё не может подавать документы: нет регистрационного номера или она не активирована в налоговой. |
404 | Не найдено | Запрашиваемый ресурс не существует. |
409 | Конфликт | Налоговая отклонила документ по существу, встроенная позиция конфликтует с уже существующей, или запрос с тем же Idempotency-Key ещё выполняется. При отказе приходит `pending`: документ сохранён, поэтому устраняйте причину, а не отправляйте повторно. |
422 | Ключ использован повторно | Тот же Idempotency-Key пришёл с другим телом запроса. Новому документу нужен новый ключ. |
500 | Внутренняя ошибка | Непредвиденный сбой на нашей стороне. Повторите с экспоненциальной задержкой. |
502 | Налоговая недоступна | Документ сохранён, в ответе приходит `pending`. Сначала прочитайте `pending.mayResubmit`: false означает, что повтор создаст второй чек — вместо этого опрашивайте `pending.statusUrl`. |
Что почитать дальше
Продолжить разработку
Скоро в каталоге плагинов WordPress.org