feat(marketplace): purchase service with atomic escrow, 30/70 fee splitting and refunds

This commit is contained in:
2026-09-15 20:41:07 +00:00
parent 46cd5d6c95
commit 5e1a3bb16e
4 changed files with 674 additions and 8 deletions
+16 -7
View File
@@ -78,6 +78,7 @@ class WalletService:
reference_id: str | None = None,
idempotency_key: str | None = None,
metadata: dict[str, Any] | None = None,
commit: bool = True,
) -> tuple[Wallet, Transaction]:
"""Credit a user's wallet atomically with row locking.
@@ -113,9 +114,12 @@ class WalletService:
metadata_json=metadata,
)
db.add(tx)
await db.commit()
await db.refresh(wallet)
await db.refresh(tx)
if commit:
await db.commit()
await db.refresh(wallet)
await db.refresh(tx)
else:
await db.flush()
return wallet, tx
@classmethod
@@ -129,6 +133,7 @@ class WalletService:
reference_id: str | None = None,
idempotency_key: str | None = None,
metadata: dict[str, Any] | None = None,
commit: bool = True,
) -> tuple[Wallet, Transaction]:
"""Debit a user's wallet atomically with row locking and balance check.
@@ -154,7 +159,8 @@ class WalletService:
wallet = await cls.get_or_create_wallet(db, user_id, for_update=True)
cur_balance = wallet.balance
if cur_balance < amount:
await db.rollback()
if commit:
await db.rollback()
raise InsufficientFundsError(
f"Insufficient funds: available {cur_balance}, required {amount}"
)
@@ -172,9 +178,12 @@ class WalletService:
metadata_json=metadata,
)
db.add(tx)
await db.commit()
await db.refresh(wallet)
await db.refresh(tx)
if commit:
await db.commit()
await db.refresh(wallet)
await db.refresh(tx)
else:
await db.flush()
return wallet, tx
@staticmethod