ЮKassa API: интеграция приёма платежей на сайт — полное руководство 2026
ЮKassa API — это способ принимать оплату на сайте или в приложении без redirection на стороннюю страницу (хотя можно и так). Интеграция ЮKassa API занимает 1-2 дня, если вы знаете, что делаете. Если нет — потратите неделю на отладку webhook-ов и поиск ошибок в подписи. Я прошёл этот путь и пишу руководство, которого мне не хватало.
Подготовка: регистрация и ключи
Регистрация
- Зайдите на yookassa.ru и нажмите «Подключить ЮKassa»
- Заполните данные: ИНН, название компании, телефон
- Дождитесь модерации — обычно 1-3 рабочих дня
- После одобрения вы получите доступ к личному кабинету
Получение ключей
В личном кабинете перейдите в «Настройки» → «Интеграция». Вам понадобятся два ключа:
- shop_id — идентификатор магазина (число, например
123456) - secret_key — секретный ключ (начинается с
test_для sandbox илиlive_для production)
Важно: никогда не коммитьте secret_key в репозиторий. Храните в переменных окружения.
Sandbox
ЮKassa предоставляет тестовое окружение. Ключи начинаются с test_. Платежи не реальные, но API работает идентично production.
Архитектура: как работает API
Схема оплаты через ЮKassa API:
1. Ваш сервер → POST /payments → ЮKassa
2. ЮKassa → возвращает payment с подтверждением
3. Ваш фронтенд → перенаправляет клиента на страницу оплаты
4. Клиент оплачивает
5. ЮKassa → webhook на ваш сервер
6. Ваш сервер → обрабатывает webhook → выдаёт товар/услугу
```text
Ключевой момент: **опирайтесь на webhook, не на ответ браузера.** Клиент может закрыть вкладку до редиректа обратно. Webhook гарантированно приходит.
## Создание платежа
### Endpoint
```text
POST https://api.yookassa.ru/v3/payments
```text
### Заголовки
```text
Content-Type: application/json
Idempotence-Key: <уникальный-ключ>
Authorization: Basic <base64(shop_id:secret_key)>
```text
**Idempotence-Key** — важная штука. ЮKassa использует его, чтобы не создать два платежа при повторном запросе (например, если клиент нажал «Оплатить» дважды). Генерируйте UUID для каждого нового платежа.
### Пример на Python
```python
import requests
import uuid
import base64
SHOP_ID = "123456"
SECRET_KEY = "test_xxxxxxxxxxxxx"
# Авторизация
auth = base64.b64encode(f"{SHOP_ID}:{SECRET_KEY}".encode()).decode()
# Создание платежа
headers = {
"Content-Type": "application/json",
"Idempotence-Key": str(uuid.uuid4()),
"Authorization": f"Basic {auth}"
}
payload = {
"amount": {
"value": "1500.00",
"currency": "RUB"
},
"confirmation": {
"type": "redirect",
"return_url": "https://yoursite.ru/payment/success"
},
"capture": True,
"description": "Заказ #1234",
"metadata": {
"order_id": "1234",
"user_id": "5678"
}
}
response = requests.post(
"https://api.yookassa.ru/v3/payments",
json=payload,
headers=headers
)
payment = response.json()
# URL для перенаправления клиента
payment_url = payment["confirmation"]["confirmation_url"]
payment_id = payment["id"]
print(f"Payment ID: {payment_id}")
print(f"Payment URL: {payment_url}")
```text
### Параметры запроса
| Параметр | Обязательный | Описание |
|----------|-------------|----------|
| amount.value | Да | Сумма платежа |
| amount.currency | Да | Валюта (RUB) |
| confirmation.type | Да | Способ подтверждения: `redirect`, `embedded` |
| confirmation.return_url | Для redirect | URL возврата после оплаты |
| capture | Нет | Автоматическое подтверждение (True) или двухстадийная оплата (False) |
| description | Нет | Описание платежа (до 128 символов) |
| metadata | Нет | Произвольные данные (возвратятся в webhook) |
| receipt | Нет | Данные для чека (фискализация) |
## Способы оплаты
ЮKassa поддерживает 20+ способов. Вот основные:
### Банковские карты «Мир»
Самый распространённый способ. Комиссия 2.8-3.5%.
```python
payload = {
"amount": {"value": "1500.00", "currency": "RUB"},
"payment_method_data": {"type": "bank_card"},
"confirmation": {"type": "redirect", "return_url": "..."},
"capture": True
}
```text
### СБП (Система быстрых платежей)
Самый дешёвый способ. Комиссия 0.4-0.7%.
```python
payload = {
"amount": {"value": "1500.00", "currency": "RUB"},
"payment_method_data": {"type": "sbp"},
"confirmation": {"type": "redirect", "return_url": "..."},
"capture": True
}
```text
### ЮMoney
Комиссия 5-6%. Подходит, если у клиента есть кошелёк ЮMoney.
```python
payload = {
"amount": {"value": "1500.00", "currency": "RUB"},
"payment_method_data": {"type": "yoo_money"},
"confirmation": {"type": "redirect", "return_url": "..."},
"capture": True
}
```text
### Apple Pay и Google Pay
Для мобильных приложений и сайтов. Требует дополнительной настройки (merchant ID, сертификаты).
### SberPay
Оплата через приложение Сбера. Комиссия ~2.5%.
### Рекомендация
Показывайте клиенту СБП первым — дешевле для вас, удобно для клиента. Если клиент хочет платить картой — пусть платит. Но мотивируйте СБП: «Оплата через СБП — быстрее и без комиссии для вас».
## Webhooks: обработка уведомлений
### Настройка
В личном кабинете ЮKassa → «Настройки» → «Webhooks». Укажите URL вашего endpoint и выберите события:
- **payment.succeeded** — платёж успешен
- **payment.waiting_for_capture** — платёж ожидает подтверждения (двухстадийная)
- **payment.canceled** — платёж отменён
- **refund.succeeded** — возврат выполнен
### Обработка webhook на Python (Flask)
```python
from flask import Flask, request, jsonify
import hashlib
import hmac
app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret"
def verify_signature(payload, signature):
"""Проверка подписи webhook"""
computed = hmac.new(
WEBHOOK_SECRET.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(computed, signature)
@app.route("/webhooks/yookassa", methods=["POST"])
def yookassa_webhook():
payload = request.get_data(as_text=True)
signature = request.headers.get("Content-Signature", "").replace("sha256=", "")
if not verify_signature(payload, signature):
return "Invalid signature", 403
event = request.get_json()
event_type = event.get("type")
if event_type == "payment.succeeded":
payment = event["object"]
payment_id = payment["id"]
order_id = payment.get("metadata", {}).get("order_id")
amount = payment["amount"]["value"]
# Логика: выдать доступ, отправить email, обновить статус
print(f"Payment {payment_id} for order {order_id}: {amount} RUB")
elif event_type == "payment.canceled":
payment = event["object"]
order_id = payment.get("metadata", {}).get("order_id")
# Логика: отменить заказ
print(f"Payment canceled for order {order_id}")
return jsonify({"status": "ok"}), 200
if __name__ == "__main__":
app.run(port=5000)
```text
### Важные правила webhook-ов
1. **Проверяйте подпись.** Без этого кто угодно может отправить фейковый webhook.
2. **Возвращайте 200 OK быстро.** Если обработка долгая — ставьте в очередь (Celery, RabbitMQ). ЮKassa повторит отправку, если не получит 200.
3. **Обрабатывайте повторения идемпотентно.** ЮKassa может отправить один webhook дважды. Проверяйте по payment_id, не обрабатывайте дважды.
4. **Логируйте все webhook-ы.** На продакшене логируйте payload и результат обработки. Это спасёт при разборе инцидентов.
## Двухстадийная оплата (hold)
Если вы хотите списать деньги только после подтверждения (например, после отгрузки товара):
```python
# Создание с capture=False
payload = {
"amount": {"value": "1500.00", "currency": "RUB"},
"capture": False,
"confirmation": {"type": "redirect", "return_url": "..."}
}
# Подтверждение (после отгрузки)
capture_url = f"https://api.yookassa.ru/v3/payments/{payment_id}/capture"
capture_payload = {"amount": {"value": "1500.00", "currency": "RUB"}}
response = requests.post(capture_url, json=capture_payload, headers=headers)
```text
Если не подтвердить в течение 7 дней — деньги автоматически вернутся клиенту.
## Возвраты
### Полный возврат
```python
refund_url = "https://api.yookassa.ru/v3/refunds"
refund_payload = {
"payment_id": payment_id,
"amount": {"value": "1500.00", "currency": "RUB"}
}
response = requests.post(refund_url, json=refund_payload, headers=headers)
```text
### Частичный возврат
```python
refund_payload = {
"payment_id": payment_id,
"amount": {"value": "500.00", "currency": "RUB"}
}
```text
**Лимиты:** возврат можно сделать в течение 3 лет с момента оплаты. Но чем быстрее — тем лучше для отношений с клиентом.
## Безопасность
### HTTPS
API работает только по HTTPS. На localhost для разработки — нормально. На проде — SSL-сертификат обязателен.
### Проверка подписи webhook
Обязательно. Используйте HMAC-SHA256 с секретом из личного кабинета.
### Idempotence-Key
Используйте UUID для каждого нового платежа. Это защитит от дублей при сетевых ошибках и повторных запросах.
### Хранение данных
Не храните номера карт, CVV и CVC — это запрещено. ЮKassa возвращает только masked.pan (первые 6 и последние 4 цифры). Этого достаточно для идентификации способа оплаты.
### IP-адреса webhook-ов
В настройках webhook можно ограничить отправку по IP. Рекомендую — лишняя защита не помешает.
## Тестирование в sandbox
### Тестовые карты
| Номер карты | Сценарий |
|------------|----------|
| 5555 5555 5555 4444 | Успешная оплата |
| 5555 5555 5555 4443 | Недостаточно средств |
| 5555 5555 5555 4442 | Ошибка банка |
**CVV:** любой трёхзначный (например, `123`)
**Срок:** любая будущая дата (например, `12/28`)
### Тестовый поток
1. Используйте ключи с префиксом `test_`
2. Создайте платеж
3. Перейдите по confirmation_url
4. Оплатите тестовой картой
5. Проверьте webhook на вашем endpoint
6. Проверьте статус платежа в личном кабинете
## Готовые SDK
ЮKassa предоставляет официальные SDK:
- **PHP:** `composer require yoomoney/yookassa-sdk-php`
- **Python:** `pip install yookassa`
- **Node.js:** `npm install yookassa-sdk`
- **Java:** Maven dependency (смотрите в документации)
- **Kotlin:** для Android-приложений
SDK избавляет от ручной работы с HTTP-запросами, подписью и обработкой ошибок. Рекомендую использовать, если нет специфических требований.
### Пример с Python SDK
```python
from yookassa import Configuration, Payment
Configuration.account_id = "123456"
Configuration.secret_key = "test_xxxxxxxxxxxxx"
payment = Payment.create({
"amount": {
"value": "1500.00",
"currency": "RUB"
},
"confirmation": {
"type": "redirect",
"return_url": "https://yoursite.ru/success"
},
"capture": True,
"description": "Заказ #1234"
})
print(payment.confirmation.confirmation_url)
```text
## Распространённые ошибки
### 1. Неверная подпись webhook
**Симптом:** webhook приходит, но подпись не совпадает.
**Решение:** убедитесь, что используете тело запроса как есть (raw bytes), а не распарсенный JSON. Кодировка — UTF-8.
### 2. Idempotence-Key повторяется
**Симптом:** при повторном запросе с тем же ключом ЮKassa возвращает ответ первого запроса (даже если это был другой платёж).
**Решение:** генерируйте новый UUID для каждого платежа. Храните соответствие idempotence_key → payment_id в БД.
### 3. Currency mismatch
**Симптом:** ошибка «Invalid currency».
**Решение:** ЮKassa работает только с RUB. Убедитесь, что amount.currency = "RUB".
### 4. Webhook не доходит
**Симптом:** платёж прошёл, а webhook не пришёл.
**Решение:** проверьте, что ваш endpoint доступен извне (ngrok для локальной разработки), возвращает 200 OK, и URL в настройках ЮKassa правильный.
### 5. Capture timeout
**Симптом:** двухстадийный платёж автоматически отменяется через 7 дней.
**Решение:** ставьте таймер или cron-задачу для подтверждения платежей. Не оставляйте на потом.
## Производительность и лимиты
- **Максимальная сумма одного платежа:** 60 000 000 ₽ (для юрлиц)
- **Минимальная сумма:** 1 ₽
- **Частота запросов:** до 100 RPS (requests per second)
- **Время ожидания ответа API:** обычно 200-500 мс
- **Доступность:** 99.95% SLA
Для среднего бизнеса этих лимитов хватает с огромным запасом. Если вы обрабатываете 1000+ платежей в минуту — обсуждаете индивидуальные условия с ЮKassa.
## Заключение
Интеграция ЮKassa API — несложная задача, если знать подводные камни. Главные правила:
1. **Всегда проверяйте подпись webhook** — безопасность прежде всего
2. **Используйте Idempotence-Key** — защита от дублей
3. **Опирайтесь на webhook, не на redirect** — надёжнее
4. **Тестируйте в sandbox** — бесплатные тестовые карты, реальное API
5. **Используйте SDK** — меньше кода, меньше ошибок
6. **Логируйте всё** — поможет при разборе инцидентов
На интеграцию от нуля до рабочего приёма платежей уходит 1-2 дня. Если нужно быстрее — используйте готовые модули для CMS или SDK.
## Подписки и рекуррентные платежи
Один из самых прибыльных сценариев использования ЮKassa API. Если у вас подписочный сервис (SaaS, контент, клуб) — рекуррентные платежи обязательны.
### Как работают подписки в ЮKassa
1. Создаёте платёж с `save_payment_method: true`
2. При успешной оплате ЮKassa возвращает `payment_method_id`
3. Сохраняете его в БД вместе с датой следующего списания
4. По cron-задаче создаёте платёж с `payment_method_id` — без участия клиента
```python
# Первый платёж с сохранением способа
payload = {
"amount": {"value": "990.00", "currency": "RUB"},
"save_payment_method": True,
"confirmation": {"type": "redirect", "return_url": "..."},
"capture": True
}
# В ответе будет:
# payment["payment_method"]["id"] = "pm_xxxxx"
# payment["payment_method"]["saved"] = True
# Рекуррентный платёж (без участия клиента)
recurring_payload = {
"amount": {"value": "990.00", "currency": "RUB"},
"payment_method_id": "pm_xxxxx",
"capture": True
}
```text
### Что делать при неудачном списании
Карта могла истечь, не хватить денег, банк заблокировал. План:
1. Первый отказ — повтор через 24 часа
2. Второй отказ — повтор через 3 дня + уведомление клиенту «Обновите платёжные данные»
3. Третий отказ — приостановка подписки + уведомление
ЮKassa отправляет webhook `payment.canceled` с причиной отказа. Логируйте причину и показывайте клиенту понятное сообщение.
### Тарифы на подписки
ЮKassa не берёт дополнительную комиссию за рекуррентные платежи. Те же 2.8-3.5% за карты, 0.4-0.7% за СБП.
## ЮKassa для маркетплейсов
Если вы строите маркетплейс — ЮKassa имеет специальное решение с расщеплением платежей.
### Как работает расщепление
1. Покупатель платит 1 000 ₽
2. ЮKassa автоматически: 50 ₽ → вам (комиссия площадки), 900 ₽ → селлеру, 50 ₽ → НДС
3. Каждый участник получает свой чек
4. Выплата селлеру — автоматически по расписанию
Для этого используется объект `deal` (сделка) вместо обычного платежа. API сложнее, но документация подробная.
### Когда нужен маркетплейс-режим
- У вас есть селлеры/подрядчики, которым нужно выплачивать деньги
- Вы — налоговый агент по НДС
- Нужно расщеплять платеж между несколькими получателями
Если вы просто продаёте свои товары — обычного API достаточно.
---
Нужна помощь с интеграцией ЮKassa или другой платёжной системы в ваш проект? **Flow Masters** внедряет платёжные решения, чат-боты и автоматизацию для бизнеса в России. [Заявка на flow-masters.ru](https://flow-masters.ru) — обсудим задачу и предложим решение.