import os from uuid import uuid4 from typing import Dict, Any import httpx from fastapi import FastAPI, HTTPException, Request from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse from jinja2 import Template app = FastAPI(title="YooKassa Mock") payments_db: Dict[str, Dict[str, Any]] = {} WEBHOOK_URL = os.getenv("WEBHOOK_URL", "").strip() MOCK_HOST = os.getenv("MOCK_HOST", "").strip() MOCK_PORT = int(os.getenv("MOCK_PORT", "8081")) CHECKOUT_TEMPLATE = Template(""" Оплата заказа

Тестовая оплата

Сумма к оплате:
{{ amount }} {{ currency }}
payment_id: {{ payment_id }}
""") def build_base_url(request: Request) -> str: if MOCK_HOST: return f"http://{MOCK_HOST}:{MOCK_PORT}" return str(request.base_url).rstrip("/") @app.post("/v3/payments") async def create_payment(request: Request): payload = await request.json() payment_id = str(uuid4()) amount_data = payload.get("amount") or {} confirmation_data = payload.get("confirmation") or {} value = amount_data.get("value", "0.00") currency = amount_data.get("currency", "RUB") return_url = confirmation_data.get("return_url") if not return_url: raise HTTPException(status_code=400, detail="confirmation.return_url is required") payments_db[payment_id] = { "return_url": return_url, "amount": value, "currency": currency, } base_url = build_base_url(request) confirmation_url = f"{base_url}/checkout?payment_id={payment_id}" response = { "id": payment_id, "status": "pending", "paid": False, "amount": { "value": value, "currency": currency, }, "confirmation": { "type": "redirect", "confirmation_url": confirmation_url, }, "test": True, } return JSONResponse(content=response) @app.get("/checkout", response_class=HTMLResponse) async def checkout(payment_id: str): payment = payments_db.get(payment_id) if not payment: raise HTTPException(status_code=404, detail="payment not found") html = CHECKOUT_TEMPLATE.render( payment_id=payment_id, amount=payment["amount"], currency=payment["currency"], ) return HTMLResponse(content=html) @app.post("/process/{payment_id}") async def process_payment(payment_id: str): payment = payments_db.get(payment_id) if not payment: raise HTTPException(status_code=404, detail="payment not found") if WEBHOOK_URL: webhook_payload = { "event": "payment.succeeded", "type": "notification", "object": { "id": payment_id, "status": "succeeded", }, } try: async with httpx.AsyncClient(timeout=10.0) as client: await client.post(WEBHOOK_URL, json=webhook_payload) except Exception as e: print(f"Webhook send failed: {e}") return RedirectResponse(url=payment["return_url"], status_code=303) @app.get("/health") async def health(): return {"status": "ok"}