"""PaymentProvider interface and NoopPaymentProvider implementation. docs/planning/economy.md US-2 (task 575fd14b5360). """ from __future__ import annotations from dataclasses import dataclass, field from datetime import datetime, timezone from typing import Protocol import uuid ALLOWED_CREDIT_PACKS: set[int] = {100, 500, 1000, 5000} @dataclass class PurchaseSession: session_id: str user_id: int credits: int provider_ref: str status: str = "created" # created, confirmed, failed created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc)) @dataclass class Confirmation: confirmed: bool provider_ref: str credits: int user_id: int class PaymentProvider(Protocol): """Pluggable payment provider protocol (e.g. Stripe, PayPal, Noop).""" async def create_purchase( self, user_id: int, credits: int, idempotency_key: str | None = None ) -> PurchaseSession: ... async def confirm(self, session_id: str) -> Confirmation: ... class NoopPaymentProvider: """Deterministic, no-op payment provider for local development and testing. Returns confirmed=True with a simulated provider reference. Clearly non-production. """ def __init__(self) -> None: self._sessions: dict[str, PurchaseSession] = {} async def create_purchase( self, user_id: int, credits: int, idempotency_key: str | None = None ) -> PurchaseSession: session_id = f"sess_{uuid.uuid4().hex[:16]}" provider_ref = f"noop_tx_{uuid.uuid4().hex[:12]}" sess = PurchaseSession( session_id=session_id, user_id=user_id, credits=credits, provider_ref=provider_ref, status="created", ) self._sessions[session_id] = sess return sess async def confirm(self, session_id: str) -> Confirmation: sess = self._sessions.get(session_id) if sess is None: return Confirmation( confirmed=False, provider_ref="", credits=0, user_id=0, ) sess.status = "confirmed" return Confirmation( confirmed=True, provider_ref=sess.provider_ref, credits=sess.credits, user_id=sess.user_id, ) payment_provider: PaymentProvider = NoopPaymentProvider()