[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
[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:highRequirements
1. Transactional Processing
processPaymentfunction body in a Drizzle transactiondb.transaction(async (tx) => { ... })for atomic session update + payment insert2. Optimistic Locking
status = 'pending'within the same transactionSELECT ... FOR UPDATE(pessimistic) or check affected row count (optimistic)3. Idempotency Key
payment_idempotency_keycolumn to thepaymentstable:UNIQUE(tx_hash, session_id)4. Processing Status
processingstatus to thesessionStatusEnumprocessingfirst, then topaidafter payment record is inserted5. Recovery Job
processingstatus for > 5 minutespaidpending(allow re-detection)6. Duplicate Payment Handling
7. Testing
jest.useFakeTimers()for the recovery job timing