npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fw42/yoomoney

v2.4.6

Published

Runtime-agnostic TypeScript SDK for YooMoney Wallet API — payments, history, notifications, payment links

Readme

yoomoney-sdk

Runtime-agnostic TypeScript SDK для YooMoney Wallet API.

Работает с Node.js 18+, Bun, Deno — использует только Web API (fetch, crypto.subtle, URLSearchParams), без привязки к конкретному рантайму.

Возможности

  • Информация об аккаунте — баланс, статус, привязанные карты
  • История операций — фильтрация по типу, лейблу, дате; автопагинация
  • Детали операции — подробная информация о транзакции
  • Проверка платежа по лейблу — верификация входящих платежей
  • Ожидание платежа — поллинг до появления платежа с заданным лейблом
  • Генерация ссылок на оплату — URL и HTML-форма для quickpay
  • Верификация вебхуков — HMAC-SHA256 проверка подписи уведомлений

Получение токена

Для работы с API нужен OAuth-токен YooMoney. Вот как его получить:

Шаг 1. Регистрация приложения

Перейдите на страницу регистрации: https://yoomoney.ru/myservices/new

Заполните форму:

| Поле | Что указать | |---|---| | Название | Любое, например My App | | Redirect URI | Рабочий домен, который вы контролируете (например https://example.com). Не используйте localhost или несуществующие домены — редирект не сработает и вы не получите code. | | Почта для связи | Ваш email |

Важно: Redirect URI должен быть реальным доменом, на который браузер сможет перейти. После авторизации YooMoney перенаправит вас на этот адрес с параметром code в URL — вам нужно будет скопировать его из адресной строки.

Нажмите Подтвердить. Вы получите client_id и client_secret — сохраните их.

Шаг 2. Авторизация (получение code)

Откройте в браузере ссылку, подставив ваш client_id и redirect_uri:

https://yoomoney.ru/oauth/authorize?client_id=ВАШ_CLIENT_ID&response_type=code&redirect_uri=https://ваш-домен.com&scope=account-info%20operation-history%20operation-details
  • Залогиньтесь в YooMoney и разрешите доступ приложению.
  • Браузер перенаправит вас на https://ваш-домен.com?code=XXXXXXXXX.
  • Скопируйте значение code из адресной строки.

Шаг 3. Обмен code на токен

curl -X POST https://yoomoney.ru/oauth/token \
  -d "code=ВАШ_CODE&client_id=ВАШ_CLIENT_ID&grant_type=authorization_code&redirect_uri=https://ваш-домен.com&client_secret=ВАШ_CLIENT_SECRET"

В ответе получите access_token:

{"access_token":"4100XXXX.XXXXXXXX..."}

Токен бессрочный — действует пока не отзовёте доступ или не запросите авторизацию повторно.

Шаг 4. Проверка

YOOMONEY_TOKEN="ваш_access_token" npm run example:account

Если всё правильно — увидите информацию о вашем кошельке.


Установка

npm install @fw42/yoomoney
# или
bun add @fw42/yoomoney

Быстрый старт (из исходников)

Node.js

git clone https://github.com/FastWalker42/yoomoney-sdk.git
cd yoomoney-sdk
npm install
npm run build

Bun

git clone https://github.com/FastWalker42/yoomoney-sdk.git
cd yoomoney-sdk
bun install

Bun нативно исполняет TypeScript, сборка не нужна.


Использование

import { YooMoneyClient } from "@fw42/yoomoney";

const client = new YooMoneyClient({
  token: "your_oauth_token",
});

// Последние 10 операций
const operations = await client.getRecentOperations(10);

// Проверить платёж по лейблу (с проверкой суммы и статуса)
const result = await client.checkPaymentByLabel("order-42", { amount: 500 });
if (result.found) {
  console.log("Платёж найден!", result.operations);
}

// Информация об аккаунте
const info = await client.getAccountInfo();
console.log(`Баланс: ${info.balance}`);

// Детали операции
const details = await client.getOperationDetails({
  operation_id: "1234567",
});

// Итерация по всей истории (автопагинация)
for await (const op of client.getOperationHistoryAll({ type: "deposition" })) {
  console.log(op.title, op.amount);
}

// Ожидание платежа (поллинг, таймаут 5 минут, проверка суммы)
const ops = await client.waitForPayment("order-42", {
  timeoutMs: 300_000,
  intervalMs: 5_000,
  amount: 500,
});

Генерация ссылок на оплату

Создавайте ссылки на оплату через YooMoney quickpay. Указывайте label чтобы потом идентифицировать платёж.

import { generatePaymentLink, generatePaymentForm } from "@fw42/yoomoney";

// Ссылка — открывается в браузере, пользователь сразу видит форму оплаты
const link = generatePaymentLink({
  receiver: "4100118425529732",  // ваш кошелёк
  sum: 500,                      // сумма списания с отправителя
  label: "order-123",            // уникальный ID для идентификации
  paymentType: "AC",             // AC = карта, PC = кошелёк
  successURL: "https://example.com/thanks",
});
console.log(link);

// HTML-форма для встраивания на сайт
const html = generatePaymentForm({
  receiver: "4100118425529732",
  sum: 500,
  label: "order-123",
});

Проверка платежей — как это работает

YooMoney не передаёт «memo» или произвольный комментарий от отправителя при проверке. Вместо этого используется механизм label — уникальная метка, которую вы задаёте при создании ссылки на оплату.

Схема работы

1. Генерируете ссылку на оплату с уникальным label (например, order-123)
2. Отправитель переходит по ссылке и оплачивает
3. Проверяете платёж одним из двух способов:
   а) Поллинг — периодически запрашиваете историю с фильтром по label
   б) Вебхук — YooMoney отправляет POST на ваш сервер при поступлении перевода

