WordPress · 7 мин чтения

Юкасса API: приём платежей на своём бэкенде

Юкасса API нужен, когда готового модуля эквайринга под вашу платформу нет или его логики недостаточно: свой чекаут на Next.js, бэкенд на Laravel для интернет-магазина, приём оплаты в Telegram-боте на aiogram или сценарий в n8n, который создаёт платёж по вебхуку из CRM. Я подключал такие интеграции на разных стеках, и разница между ними обычно только в языке обработчика вебхука, сам протокол Юкасса API одинаковый везде: HTTP-запросы, JSON и два ключа доступа.

Что нужно для старта: shopId, secretKey и тестовый режим

Доступ к API появляется в личном кабинете Юкассы после регистрации магазина, для этого нужен статус ИП, ООО или самозанятого: у ЮKassa есть отдельный приём платежей для самозанятых, из документов там нужны паспорт и ИНН, договор заключается онлайн, а встроить оплату можно и платёжным модулем, и по API. После подписания договора в разделе «Интеграция» открывается вкладка API, там два значения: shopId, числовой идентификатор магазина, и secretKey, строка вида live_... или test_.... Оба используются как логин и пароль в HTTP Basic Auth, отдельный OAuth-токен для базовой интеграции не нужен.

Пока магазин проходит модерацию, доступен тестовый режим: отдельная пара shopId и secretKey с префиксом test_ и набор тестовых карт для эмуляции успешной оплаты, отказа банка или недостатка средств. Я всегда прогоняю весь сценарий, от создания платежа до обработки вебхука, в тестовом режиме и только потом переключаю ключи в конфиге на боевые.

Если сайт уже стоит на WooCommerce и штатного плагина Юкассы хватает для обычной корзины, городить прямую интеграцию с API смысла нет, плагин закрывает типовой сценарий оплаты за вечер настройки. Прямой API нужен, когда логика заказа не укладывается в готовый модуль: свой чекаут, холдирование средств на несколько дней, или бэкенд, который в одном запросе параллельно считает доставку через СДЭК и создаёт платёж в Юкассе.

Создание платежа: запрос к /v3/payments

Платёж создаётся POST-запросом на https://api.yookassa.ru/v3/payments. Авторизация Basic: логин shopId, пароль secretKey. Обязательный заголовок Idempotence-Key с уникальным значением на каждый запрос, обычно uuid, он защищает от повторного списания, если форма ушла на сервер дважды или запрос повторился из-за таймаута. В теле нужны как минимум amount с value и currency, confirmation с типом подтверждения и capture, флаг, списывать деньги сразу или сначала заблокировать. Можно добавить description и metadata, туда я обычно кладу order_id, чтобы потом сверять платежи с заказами в своей базе.

curl https://api.yookassa.ru/v3/payments \
  -u shopId:secretKey \
  -H "Idempotence-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": {
      "value": "1000.00",
      "currency": "RUB"
    },
    "capture": true,
    "confirmation": {
      "type": "redirect",
      "return_url": "https://example.com/order/return"
    },
    "description": "Заказ №145"
  }'

В ответ Юкасса возвращает объект платежа со статусом pending, полем confirmation_url, если выбран тип redirect, и полем test, которое показывает, тестовый это платёж или реальный.

{
  "id": "2d3a1e73-000f-5000-9000-1b6f9db6a1d2",
  "status": "pending",
  "paid": false,
  "amount": {
    "value": "1000.00",
    "currency": "RUB"
  },
  "confirmation": {
    "type": "redirect",
    "confirmation_url": "https://yoomoney.ru/checkout/payments/v2/contract?orderId=2d3a1e73"
  },
  "created_at": "2026-09-01T10:15:00.000Z",
  "test": true
}

Клиента нужно отправить на confirmation_url, он вводит данные карты или платит через СБП, после чего возвращается на return_url. На этот момент полагаться нельзя: возврат на return_url значит только, что человек закрыл окно оплаты, а не что деньги списаны. Финальный статус подтверждает либо вебхук, либо повторный GET-запрос на v3/payments/{id}.

