veza/veza-backend-api/migrations/979_refunds_unique_partial.sql

31 lines
1.4 KiB
MySQL
Raw Normal View History

-- Migration 979: partial UNIQUE on refunds.hyperswitch_refund_id (v1.0.6.1 hotfix)
--
-- The v1.0.6 migration 978 used a plain UNIQUE constraint on
-- hyperswitch_refund_id. That broke when two refunds in the same DB
-- stayed in their post-Phase-1 / pre-Phase-2 state: both rows have
-- hyperswitch_refund_id='' (empty string, because Go's zero-value for
-- string writes '' rather than NULL), and PostgreSQL treats two empty
-- strings as colliding under a regular UNIQUE constraint.
--
-- Surfaced by the v1.0.6 refund smoke test (scenario S4, triggered
-- after the S3 PSP-error path left one row with refund_id=''): the
-- second refund attempt on a different order got a UNIQUE violation
-- at INSERT time.
--
-- Fix: make the UNIQUE partial — only enforce uniqueness on rows that
-- have actually received a PSP-assigned refund_id. Empty strings and
-- NULLs are ignored. This preserves the load-bearing idempotency
-- guarantee for successful refunds (duplicate webhook lands on the
-- same row) without rejecting legitimate second attempts after a PSP
-- failure on a different order.
ALTER TABLE public.refunds
DROP CONSTRAINT IF EXISTS refunds_hyperswitch_refund_id_key;
DROP INDEX IF EXISTS refunds_hyperswitch_refund_id_unique;
CREATE UNIQUE INDEX refunds_hyperswitch_refund_id_unique
ON public.refunds (hyperswitch_refund_id)
WHERE hyperswitch_refund_id IS NOT NULL
AND hyperswitch_refund_id <> '';