Способ 1: Поллинг (простой)

import { YooMoneyClient, generatePaymentLink } from "@fw42/yoomoney";

const client = new YooMoneyClient({ token: "..." });
const label = `order-${Date.now()}`;

// Генерируем ссылку
const link = generatePaymentLink({
  receiver: "4100118425529732",
  sum: 100,
  label,
});
console.log("Отправьте пользователю:", link);

// Ждём оплату (поллинг каждые 5 секунд, таймаут 5 минут)
// amount: 100 — SDK автоматически проверит что сумма >= 100
try {
  const ops = await client.waitForPayment(label, {
    timeoutMs: 300_000,
    intervalMs: 5_000,
    amount: 100,
  });
  console.log("Оплата получена!", ops[0].amount);
} catch {
  console.log("Таймаут — оплата не поступила");
}

Способ 2: Вебхук (мгновенный)

YooMoney отправляет HTTP POST на ваш сервер при каждом входящем переводе.

  1. Настройте Notification URL в настройках приложения
  2. Обрабатывайте уведомления:
import {
  parseNotification,
  verifyNotificationSignature,
} from "@fw42/yoomoney";

// В вашем HTTP-сервере (Express, Hono, Bun.serve и т.д.)
// requestBody — СЫРАЯ строка тела POST (не парсенная).
async function handleWebhook(requestBody: string) {
  // Рекомендуется проверять подпись по СЫРОМУ телу — это гарантирует,
  // что подпись вычислена ровно для того набора полей, что прислал YooMoney.
  const isValid = await verifyNotificationSignature(
    requestBody,
    "ваш_секрет_из_настроек_уведомлений",
  );

  if (!isValid) {
    return { status: 403, body: "Invalid signature" };
  }

  // После проверки подписи — парсим и используем.
  const notification = parseNotification(requestBody);
  console.log(`Получен платёж: ${notification.amount} руб.`);
  console.log(`Label: ${notification.label}`);
  console.log(`От: ${notification.sender}`);

  return { status: 200, body: "OK" };
}