В payment_method_data можно сразу указать способ оплаты, например bank_card или sbp, тогда Юкасса пропустит экран выбора способа и сразу покажет форму карты или QR для Системы быстрых платежей. Юкасса поддерживает и официальный PHP SDK, пакет yoomoney/yookassa-sdk-php для Composer, он оборачивает те же запросы в готовые классы и сам подставляет Idempotence-Key, если не передать его явно. На проектах с Composer и автозагрузкой беру SDK, чтобы не сериализовать JSON руками, а на лендинге из одного файла проще оставить голый curl без лишней зависимости.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Способы подтверждения оплаты: redirect, embedded, qr

Тип confirmation задаёт, как клиент увидит форму оплаты.

Тип confirmation Где клиент видит форму оплаты Типичный сценарий
redirect На отдельной странице Юкассы, после оплаты возврат на return_url Лендинг, интернет-магазин без кастомного чекаута
embedded Прямо на вашей странице через JS-виджет Юкассы Кастомный чекаут, когда важно не уводить клиента с домена
qr QR-код, который клиент сканирует камерой телефона Офлайн-точка, оплата в переписке или в Telegram-боте

Для сайта на Next.js или Laravel обычно достаточно redirect, для формы, встроенной прямо в страницу без ухода на чужой домен, подходит embedded, а для оплаты по QR-коду в офлайн-точке или в переписке есть тип qr.

Двухстадийные платежи: холдирование вместо мгновенного списания

Если поставить capture в false, Юкасса не спишет деньги сразу, а заблокирует сумму на карте клиента и переведёт платёж в статус waiting_for_capture. Это нужно, когда услугу оказывают не мгновенно: бронирование, аренда оборудования, маркетплейс, где продавец подтверждает заказ вручную.

Параметр capture: true capture: false
Когда спишутся деньги Сразу при статусе succeeded Только после отдельного запроса capture
Статус после оплаты картой succeeded waiting_for_capture
Типичный сценарий Магазин с мгновенной отгрузкой Бронирование, маркетплейс, услуги с подтверждением вручную

Списание подтверждается отдельным запросом на v3/payments/{id}/capture, можно списать полную сумму или часть, если товара оказалось меньше, чем в заказе.

curl https://api.yookassa.ru/v3/payments/2d3a1e73-000f-5000-9000-1b6f9db6a1d2/capture \
  -u shopId:secretKey \
  -H "Idempotence-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": {
      "value": "1000.00",
      "currency": "RUB"
    }
  }'

Сколько держится waiting_for_capture, зависит от способа оплаты: от 2 часов до 7 дней. Точный срок приходит в параметре expires_at, на него и надо опираться в бизнес-логике, а не на верхнюю границу. Если за этот срок capture не вызвать, Юкасса отменит платёж с причиной expired_on_capture и деньги вернутся клиенту. Если сделка не состоялась раньше, платёж можно отменить явно запросом на v3/payments/{id}/cancel, чтобы не ждать автоотмены.

Обработка вебхуков на своём сервере

URL для уведомлений указывается в личном кабинете в разделе с HTTP-уведомлениями, туда Юкасса будет слать POST-запрос при каждом изменении статуса: payment.waiting_for_capture, payment.succeeded, payment.canceled, refund.succeeded.

{
  "type": "notification",
  "event": "payment.succeeded",
  "object": {
    "id": "2d3a1e73-000f-5000-9000-1b6f9db6a1d2",
    "status": "succeeded",
    "paid": true,
    "amount": {
      "value": "1000.00",
      "currency": "RUB"
    },
    "metadata": {
      "order_id": "145"
    }
  }
}

