import { MigrationInterface, QueryRunner } from "typeorm"; /** * Drops the per-reference partial UNIQUE index so a domain order may have MANY intents — including * more than one SUCCEEDED (a second real payment is registered as its own row, not blocked by the * DB). A plain, non-unique lookup index replaces it for the reference lookups. * * DATA SAFETY: dropping an index never touches row data — no rows or column values change, and * relaxing a constraint cannot conflict with existing rows. * * ROLLBACK CAVEAT: `down()` re-creates the UNIQUE index on a best-effort basis. Once the new * "free initiate" behaviour has produced two active/SUCCEEDED intents for one order, re-creating * the unique index WILL FAIL (the duplicates violate it). Roll forward is always safe; rolling * back to the strict constraint is only possible while no duplicates exist. */ export class DropActiveReferenceUniqueIndex1782200000000 implements MigrationInterface { name = "DropActiveReferenceUniqueIndex1782200000000"; public async up(queryRunner: QueryRunner): Promise { await queryRunner.query( `DROP INDEX IF EXISTS "edr_payment"."uq_payment_intent_active_reference"`, ); await queryRunner.query( `CREATE INDEX IF NOT EXISTS "idx_payment_intent_reference" ON "edr_payment"."payment_intent" ("service", "reference_type", "reference_id")`, ); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query( `DROP INDEX IF EXISTS "edr_payment"."idx_payment_intent_reference"`, ); // Best-effort restore — fails if duplicate active/SUCCEEDED intents already exist (see note above). await queryRunner.query( `CREATE UNIQUE INDEX IF NOT EXISTS "uq_payment_intent_active_reference" ON "edr_payment"."payment_intent" ("service", "reference_type", "reference_id") WHERE status NOT IN ('FAILED','CANCELLED') AND deleted_at IS NULL`, ); } }