Initial commit: YooKassa mock v2
This commit is contained in:
418
README.md
Normal file
418
README.md
Normal file
@@ -0,0 +1,418 @@
|
||||
# YooKassa Mock v2
|
||||
|
||||
Легковесный mock-сервер для эмуляции базового сценария работы YooKassa.
|
||||
|
||||
## Что умеет
|
||||
|
||||
- создавать платеж
|
||||
- возвращать `confirmation_url`
|
||||
- показывать checkout-страницу
|
||||
- завершать платеж как:
|
||||
- `succeeded`
|
||||
- `canceled`
|
||||
- отправлять webhook
|
||||
- повторять webhook при ошибке
|
||||
- поддерживать `Idempotence-Key`
|
||||
- отдавать статус платежа по `payment_id`
|
||||
|
||||
---
|
||||
|
||||
# Файлы
|
||||
|
||||
- `main.py` — FastAPI приложение
|
||||
- `requirements.txt` — зависимости
|
||||
- `Dockerfile` — образ
|
||||
- `docker-compose.yml` — запуск контейнера
|
||||
- `.env` — переменные окружения
|
||||
|
||||
---
|
||||
|
||||
# Запуск
|
||||
|
||||
## 1. Сборка и старт
|
||||
|
||||
~~~bash
|
||||
docker compose up -d --build
|
||||
~~~
|
||||
|
||||
## 2. Проверка здоровья
|
||||
|
||||
~~~bash
|
||||
curl -s http://127.0.0.1:${YMK_PUBLIC_PORT}/health
|
||||
~~~
|
||||
|
||||
Пример ответа:
|
||||
|
||||
~~~json
|
||||
{"status":"ok","payments_total":0,"idempotence_keys_total":0}
|
||||
~~~
|
||||
|
||||
---
|
||||
|
||||
# Переменные окружения
|
||||
|
||||
Пример `.env`:
|
||||
|
||||
~~~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_url`
|
||||
- `amount.value`
|
||||
|
||||
### Пример запроса
|
||||
|
||||
~~~bash
|
||||
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"
|
||||
}'
|
||||
~~~
|
||||
|
||||
### Пример успешного ответа
|
||||
|
||||
~~~json
|
||||
{
|
||||
"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}`
|
||||
|
||||
Получение статуса платежа.
|
||||
|
||||
### Пример
|
||||
|
||||
~~~bash
|
||||
curl -s http://127.0.0.1:8083/v3/payments/fdb332f8-bb1d-4def-bc1d-d5b00a7e287a
|
||||
~~~
|
||||
|
||||
### Пример ответа
|
||||
|
||||
~~~json
|
||||
{
|
||||
"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`:
|
||||
|
||||
- `success`
|
||||
- `cancel`
|
||||
- `fail`
|
||||
|
||||
### Пример success
|
||||
|
||||
~~~bash
|
||||
curl -i -X POST http://127.0.0.1:8083/process/fdb332f8-bb1d-4def-bc1d-d5b00a7e287a \
|
||||
-d 'action=success'
|
||||
~~~
|
||||
|
||||
### Пример cancel
|
||||
|
||||
~~~bash
|
||||
curl -i -X POST http://127.0.0.1:8083/process/294ec8cd-c1fb-44b4-91cc-04d669343390 \
|
||||
-d 'action=cancel'
|
||||
~~~
|
||||
|
||||
### Поведение
|
||||
|
||||
- `success` → статус `succeeded`, `paid=true`
|
||||
- `cancel` → статус `canceled`, `paid=false`
|
||||
- `fail` → статус `canceled`, `paid=false`
|
||||
|
||||
После этого сервис делает:
|
||||
|
||||
- webhook на `WEBHOOK_URL`
|
||||
- `303 See Other` на `return_url`
|
||||
|
||||
---
|
||||
|
||||
## 5. GET `/health`
|
||||
|
||||
Проверка состояния сервиса.
|
||||
|
||||
### Пример
|
||||
|
||||
~~~bash
|
||||
curl -s http://127.0.0.1:8083/health
|
||||
~~~
|
||||
|
||||
### Ответ
|
||||
|
||||
~~~json
|
||||
{
|
||||
"status": "ok",
|
||||
"payments_total": 2,
|
||||
"idempotence_keys_total": 2
|
||||
}
|
||||
~~~
|
||||
|
||||
---
|
||||
|
||||
# Webhook
|
||||
|
||||
После `success`:
|
||||
|
||||
~~~json
|
||||
{
|
||||
"event": "payment.succeeded",
|
||||
"type": "notification",
|
||||
"object": {
|
||||
"id": "payment_id",
|
||||
"status": "succeeded",
|
||||
"paid": true,
|
||||
"amount": {
|
||||
"value": "100.00",
|
||||
"currency": "RUB"
|
||||
},
|
||||
"description": "Заказ №1",
|
||||
"metadata": {}
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
После `cancel` / `fail`:
|
||||
|
||||
~~~json
|
||||
{
|
||||
"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`
|
||||
|
||||
Логи смотреть так:
|
||||
|
||||
~~~bash
|
||||
docker logs --tail 100 yookassa-mock
|
||||
~~~
|
||||
|
||||
---
|
||||
|
||||
# Сценарии через metadata
|
||||
|
||||
При создании платежа можно задать сценарий по умолчанию:
|
||||
|
||||
~~~json
|
||||
"metadata": {
|
||||
"mock_scenario": "cancel"
|
||||
}
|
||||
~~~
|
||||
|
||||
Допустимые значения:
|
||||
- `success`
|
||||
- `cancel`
|
||||
- `fail`
|
||||
|
||||
Если в `POST /process/{payment_id}` не передать `action`, будет использован `mock_scenario`.
|
||||
|
||||
---
|
||||
|
||||
# Basic Auth
|
||||
|
||||
Если включить:
|
||||
|
||||
~~~env
|
||||
MOCK_REQUIRE_AUTH=1
|
||||
~~~
|
||||
|
||||
Тогда endpoints `/v3/payments` и `/v3/payments/{payment_id}` будут требовать Basic Auth.
|
||||
|
||||
Логин/пароль берутся из:
|
||||
- `MOCK_SHOP_ID`
|
||||
- `MOCK_SECRET_KEY`
|
||||
|
||||
---
|
||||
|
||||
# Ограничения
|
||||
|
||||
Это мок, не полная копия YooKassa.
|
||||
|
||||
Сейчас нет:
|
||||
- persistent storage
|
||||
- списка платежей
|
||||
- ручного cancel endpoint в стиле реального API
|
||||
- полной схемы YooKassa
|
||||
- реальной проверки секретов YooKassa
|
||||
- capture/authorize flow с отдельным подтверждением списания
|
||||
- рефандов
|
||||
|
||||
---
|
||||
|
||||
# Текущее состояние проверки
|
||||
|
||||
Проверено вручную:
|
||||
|
||||
- создание платежа — OK
|
||||
- `confirmation_url` — OK
|
||||
- `GET /v3/payments/{id}` — OK
|
||||
- идемпотентность — OK
|
||||
- `pending -> succeeded` — OK
|
||||
- `pending -> canceled` — OK
|
||||
- redirect на `return_url` — OK
|
||||
- retry webhook — OK
|
||||
|
||||
Проблема сейчас только одна:
|
||||
`WEBHOOK_URL` отвечает `404 Not Found`, значит endpoint на основном backend отсутствует или указан неверно.
|
||||
Reference in New Issue
Block a user