Skip to content

fix(monitor): watch multi-seller wallets with per-account cursors and postgres leader lease (#582) - #591

Open
mumusama75 wants to merge 1 commit into
Quittance-Labs:mainfrom
mumusama75:fix/582-multi-seller-payment-monitor
Open

mumusama75 wants to merge 1 commit into
Quittance-Labs:mainfrom
mumusama75:fix/582-multi-seller-payment-monitor

Conversation

@mumusama75

Copy link
Copy Markdown

Resolves #582

Context & Summary

Previously, PaymentMonitorService only polled a single seller account (SELLER_PUBLIC_KEY). In wallet-scoped deployments without SELLER_PUBLIC_KEY, the payment monitor remained disabled, requiring explicit verify calls to move invoices to PAID. Furthermore, server-dual.ts wired a FilePaymentMonitorCheckpointStore even in Postgres mode, �alidateStellarConfig demanded SELLER_SECRET_KEY for watch-only monitoring, and POST /api/payment/sync was unauthenticated.

This PR implements multi-seller wallet monitoring with durable per-account cursors, fair round-robin budgeting, creation-aligned cursor bootstrapping, PostgreSQL advisory lock leader leasing across replicas, operator token protection, and 100% test coverage.

Key Changes

  1. Dynamic Multi-Account Derivation & Lifecycle:
    • Dynamically derives the set of monitored accounts by grouping active watched invoices by sellerPublicKey.
    • Supports SELLER_PUBLIC_KEY as an optional pinned account for single-seller deployments.
    • When an account transitions to idle (no active watches), its cursor is kept in durable checkpoints and state snapshots so future watches resume without rescan.
  2. Creation-Aligned Cursor Bootstrapping:
    • When encountering a new account without an existing checkpoint, bootstraps the cursor at or before the earliest watched invoice's createdAt timestamp (via getCursorAtOrBefore in stellar.service.ts or backward pagination), preventing payments that landed between invoice creation and the first poll from being skipped.
  3. Round-Robin Budgeting & Error Isolation:
    • Round-robins active accounts per tick under the shared maxPagesPerRun budget.
    • Isolates Horizon rate-limit/connection errors per account with exponential backoff (monitorBackoffMs), ensuring a failing account delays other accounts by at most one tick.
  4. PostgreSQL Advisory Lock Leader Lease:
    • Implemented PostgresMonitorLeaderLease in �ackend/src/services/monitor-leader-lease.ts using session-level advisory locks (pg_try_advisory_lock(742918452)) on a dedicated PostgreSQL connection.
    • Only the elected leader polls Horizon; standby replicas continue serving HTTP traffic. On leader stop/disconnect, a standby replica seamlessly acquires the lease within the lease window with zero double-settle risk.
    • In-memory mode runs SingleInstanceLeaderLease and reports singleInstance: true and leaderElection: 'single-instance' in readiness probes.
  5. Postgres Checkpoint Store & Watch-Only Config:
    • server-dual.ts in Postgres mode now properly wires PostgresPaymentMonitorCheckpointStore(pool).
    • �alidateStellarConfig no longer requires SELLER_SECRET_KEY for watch-only monitoring.
    • server-dual.ts, server-mvp.ts, and server.ts all start the payment monitor in both pinned single-seller and dynamic multi-seller modes.
  6. Operator Token Protection:
    • POST /api/payment/sync requires operator authorization via Authorization: Bearer or X-Operator-Token: .
  7. Comprehensive Verification:
    • Created �ackend/tests/payment-monitor-multi-account.test.ts covering all criteria.
    • Updated �ackend/tests/server-mvp-payment-monitor.test.ts to assert 401 on unauthenticated sync and 200 with operator token.
    • All 903 backend tests pass; typecheck is 100% clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Watch every seller wallet with pending invoices from one monitor, with per-account cursors and one leader across replicas

1 participant