8.8 KiB
YooKassa Mock v2
Легковесный mock-сервер для эмуляции базового сценария работы YooKassa.
Что умеет
- создавать платеж
- возвращать
confirmation_url - показывать checkout-страницу
- завершать платеж как:
succeededcanceled
- отправлять webhook
- повторять webhook при ошибке
- поддерживать
Idempotence-Key - отдавать статус платежа по
payment_id
Файлы
main.py— FastAPI приложениеrequirements.txt— зависимостиDockerfile— образdocker-compose.yml— запуск контейнера.env— переменные окружения
Запуск
1. Сборка и старт
docker compose up -d --build
2. Проверка здоровья
curl -s http://127.0.0.1:${YMK_PUBLIC_PORT}/health
Пример ответа:
{"status":"ok","payments_total":0,"idempotence_keys_total":0}
Переменные окружения
Пример .env:
YMK_PUBLIC_PORT=8083
WEBHOOK_URL=http://192.168.149.101:8000/api/tickets/webhook/yookassa
MOCK_HOST=192.168.149.101
MOCK_PORT=8083
MOCK_REQUIRE_AUTH=0
MOCK_SHOP_ID=test_shop
MOCK_SECRET_KEY=test_secret
WEBHOOK_RETRY_COUNT=3
WEBHOOK_RETRY_DELAY_SEC=1
WEBHOOK_DELAY_SEC=0
Описание
YMK_PUBLIC_PORT
Внешний порт хоста, на который публикуется контейнер.
WEBHOOK_URL
URL для отправки webhook после смены статуса платежа.
MOCK_HOST
Хост, который будет подставляться в confirmation_url.
MOCK_PORT
Порт, который будет подставляться в confirmation_url.
MOCK_REQUIRE_AUTH
0— Basic Auth не обязателен1— Basic Auth обязателен
MOCK_SHOP_ID
Логин для Basic Auth, если auth включен.
MOCK_SECRET_KEY
Пароль для Basic Auth, если auth включен.
WEBHOOK_RETRY_COUNT
Количество попыток отправки webhook.
WEBHOOK_RETRY_DELAY_SEC
Пауза между retry webhook.
WEBHOOK_DELAY_SEC
Искусственная задержка перед первой отправкой webhook.
API
1. POST /v3/payments
Создание платежа.
Обязательное
- заголовок
Idempotence-Key confirmation.return_urlamount.value
Пример запроса
curl -s -X POST http://127.0.0.1:8083/v3/payments \
-H 'Content-Type: application/json' \
-H 'Idempotence-Key: test-001' \
-d '{
"amount": {
"value": "100.00",
"currency": "RUB"
},
"confirmation": {
"type": "redirect",
"return_url": "http://192.168.149.101:3000/payment/return"
},
"capture": true,
"description": "Заказ №1"
}'
Пример успешного ответа
{
"id": "fdb332f8-bb1d-4def-bc1d-d5b00a7e287a",
"status": "pending",
"paid": false,
"amount": {
"value": "100.00",
"currency": "RUB"
},
"confirmation": {
"type": "redirect",
"confirmation_url": "http://192.168.149.101:8083/checkout?payment_id=fdb332f8-bb1d-4def-bc1d-d5b00a7e287a"
},
"created_at": "2026-03-12T06:35:47.512Z",
"description": "Заказ №1",
"metadata": {},
"capture": true,
"recipient": {
"account_id": "test_shop",
"gateway_id": "100700"
},
"refundable": false,
"test": true
}
Идемпотентность
Если повторно вызвать POST /v3/payments с тем же Idempotence-Key, мок вернет тот же платеж, а не создаст новый.
2. GET /v3/payments/{payment_id}
Получение статуса платежа.
Пример
curl -s http://127.0.0.1:8083/v3/payments/fdb332f8-bb1d-4def-bc1d-d5b00a7e287a
Пример ответа
{
"id": "fdb332f8-bb1d-4def-bc1d-d5b00a7e287a",
"status": "succeeded",
"paid": true,
"amount": {
"value": "100.00",
"currency": "RUB"
},
"confirmation": {
"type": "redirect",
"confirmation_url": "http://192.168.149.101:8083/checkout?payment_id=fdb332f8-bb1d-4def-bc1d-d5b00a7e287a"
},
"created_at": "2026-03-12T06:35:47.512Z",
"description": "Заказ №1",
"metadata": {},
"capture": true,
"recipient": {
"account_id": "test_shop",
"gateway_id": "100700"
},
"refundable": true,
"test": true
}
3. GET /checkout?payment_id=<id>
HTML-страница оплаты.
Что показывает:
payment_id- статус
- сценарий
- описание
- сумму
На странице есть три кнопки:
Оплатить успешноОтменить оплатуОшибка оплаты
4. POST /process/{payment_id}
Меняет статус платежа, отправляет webhook, делает redirect на return_url.
Входные варианты
Через form-data / x-www-form-urlencoded поле action:
successcancelfail
Пример success
curl -i -X POST http://127.0.0.1:8083/process/fdb332f8-bb1d-4def-bc1d-d5b00a7e287a \
-d 'action=success'
Пример cancel
curl -i -X POST http://127.0.0.1:8083/process/294ec8cd-c1fb-44b4-91cc-04d669343390 \
-d 'action=cancel'
Поведение
success→ статусsucceeded,paid=truecancel→ статусcanceled,paid=falsefail→ статусcanceled,paid=false
После этого сервис делает:
- webhook на
WEBHOOK_URL 303 See Otherнаreturn_url
5. GET /health
Проверка состояния сервиса.
Пример
curl -s http://127.0.0.1:8083/health
Ответ
{
"status": "ok",
"payments_total": 2,
"idempotence_keys_total": 2
}
Webhook
После success:
{
"event": "payment.succeeded",
"type": "notification",
"object": {
"id": "payment_id",
"status": "succeeded",
"paid": true,
"amount": {
"value": "100.00",
"currency": "RUB"
},
"description": "Заказ №1",
"metadata": {}
}
}
После cancel / fail:
{
"event": "payment.canceled",
"type": "notification",
"object": {
"id": "payment_id",
"status": "canceled",
"paid": false,
"amount": {
"value": "250.00",
"currency": "RUB"
},
"description": "Заказ №2",
"metadata": {
"mock_scenario": "cancel"
}
}
}
Retry webhook
Если endpoint webhook отвечает ошибкой или недоступен:
- мок делает повторные попытки
- количество попыток задается
WEBHOOK_RETRY_COUNT - задержка между попытками задается
WEBHOOK_RETRY_DELAY_SEC
Логи смотреть так:
docker logs --tail 100 yookassa-mock
Сценарии через metadata
При создании платежа можно задать сценарий по умолчанию:
"metadata": {
"mock_scenario": "cancel"
}
Допустимые значения:
successcancelfail
Если в POST /process/{payment_id} не передать action, будет использован mock_scenario.
Basic Auth
Если включить:
MOCK_REQUIRE_AUTH=1
Тогда endpoints /v3/payments и /v3/payments/{payment_id} будут требовать Basic Auth.
Логин/пароль берутся из:
MOCK_SHOP_IDMOCK_SECRET_KEY
Ограничения
Это мок, не полная копия YooKassa.
Сейчас нет:
- persistent storage
- списка платежей
- ручного cancel endpoint в стиле реального API
- полной схемы YooKassa
- реальной проверки секретов YooKassa
- capture/authorize flow с отдельным подтверждением списания
- рефандов
Текущее состояние проверки
Проверено вручную:
- создание платежа — OK
confirmation_url— OKGET /v3/payments/{id}— OK- идемпотентность — OK
pending -> succeeded— OKpending -> canceled— OK- redirect на
return_url— OK - retry webhook — OK
Проблема сейчас только одна:
WEBHOOK_URL отвечает 404 Not Found, значит endpoint на основном backend отсутствует или указан неверно.