⚠️ Идемпотентность: ЮMoney делает до 3 попыток доставки одного и того же уведомления, если не получил 200 OK в ответ (сразу, через 10 минут, через час). SDK не дедуплицирует уведомления сам — если ваш обработчик необратимо меняет состояние (зачисляет баланс, выполняет заказ), проверяйте operation_id на повтор перед обработкой (например, по уникальному индексу в БД), иначе временный сбой ответа на первую попытку может привести к двойной обработке одного платежа.

Куда приходит label и как его получить

Label привязывается к операции автоматически когда отправитель оплачивает по вашей ссылке. Он попадает в историю операций вашего кошелька — того, на который пришёл перевод. Отдельный сервер для этого не нужен.

Вы генерируете ссылку с label  →  отправитель платит  →
в истории ВАШЕГО кошелька появляется операция с этим label  →
вы запрашиваете историю с фильтром по label и находите её

Получить label можно двумя способами:

  • checkPaymentByLabel(label, opts?) — запрос к operation-history с фильтром label. По умолчанию возвращает только успешные операции (status === "success"). С опцией amount — только те, где сумма >= указанной.
  • getOperationDetails({ operation_id }) — в ответе будет поле label, если оно было задано при оплате.

Отправитель не видит label и не может его изменить — он зашит в ссылку/форму оплаты.

Как идентифицировать кто заплатил без memo

YooMoney не поддерживает произвольное «memo» от отправителя. Вместо этого:

  1. Label (рекомендуемый) — ваш главный инструмент. При генерации ссылки задаёте уникальный label (например user-42-topup или order-abc). Этот label привязан к ссылке и возвращается при проверке платежа — как через поллинг operation-history, так и через вебхук.

  2. Sender — номер кошелька отправителя (приходит в operation-details и в notification). Если отправитель платит с карты — поле пустое.

  3. Amount — если каждому пользователю выставлять уникальную сумму (например, +0.01 * userId), можно идентифицировать по сумме. Ненадёжный метод, только как запасной.

Рекомендуемый паттерн: каждому пользователю генерируете уникальную ссылку со своим label. Проверяете по label через waitForPayment() или checkPaymentByLabel() — это 100% надёжно и не требует вебхуков или сервера.

⚠️ Безопасность: Всегда указывайте ожидаемую сумму при проверке платежа! Пользователь может изменить sum в URL оплаты и отправить меньше. SDK автоматически отсеет операции с суммой < ожидаемой.

// Небезопасно — примет любую сумму:
await client.checkPaymentByLabel("order-42");

// Безопасно — проверит что amount >= 1000:
await client.checkPaymentByLabel("order-42", { amount: 1000 });

Примеры

Node.js (tsx)

YOOMONEY_TOKEN=<token> npx tsx examples/get-account-info.ts
YOOMONEY_TOKEN=<token> npx tsx examples/get-history.ts
YOOMONEY_TOKEN=<token> npx tsx examples/get-details.ts <operation_id>
YOOMONEY_TOKEN=<token> npx tsx examples/check-payment.ts <label>
npx tsx examples/generate-link.ts

Bun

YOOMONEY_TOKEN=<token> bun examples/get-account-info.ts
YOOMONEY_TOKEN=<token> bun examples/get-history.ts
YOOMONEY_TOKEN=<token> bun examples/get-details.ts <operation_id>
YOOMONEY_TOKEN=<token> bun examples/check-payment.ts <label>
bun examples/generate-link.ts

Тесты

# Node.js
npm test

# Bun
bun test

API

new YooMoneyClient(options)

| Параметр | Тип | По умолчанию | Описание | |---|---|---|---| | token | string | — | OAuth-токен (обязательный) | | baseUrl | string | https://yoomoney.ru | Базовый URL | | timeout | number | 10000 | Таймаут запроса в мс | | maxRetries | number | 3 | Количество ретраев на 429 / 5xx / сетевые ошибки | | retryBaseDelay | number | 500 | Базовая задержка для экспоненциального backoff в мс |

