Юкасса 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-адреса с разной логикой.