Skip to content

[BUG] Atomic Payment Processing with Conflict Resolution #9

Description

@oomokaro1

[BUG] Atomic Payment Processing with Conflict Resolution

Priority: High

Difficulty: Hard
Estimated Effort: 2-3 days
Relevant Packages: OrbitStream_backend/
Labels: bug, enhancement, data-integrity, priority:high

Requirements

1. Transactional Processing

  • Wrap the entire processPayment function body in a Drizzle transaction
  • Use db.transaction(async (tx) => { ... }) for atomic session update + payment insert
  • If either operation fails, both are rolled back

2. Optimistic Locking

  • Before updating the session, verify status = 'pending' within the same transaction
  • Use SELECT ... FOR UPDATE (pessimistic) or check affected row count (optimistic)
  • If the session is no longer pending (another payment got there first), skip this payment and log a warning

3. Idempotency Key

  • Add a payment_idempotency_key column to the payments table: UNIQUE(tx_hash, session_id)
  • Before inserting, check if a payment with the same idempotency key already exists
  • If it exists, skip the insert (idempotent operation)
  • Run a Drizzle migration to add the unique constraint

4. Processing Status

  • Add a processing status to the sessionStatusEnum
  • When a payment is detected: set status to processing first, then to paid after payment record is inserted
  • This prevents concurrent payments for the same session from both succeeding

5. Recovery Job

  • Implement a periodic job (every 5 minutes) that:
    • Finds sessions stuck in processing status for > 5 minutes
    • Checks Horizon to see if the payment actually confirmed
    • If confirmed: insert the missing payment record, set status to paid
    • If not confirmed: set status back to pending (allow re-detection)
    • Log all recovery actions

6. Duplicate Payment Handling

  • If a payment arrives for an already-paid session:
    • Log a warning with the session ID and tx hash
    • Do NOT update the session status (it's already paid)
    • Do NOT insert a duplicate payment record
    • Return gracefully (no error thrown)

7. Testing

  • Unit tests: transaction rollback on failure, idempotency check, processing status transitions
  • Integration tests: concurrent payment submissions (2 payments for same session simultaneously)
  • Test recovery job: stuck processing sessions are recovered correctly
  • Test duplicate payment: same tx hash submitted twice is handled gracefully
  • Use jest.useFakeTimers() for the recovery job timing

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions