feat(services,tasks): order placement, settlement, invoice listener
services.py - place_order: validates against live menu, prices line items authoritatively from DB (modifier ids resolved server-side, not trusted from input), creates LNbits invoice, persists order + items. Order id := payment_hash for zero-metadata listener lookups. - mark_order_paid: idempotent paid -> [accepted if auto-accept] + stock decrement + queues a print job. - transition_order: explicit state-machine guard for accept/ready/ complete/cancel/refund. - quote_balance_required: pre-flight total for the webapp's multi-restaurant balance check (per the user's requirement to verify funds before opening any per-restaurant invoice). tasks.py - Single invoice listener filtered on extra.tag == 'restaurant', looks up order by payment_hash, delegates to mark_order_paid. Wrapped in try/except so one bad payment doesn't kill the loop.
This commit is contained in:
parent
5f4b416f5f
commit
201c387722
2 changed files with 387 additions and 0 deletions
46
tasks.py
Normal file
46
tasks.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
"""
|
||||
Background tasks.
|
||||
|
||||
The invoice listener is the *only* place where money-moves trigger
|
||||
business logic. We keep it small and idempotent: filter by
|
||||
extra.tag == 'restaurant', look up the order by payment_hash, and
|
||||
hand off to services.mark_order_paid().
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.core.models import Payment
|
||||
from lnbits.tasks import register_invoice_listener
|
||||
|
||||
from .crud import get_order_by_payment_hash
|
||||
from .services import mark_order_paid
|
||||
|
||||
|
||||
async def wait_for_paid_invoices() -> None:
|
||||
invoice_queue: asyncio.Queue = asyncio.Queue()
|
||||
register_invoice_listener(invoice_queue, "ext_restaurant")
|
||||
|
||||
while True:
|
||||
payment = await invoice_queue.get()
|
||||
try:
|
||||
await on_invoice_paid(payment)
|
||||
except Exception as ex:
|
||||
logger.exception(f"[RESTAURANT] invoice listener error: {ex}")
|
||||
|
||||
|
||||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
if not payment.extra or payment.extra.get("tag") != "restaurant":
|
||||
return
|
||||
|
||||
order = await get_order_by_payment_hash(payment.payment_hash)
|
||||
if not order:
|
||||
# Could be an order created on a different LNbits instance, or
|
||||
# a payment whose order row was already deleted. Nothing to do.
|
||||
logger.debug(
|
||||
f"[RESTAURANT] No order for payment {payment.payment_hash[:12]}.."
|
||||
)
|
||||
return
|
||||
|
||||
await mark_order_paid(order.id)
|
||||
Loading…
Add table
Add a link
Reference in a new issue