Методы клиента

| Метод | Описание | |---|---| | getAccountInfo() | Информация об аккаунте | | getOperationHistory(params?) | Страница истории операций | | getOperationHistoryAll(params?) | AsyncGenerator по всей истории | | getOperationDetails({ operation_id }) | Детали операции | | checkPaymentByLabel(label, opts?) | Проверка входящего платежа по лейблу с валидацией суммы и статуса | | getRecentOperations(count?) | Последние N операций | | waitForPayment(label, opts?) | Поллинг до появления платежа с лейблом (поддерживает amount и requireSuccess) |

CheckPaymentOptions

| Параметр | Тип | По умолчанию | Описание | |---|---|---|---| | amount | number | — | Ожидаемая сумма (сколько вы хотите получить на кошелёк). Если не указана — open-ended режим: принимается любой платёж с подходящим label. | | requireSuccess | boolean | true | Отсеивает операции с status !== "success" | | feePayer | "sender" \| "receiver" | "sender" | Кто платит комиссию YooMoney (бизнес-логика SDK, не API). См. таблицу ниже. | | ignoreFee | boolean | false | Если true, точное сравнение op.amount >= amount без учёта комиссии. |

Семантика проверки суммы (используется только op.amount — это единственное поле, доступное через operation-history):

| amount задан? | ignoreFee | feePayer | Пороговое значение | |---|---|---|---| | нет (open-ended) | — | — | ничего не проверяется, принимается любой входящий платёж с этим label | | да | true | игнорируется | op.amount >= amount (точное сравнение, без всякого допуска) | | да | false | "sender" (по умолчанию) | op.amount >= amount - ROUNDING_TOLERANCE | | да | false | "receiver" | op.amount >= amount * (1 - MAX_FEE_RATE) - ROUNDING_TOLERANCE |

Почему 3%? Это максимальная комиссия YooMoney (для банковских карт). Переводы между кошельками дешевле (0.5%), но 3% — безопасный worst-case для любого способа оплаты. Константа MAX_FEE_RATE = 0.03 экспортируется из SDK.

ROUNDING_TOLERANCE = 0.01 (1 копейка, экспортируется из SDK) — покрывает округление до копейки при вычислении sum и комиссии YooMoney. При feePayer: "sender" generatePaymentLink считает sum точной обратной формулой (amount / (1 - MAX_FEE_RATE), а не приближением amount * (1 + MAX_FEE_RATE)), поэтому при реальной комиссии ровно 3% зачисленная сумма математически всегда равна amount — допуск нужен только как запас на округление, а не потому что расхождение действительно ожидается.

feePayer в generatePaymentLink и в checkPaymentByLabel должны совпадать! Иначе логика сломается: например, если ссылка создана с feePayer="sender" (отправитель платит сверху), а проверка идёт с feePayer="receiver" (допускаем недоплату 3%), то недобросовестный плательщик сможет заплатить меньше.

WaitForPaymentOptions (extends CheckPaymentOptions)

| Параметр | Тип | По умолчанию | Описание | |---|---|---|---| | timeoutMs | number | 300000 | Общий таймаут поллинга в мс | | intervalMs | number | 5000 | Интервал между запросами в мс (минимум 1000) | | amount, requireSuccess, feePayer, ignoreFee | — | — | Любые поля из CheckPaymentOptions |

PaymentLinkParams

| Параметр | Тип | По умолчанию | Описание | |---|---|---|---| | receiver | string | — | Номер кошелька получателя (обязательный) | | sum | number | — | Сумма, которую отправитель должен заплатить. Опциональная — если опустить, будет создана open-ended ссылка. Если указан feePayer, SDK автоматически скорректирует sum (см. ниже). | | paymentType | "PC" \| "AC" | — | Метод оплаты: PC = кошелёк YooMoney, AC = банковская карта | | label | string | — | Уникальный идентификатор платежа (до 64 символов) | | successURL | string | — | URL для редиректа после успешной оплаты | | feePayer | "sender" \| "receiver" | — | Кто платит комиссию. Если задан, SDK корректирует sum: "sender" → sum / (1 - MAX_FEE_RATE), "receiver" → sum без изменений |

