v0.1.0-dev: initial public extraction + new abstractions
This commit is contained in:
commit
e38120cf11
17 changed files with 2429 additions and 0 deletions
69
cardano_checkout/store.py
Normal file
69
cardano_checkout/store.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
"""Persistence abstraction for Invoice objects.
|
||||
|
||||
The SDK does not prescribe a database. Consumers implement
|
||||
:class:`InvoiceStore` against whatever backend suits them — SQLAlchemy
|
||||
(the host app pattern), SQLite (example-studio pattern), Postgres raw
|
||||
(high-volume pattern), in-memory dict (tests).
|
||||
|
||||
All methods are async so the same Protocol works cleanly for both
|
||||
asyncpg/asyncio-sqlalchemy backends and synchronous backends wrapped
|
||||
with ``asyncio.to_thread``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, Protocol, runtime_checkable
|
||||
|
||||
from cardano_checkout.invoice import Invoice, InvoiceStatus
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class InvoiceStore(Protocol):
|
||||
"""Persistence backend for invoices.
|
||||
|
||||
Consumers implement the six methods below. The SDK's monitor + scheduler
|
||||
modules operate entirely through this interface, never touching a specific
|
||||
ORM or driver.
|
||||
"""
|
||||
|
||||
async def create(self, invoice: Invoice) -> None:
|
||||
"""Insert a new invoice. Should raise if an invoice with the same id exists."""
|
||||
...
|
||||
|
||||
async def get(self, invoice_id: str) -> Optional[Invoice]:
|
||||
"""Fetch one invoice by id. Returns None if not found."""
|
||||
...
|
||||
|
||||
async def list_by_status(
|
||||
self, status: InvoiceStatus, limit: int = 100
|
||||
) -> list[Invoice]:
|
||||
"""List invoices in a given state, newest-first. Used by the monitor poll loop."""
|
||||
...
|
||||
|
||||
async def update(self, invoice: Invoice) -> None:
|
||||
"""Persist the current state of an invoice.
|
||||
|
||||
Implementations should compare-and-set on ``invoice.id`` — if the row
|
||||
doesn't exist the call should raise. Does NOT create; see :meth:`create`.
|
||||
"""
|
||||
...
|
||||
|
||||
async def next_derivation_index(self, merchant_id: str) -> int:
|
||||
"""Return the next unused receive-address index for a merchant.
|
||||
|
||||
Should be transactionally safe against concurrent invoice creation;
|
||||
consumers typically implement this via ``SELECT COALESCE(MAX(index), -1) + 1 ... FOR UPDATE``
|
||||
or an atomic counter row.
|
||||
"""
|
||||
...
|
||||
|
||||
async def record_tx(
|
||||
self, invoice_id: str, tx_hash: str, lovelace_delta: int
|
||||
) -> None:
|
||||
"""Record an observed inbound UTxO against an invoice.
|
||||
|
||||
Must be idempotent on (invoice_id, tx_hash) — monitor loops will
|
||||
re-observe the same UTxO until the invoice transitions to a terminal
|
||||
state.
|
||||
"""
|
||||
...
|
||||
Loading…
Add table
Add a link
Reference in a new issue