Skip to content

alexanderchainsaw/payment_service

Repository files navigation

Payment Service

Асинхронный сервис приёма платежей. Принимает запрос, кладёт событие через outbox, обрабатывает его в отдельном consumer'е (эмуляция платёжного шлюза), обновляет статус и шлёт webhook. Сообщения, не обработанные за 3 попытки, уходят в DLQ.

Стек

FastAPI · SQLAlchemy 2.0 async + PostgreSQL · Alembic · RabbitMQ (FastStream) · dishka (DI) · Docker.

Как устроено

POST /payments пишет платёж и outbox-событие в одной транзакции. Фоновый relay вычитывает outbox (FOR UPDATE SKIP LOCKED) и публикует событие в payments.new. Consumer обрабатывает платёж и шлёт webhook.

  • Создание идемпотентно по заголовку Idempotency-Key.
  • Вызов шлюза идемпотентен по id платежа - повтор сообщения не спишет дважды.
  • Шлюз различает declined (финальный отказ -> failed) и временный сбой (исход неизвестен -> повтор, статус остаётся pending, после 3 попыток DLQ).
  • Webhook шлётся с ретраями (1s, 2s, 4s).

Тело webhook:

{"payment_id": "...", "status": "succeeded", "amount": "199.90",
 "currency": "RUB", "processed_at": "..."}

Запуск

make up      # поднять стек, миграции применяются сами
make logs
make down    # остановить и снести тома

Наружу открыт только API на :8080, доки - http://localhost:8080/docs.

API

Все ручки требуют заголовок X-API-Key (по умолчанию local-dev-api-key).

# создать
curl -i -X POST http://localhost:8080/api/v1/payments \
  -H "X-API-Key: local-dev-api-key" \
  -H "Idempotency-Key: 11111111-1111-1111-1111-111111111111" \
  -H "Content-Type: application/json" \
  -d '{"amount": "199.90", "currency": "RUB", "webhook_url": "https://webhook.site/<id>"}'

# получить
curl http://localhost:8080/api/v1/payments/<payment_id> -H "X-API-Key: local-dev-api-key"

Проверка сценариев

Поведение шлюза и авторизации задаётся в deploy/dev.yml. После правки - make up (образ пересоберётся). Те же значения переопределяются через env, напр. GATEWAY__ERROR_RATE=1 (удобно при локальном запуске без Docker).

Сценарий Настройка
Успешная оплата по умолчанию (gateway.success_rate: 0.9)
Отказ (declined) gateway.success_rate: 0
Временный сбой -> DLQ gateway.error_rate: 1
Сбой webhook -> DLQ webhook_url: https://httpbin.org/status/500
Идемпотентность повторить запрос с тем же Idempotency-Key
Доставка webhook webhook_url на https://webhook.site
Без авторизации security.enabled: false

Чтобы видеть очереди и DLQ, добавьте сервису rabbitmq в deploy/docker-compose.yml ports: ["15672:15672"] и откройте http://localhost:15672 (guest/guest).

Локальная разработка

make install
make migrate     # нужен запущенный PostgreSQL
make api
make consumer
make test        # pytest; PostgreSQL/RabbitMQ не нужны - инфраструктура in-memory
make lint        # ruff + isort + black

Строки подключения переопределяются через DATABASE__DSN, RABBIT__URL

About

Микросервис-прослойка для приёма платежей и надёжной отправки во внешний шлюз.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors