[FEAT] Exactly-Once Payment Processing with Conflict Resolution
Priority: High
Difficulty: Hard
Estimated Effort: 2-3 days
Relevant Packages: OrbitStream_backend/
Labels: enhancement, data-integrity, priority:high
Requirements
1. Serializable Transaction
Wrap the entire processPayment in a Drizzle transaction with the highest isolation level:
await db.transaction(async (tx) => {
// Lock the session row
const [session] = await tx.execute(
sql`SELECT * FROM checkout_sessions WHERE id = ${sessionId} FOR UPDATE`
);
if (session.status !== 'pending') {
return; // Another payment already processed this session
}
// Update session
await tx.update(checkoutSessions).set({ status: 'paid' })...
// Insert payment
await tx.insert(payments).values({ ... })...
});
2. Idempotency Check
Before processing, check if a payment with the same tx_hash already exists:
const existing = await db.query.payments.findFirst({
where: eq(payments.txHash, op.transaction_hash),
});
if (existing) {
this.logger.warn(`Duplicate payment ${op.transaction_hash} — skipping`);
return;
}
3. Recovery for Stale Sessions
Implement a periodic job that finds and repairs inconsistent state:
- Sessions stuck in
processing for > 5 minutes → check Horizon, recover or rollback
- Sessions marked
paid with no payment record → check Horizon, insert missing payment
- Sessions with payment records but still
pending → mark as paid
Run every 5 minutes using @nestjs/schedule.
4. Duplicate Payment Handling
If a payment arrives for an already-paid session:
- Log a warning
- Don't update the session
- Don't insert a duplicate payment
- Don't dispatch a duplicate webhook
- Return gracefully
5. Testing
- Concurrent payment submissions (2 payments for same session simultaneously)
- Verify only one succeeds
- Test idempotency: same tx hash submitted twice
- Test recovery job: orphaned sessions are repaired
- Test crash simulation: session stuck in processing
[FEAT] Exactly-Once Payment Processing with Conflict Resolution
Priority: High
Difficulty: Hard
Estimated Effort: 2-3 days
Relevant Packages:
OrbitStream_backend/Labels:
enhancement,data-integrity,priority:highRequirements
1. Serializable Transaction
Wrap the entire
processPaymentin a Drizzle transaction with the highest isolation level:2. Idempotency Check
Before processing, check if a payment with the same
tx_hashalready exists:3. Recovery for Stale Sessions
Implement a periodic job that finds and repairs inconsistent state:
processingfor > 5 minutes → check Horizon, recover or rollbackpaidwith no payment record → check Horizon, insert missing paymentpending→ mark as paidRun every 5 minutes using
@nestjs/schedule.4. Duplicate Payment Handling
If a payment arrives for an already-paid session:
5. Testing