mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-27 18:18:52 +01:00
fix: CHECK constraints on money-table statuses; cover processing in payout guard
A typo in a status string (e.g. 'complete' vs 'completed') silently produced a money row no index/predicate matched, orphaning funds. - Migration 017 adds idempotent NOT VALID CHECK constraints on payments.status, payments.payment_type and payouts.status. NOT VALID enforces all new writes without failing on any pre-existing rows; conname guards make it re-runnable. - The in-code existing-payout guard in completeGame now also covers the 'processing' status (matching the unique index from migration 015), so a duplicate win can't create a second payable row while a payout is mid-flight. Full suite: 15 suites / 131 tests.
This commit is contained in:
@@ -1193,7 +1193,7 @@ class GameModeManager {
|
||||
// Check if payout was already processed for this game (prevent duplicates)
|
||||
if (gameDbId) {
|
||||
const existingPayout = await this.db.query(`
|
||||
SELECT id FROM payouts WHERE game_id = $1 AND status IN ('pending', 'completed')
|
||||
SELECT id FROM payouts WHERE game_id = $1 AND status IN ('pending', 'processing', 'completed')
|
||||
`, [gameDbId]);
|
||||
if (existingPayout.rows.length > 0) {
|
||||
console.log(`⚠️ Payout already exists for game ${gameId}, skipping duplicate`);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
-- Migration 017: CHECK constraints on money-table status/type columns
|
||||
--
|
||||
-- payments.status, payouts.status and payments.payment_type were free-form VARCHARs.
|
||||
-- A typo in any one query (e.g. 'complete' vs 'completed') silently produces a row that
|
||||
-- no index/predicate matches — orphaning money (a payout stuck in a misspelled status is
|
||||
-- invisible to both the batcher and the retry candidate query). These CHECKs catch typos.
|
||||
--
|
||||
-- Added NOT VALID so they enforce all NEW writes without failing on any pre-existing rows,
|
||||
-- and guarded by conname checks so the migration is idempotent / safe to re-run.
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'payments_status_check') THEN
|
||||
ALTER TABLE payments
|
||||
ADD CONSTRAINT payments_status_check
|
||||
CHECK (status IN ('pending', 'confirmed', 'expired', 'refunded')) NOT VALID;
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'payments_payment_type_check') THEN
|
||||
ALTER TABLE payments
|
||||
ADD CONSTRAINT payments_payment_type_check
|
||||
CHECK (payment_type IN ('single_game', 'credits_package')) NOT VALID;
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'payouts_status_check') THEN
|
||||
ALTER TABLE payouts
|
||||
ADD CONSTRAINT payouts_status_check
|
||||
CHECK (status IN ('pending', 'processing', 'completed', 'failed', 'needs_review', 'permanently_failed', 'batched')) NOT VALID;
|
||||
END IF;
|
||||
END $$;
|
||||
Reference in New Issue
Block a user