Корректировка sum в зависимости от feePayer:

| feePayer | sum в ссылке | Что произойдёт | |------------|----------------|----------------| | не задан | как передали | YooMoney сам решает (по умолчанию комиссия вычитается из sum) | | "sender" | sum / (1 - MAX_FEE_RATE) (≈ sum * 1.0309) | Отправитель платит на ~3% больше, получатель получает ровно sum (с точностью до копейки) | | "receiver" | sum | Отправитель платит sum, получатель получает sum - 3% |

sum * (1 + MAX_FEE_RATE) выглядит как очевидная обратная формула, но это лишь приближение первого порядка — комиссия берётся с уже увеличенной суммы, а не с исходной. Точная формула — sum / (1 - MAX_FEE_RATE). Разница между ними растёт пропорционально сумме (≈0.09% от sum при 3%): незаметно на маленьких тестовых суммах, но на 1000 ₽ это уже ~90 копеек недостачи.

Примеры использования

Комиссию платит отправитель (рекомендуемый сценарий для фиксированной суммы)

// Хотим получить 500 ₽. SDK автоматически сделает sum = 500 / 0.97 = 515.46.
const link = generatePaymentLink({
  receiver: "4100118425529732",
  sum: 500,
  feePayer: "sender",
  label: "order-42",
  paymentType: "AC",
});

// Проверяем, что получатель получил ровно 500 ₽ (с точностью до ROUNDING_TOLERANCE)
const result = await client.checkPaymentByLabel("order-42", {
  amount: 500,
  feePayer: "sender", // важно: то же значение, что и в generatePaymentLink
});

Комиссию платит получатель (юзер платит ровно указанную сумму)

// Юзер заплатит ровно 500 ₽. Получатель получит ~485 ₽ (500 - 3%).
const link = generatePaymentLink({
  receiver: "4100118425529732",
  sum: 500,
  feePayer: "receiver",
  label: "order-42",
});

// Принимаем платежи, где получатель получил >= 500 * 0.97 - ROUNDING_TOLERANCE ≈ 484.99 ₽
const result = await client.checkPaymentByLabel("order-42", {
  amount: 500,
  feePayer: "receiver",
});

Игнорировать комиссию (точное сравнение, без допуска)

// Полезно для переводов между кошельками YooMoney (там комиссия 0.5%,
// но вы хотите точно знать, что получатель получил >= 500 ₽).
const result = await client.checkPaymentByLabel("order-42", {
  amount: 500,
  ignoreFee: true,
});

Свободное пополнение баланса (open-ended)

// Создаём ссылку без sum — юзер введёт любую сумму
const link = generatePaymentLink({
  receiver: "4100118425529732",
  label: "topup-user-42",
});

// Проверяем, что пришёл любой платёж с этим label (сумма не важна)
const result = await client.checkPaymentByLabel("topup-user-42");
if (result.found) {
  // result.operations[0].amount — сколько фактически зачислено
  const credited = result.operations[0].amount;
  console.log(`Зачислено: ${credited} ₽`);
}

Ожидание платежа с поллингом

// Ждём до 10 минут, проверяя каждые 5 секунд
const ops = await client.waitForPayment("order-99", {
  amount: 1000,
  feePayer: "sender",
  timeoutMs: 600_000,
  intervalMs: 5_000,
});
console.log(`Платёж получен: ${ops[0].amount} ₽ зачислено`);

Утилиты

