90 lines
2.4 KiB
Python
90 lines
2.4 KiB
Python
"""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()
|