Java-библиотека для работы с API сервиса «Мой налог» (ФНС России).
Позволяет программно регистрировать доходы, получать чеки и выставлять счета для самозанятых.
- Аутентификация по логину и паролю с автоматическим retry
- Автоматическое обновление токена (без повторного ввода пароля)
- Отправка чека: синхронно и асинхронно
- Выставление счетов с реквизитами банковского счёта
- Поддержка прокси (HTTP/HTTPS, с аутентификацией и без)
- Настраиваемый таймаут запросов
- Потокобезопасная работа (ReadWriteLock на обновление токена)
- Spring Boot Starter — достаточно двух строк в
application.properties - Работает и без Spring — как обычная Java-библиотека
Установите библиотеку в локальный Maven-репозиторий:
mvn installДобавьте зависимость в проект-потребитель:
<dependency>
<groupId>io.github.skiddgoddamn</groupId>
<artifactId>moynalog-client</artifactId>
<version>1.1.3</version>
</dependency>Добавьте учётные данные в application.properties:
moy-nalog.username=ваш_логин
moy-nalog.password=ваш_парольБиблиотека автоматически создаст и зарегистрирует бин MoyNalogClient.
Аутентификация выполняется при старте приложения — если учётные данные неверны, приложение не запустится.
Используйте клиент через инъекцию зависимостей:
@Service
public class TaxService {
private final MoyNalogClient moyNalogClient;
public TaxService(MoyNalogClient moyNalogClient) {
this.moyNalogClient = moyNalogClient;
}
public Receipt registerIncome() {
return moyNalogClient.addIncome(List.of(
new IncomeItem("Консультация", 1, 5000.0),
new IncomeItem("Разработка", 3, 10000.0)
));
}
}| Свойство | По умолчанию | Описание |
|---|---|---|
moy-nalog.username |
— | Логин (обязательно) |
moy-nalog.password |
— | Пароль (обязательно) |
moy-nalog.zone-offset |
Z (UTC) |
Часовой пояс чека, например +03:00 |
moy-nalog.api-path |
https://lknpd.nalog.ru/api/v1 |
Базовый URL API |
moy-nalog.prefix |
(пусто) | Префикс генерируемого ID устройства |
moy-nalog.request-timeout |
30 |
Таймаут ожидания ответа от сервера (секунды) |
moy-nalog.proxy.host |
(не задан) | Хост прокси-сервера |
moy-nalog.proxy.port |
8080 |
Порт прокси-сервера |
moy-nalog.proxy.username |
(не задан) | Логин для аутентификации на прокси |
moy-nalog.proxy.password |
(не задан) | Пароль для аутентификации на прокси |
// С настройками по умолчанию
MoyNalogClient client = new MoyNalogClient();
client.init("ваш_логин", "ваш_пароль");
// Или с кастомной конфигурацией
MoyNalogClientConfig config = new MoyNalogClientConfig();
config.setZoneOffset("+03:00");
config.setRequestTimeout(60); // секунды
MoyNalogClient client = new MoyNalogClient(config);
AuthenticationDTO.Profile profile = client.init("ваш_логин", "ваш_пароль");
System.out.println("Авторизован: " + profile.getInn());moy-nalog.proxy.host=proxy.example.com
moy-nalog.proxy.port=8080
# Если прокси требует аутентификации:
moy-nalog.proxy.username=proxyuser
moy-nalog.proxy.password=proxypassMoyNalogClientConfig config = new MoyNalogClientConfig();
config.setProxyHost("proxy.example.com");
config.setProxyPort(8080);
// Если прокси требует аутентификации:
config.setProxyUsername("proxyuser");
config.setProxyPassword("proxypass");
MoyNalogClient client = new MoyNalogClient(config);
client.init("ваш_логин", "ваш_пароль");List<IncomeItem> services = List.of(
new IncomeItem("Консультация", 1, 3000.0), // 1 × 3000 = 3000 руб.
new IncomeItem("Обучение", 2, 1500.0) // 2 × 1500 = 3000 руб.
);
Receipt receipt = client.addIncome(services);
System.out.println("UUID: " + receipt.uuid());
System.out.println("JSON-чек: " + receipt.jsonUrl());
System.out.println("Для печати:" + receipt.printUrl());CompletableFuture<Receipt> future = client.addIncomeAsync(services);
future.thenAccept(receipt -> System.out.println("Чек зарегистрирован: " + receipt.uuid()))
.exceptionally(ex -> { System.err.println("Ошибка: " + ex.getMessage()); return null; });// true — только избранные реквизиты, false — все
List<PaymentType> paymentTypes = client.getPaymentTypes(true);
PaymentType account = paymentTypes.get(0);List<IncomeItem> services = List.of(
new IncomeItem("Разработка сайта", 1, 50000.0)
);
Invoice invoice = client.createInvoice(
account, // реквизиты оплаты
"ИП Иванов Иван Иванович", // наименование плательщика
"123456789012", // ИНН плательщика
ClientType.FROM_LEGAL_ENTITY, // тип плательщика
services
);
System.out.println("Статус: " + invoice.getStatus());
System.out.println("Сумма: " + invoice.getTotalAmount());
System.out.println("Ссылка оплаты: " + invoice.getTransitionPageURL());| Поле | Тип | Описание |
|---|---|---|
name |
String |
Наименование услуги |
quantity |
int |
Количество единиц |
amount |
double |
Цена за одну единицу (не итог) |
Итоговая сумма по услуге рассчитывается автоматически:
amount × quantity.
| Поле | Тип | Описание |
|---|---|---|
uuid |
String |
Идентификатор чека в ФНС |
jsonUrl |
String |
Ссылка на данные чека (JSON) |
printUrl |
String |
Ссылка на чек для печати |
| Поле | Тип | Описание |
|---|---|---|
id |
Long |
Идентификатор в системе ФНС |
type |
String |
Тип: ACCOUNT, CARD и др. |
bankName |
String |
Наименование банка |
bankBik |
String |
БИК банка |
currentAccount |
String |
Расчётный счёт |
corrAccount |
String |
Корреспондентский счёт |
favorite |
Boolean |
Является ли избранным |
| Значение | Описание |
|---|---|
ClientType.FROM_LEGAL_ENTITY |
Юридическое лицо или ИП |
ClientType.FROM_INDIVIDUAL |
Физическое лицо |
| Поле | Тип | Описание |
|---|---|---|
invoiceId |
Long |
Числовой идентификатор счёта |
uuid |
String |
UUID счёта |
status |
String |
Статус: CREATED, PAID, CANCELLED |
transitionPageURL |
String |
Ссылка на страницу оплаты |
clientType |
ClientType |
Тип плательщика |
clientName |
String |
Наименование плательщика |
clientInn |
String |
ИНН плательщика |
totalAmount |
BigDecimal |
Итоговая сумма (руб.) |
totalTax |
BigDecimal |
Сумма налога (руб.) |
services |
List<ServiceItem> |
Список позиций |
createdAt |
String |
Время создания (ISO-8601) |
paidAt |
String |
Время оплаты (null, если не оплачен) |
cancelledAt |
String |
Время отмены (null, если не отменён) |
| Исключение | Когда выбрасывается |
|---|---|
ApiRequestException |
Сервер вернул код ответа, отличный от 200. Содержит statusCode и body. |
ApiException |
Сетевая ошибка или прерывание потока. |
IllegalStateException |
Вызов метода до init. |
try {
Receipt receipt = client.addIncome(services);
} catch (ApiRequestException e) {
System.err.println("Код ответа: " + e.getStatusCode());
System.err.println("Ответ сервера: " + e.getBody());
} catch (ApiException e) {
System.err.println("Сетевая ошибка: " + e.getMessage());
}Токен доступа имеет ограниченный срок жизни. Перед каждым вызовом addIncome, createInvoice и getPaymentTypes библиотека проверяет, не истёк ли токен, и при необходимости автоматически обновляет его через refresh-токен — без повторного ввода пароля.
Для безопасной работы в многопоточной среде используется ReadWriteLock:
- Обычные запросы захватывают read-lock — выполняются параллельно.
- Обновление токена захватывает write-lock — блокирует новые запросы до завершения обновления.
- Убрана принудительная фиксация
HTTP/1.1— клиент теперь автоматически согласует протокол (HTTP/1.1 / HTTP/2) через TLS ALPN - Добавлен настраиваемый таймаут ответа (
moy-nalog.request-timeout, по умолчанию 30 сек) - Добавлен автоматический retry при аутентификации в случае transient сетевой ошибки
- Добавлен enum
ClientType(FROM_LEGAL_ENTITY,FROM_INDIVIDUAL) вместо строки вcreateInvoice
- Добавлена поддержка прокси (с аутентификацией и без)
- Добавлены методы
getPaymentTypes()иcreateInvoice() - Новые модели:
PaymentType,Invoice
- Первый публичный релиз