| Функция | Описание | |---|---| | generatePaymentLink(params) | URL для оплаты через quickpay | | generatePaymentForm(params, buttonText?) | HTML-форма для встраивания | | parseNotification(body) | Парсинг тела вебхука | | verifyNotificationSignature(input, secret) | Проверка HMAC-SHA256 подписи. input может быть сырой URL-encoded строкой, URLSearchParams или распарсенным IncomingNotification. Рекомендуется передавать сырую строку — это гарантирует, что подпись будет вычислена для того же набора полей, что прислал YooMoney. |

Подпись уведомлений: SDK реализует алгоритм HMAC-SHA256 согласно официальной документации YooMoney: удаляет поле sign, сортирует оставшиеся параметры по алфавиту, применяет URL-кодирование (RFC 3986), объединяет в key=value&key=value... (пустые значения как key=), вычисляет HMAC-SHA256 и сравнивает с sign (hex, lowercase) с использованием constant-time сравнения.

Обработка ошибок

Ошибки валидации параметров и HTTP-статус-ошибки — экземпляры YooMoneyError (или её подкласса YooMoneyHttpError), с полем code, по которому удобно ветвить логику. Но не все ошибки SDK обёрнуты: если fetch() сам падает (обрыв сети, DNS, таймаут по AbortController) и все maxRetries попыток исчерпаны, наружу летит сырая исходная ошибка (TypeError/DOMException и т.п.), не YooMoneyError — это учтено в примере ниже веткой else.

import { YooMoneyError, YooMoneyHttpError } from "@fw42/yoomoney";

try {
  await client.waitForPayment(label, { amount: 500, timeoutMs: 30_000 });
} catch (err) {
  if (err instanceof YooMoneyHttpError) {
    // err.statusCode, err.statusText — стабильный HTTP-статус после исчерпания ретраев
    console.error(`HTTP ${err.statusCode}: ${err.statusText}`);
  } else if (err instanceof YooMoneyError && err.code === "timeout") {
    console.log("Платёж не пришёл за отведённое время");
  } else if (err instanceof YooMoneyError) {
    console.error(`Ошибка API: ${err.code} — ${err.message}`);
  } else {
    // Сетевая ошибка (fetch упал, ретраи исчерпаны) — НЕ YooMoneyError.
    console.error("Сетевая ошибка:", err);
  }
}

YooMoneyHttpError extends YooMoneyError и всегда имеет code === "http_error", плюс statusCode/statusText. Бросается методом post() после исчерпания maxRetries на ответах, не являющихся response.ok (например, стабильный 401/403/5xx).

Значения code у YooMoneyError, которые бросает сам SDK (валидация параметров, до похода в API):

| code | Откуда | Причина | |---|---|---| | invalid_token | new YooMoneyClient() | token не передан или не строка | | invalid_label | checkPaymentByLabel, waitForPayment, generatePaymentLink, generatePaymentForm | label пустой или длиннее 64 символов | | invalid_amount | checkPaymentByLabel, waitForPayment | amount не положительное конечное число | | invalid_fee_payer | checkPaymentByLabel, waitForPayment, generatePaymentLink, generatePaymentForm | feePayer — не "sender"/"receiver" | | invalid_interval | waitForPayment | intervalMs < 1000 | | timeout | waitForPayment | Платёж с этим label не найден за timeoutMs | | illegal_param_operation_id | getOperationDetails | operation_id не передан | | invalid_content_type | внутренний post() | YooMoney ответил не application/json (обычно значит проблему с URL/окружением, не с данными) | | invalid_params / invalid_receiver / invalid_sum | generatePaymentLink, generatePaymentForm | Невалидные PaymentLinkParams (нет receiver, sum не положительное число и т.п.) |

Отдельно: если сам YooMoney API вернул {"error": "..."} (например, illegal_param_operation_id от самого API, а не от SDK-валидации), SDK пробрасывает это значение как code без изменений — так что err.code может содержать и коды ошибок YooMoney напрямую, не только перечисленные выше.

Сетевые сбои (обрыв соединения, таймаут по AbortController) после исчерпания maxRetries пробрасываются как есть, не оборачиваются в YooMoneyError — см. пример выше.

Лицензия

MIT