По умолчанию подпись запроса Юкасса не присылает, поэтому обработчик нельзя писать так, будто телу запроса можно верить. Я делаю два уровня проверки: сверяю IP отправителя со списком адресов Юкассы из документации и на каждое уведомление отдельно запрашиваю GET v3/payments/{id} своими же ключами, прежде чем менять статус заказа в базе. Так поддельный POST на адрес обработчика не пройдёт, потому что реальные данные о платеже сервер берёт не из тела вебхука, а напрямую у Юкассы. Отдельно закладываю идемпотентность на своей стороне: Юкасса может прислать одно и то же уведомление повторно, если сервер не ответил 200 OK вовремя, поэтому перед тем как выдавать доступ или списывать товар со склада, проверяю, не обработан ли уже этот payment_id.

Для такого обработчика не нужен отдельный сервер, PHP-скрипт на обычном хостинге с HTTPS и открытым портом 443 справляется полностью, специальный VPS под приём вебхуков не требуется. Если под приём вебхуков ещё не написан бэкенд, я делаю такие интеграции в рамках услуги разработка API на Laravel, туда же обычно добавляю очередь повторных проверок статуса и логирование входящих событий.

В сценариях с n8n вебхук от Юкассы принимает нода Webhook, дальше по цепочке можно проверить event, обновить статус заказа в CRM и отправить уведомление в Telegram-канал менеджеру. Отдельно смотрю, где физически хранятся персональные данные клиента после такой цепочки: если CRM или таблица заказов держит контакты за рубежом, это конфликтует с требованием хранить персональные данные россиян на серверах в РФ, поэтому для интеграций с оплатой выбираю сервисы с российским хостингом данных.

Чеки по 54-ФЗ в запросе на оплату

Если фискализацию ведёт сама Юкасса, в тело запроса на создание платежа добавляется объект receipt с данными покупателя (email или телефон) и списком items: описание позиции, количество, сумма, код НДС vat_code, признак предмета расчёта payment_subject и признак способа расчёта payment_mode.

"receipt": {
  "customer": {
    "email": "client@example.com"
  },
  "items": [
    {
      "description": "Консультация по настройке",
      "quantity": "1.00",
      "amount": {
        "value": "1000.00",
        "currency": "RUB"
      },
      "vat_code": 1,
      "payment_subject": "service",
      "payment_mode": "full_payment"
    }
  ]
}

Без этого объекта касса Юкассы не сможет выбить чек по конкретной покупке. Если у бизнеса уже есть своя онлайн-касса, подключённая отдельно, чек формирует она, и в запросе на оплату этот блок не нужен, тогда данные для чека уходят своим маршрутом, обычно через ту же онлайн-кассу или её API.

Условия и возможности ЮKassa меняются, поэтому перед запуском я уточняю их у самого сервиса.

Частые вопросы

Нужен ли отдельный сервер для приёма вебхуков от Юкассы

Нет, обычного хостинга с PHP и HTTPS достаточно. Требования к URL для уведомлений такие: протокол HTTPS и TCP-порт 443 или 8443, версия TLS/SSL не ниже 1.2, а сертификат подойдёт любой, в том числе самоподписанный. Авторизации на самом адресе быть не должно, иначе Юкасса не сможет достучаться до обработчика.

Как протестировать оплату без реальных денег

Юкасса выдаёт тестовые shopId и secretKey с префиксом test_ и набор тестовых карт для разных сценариев: успешная оплата, отказ банка, недостаток средств. Весь путь от создания платежа до вебхука можно прогнать в этом режиме, прежде чем переключаться на боевые ключи.

Что делать, если вебхук не пришёл

Полагаться только на вебхук рискованно, сеть иногда теряет запросы. Ставлю периодическую проверку через cron: раз в несколько минут выбираю все платежи в статусе pending старше пары минут и запрашиваю их статус через GET v3/payments/{id}, чтобы не зависеть от одной-единственной доставки уведомления.

Можно ли принимать оплату через Юкасса API в Telegram-боте

Да, бот на aiogram создаёт платёж обычным запросом к v3/payments и отправляет пользователю кнопку со ссылкой на confirmation_url. Обработчик вебхука для смены статуса заказа делается отдельно от вебхука самого бота, это два разных HTTPS-адреса с разной логикой.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно