36 lines
1.9 KiB
MySQL
36 lines
1.9 KiB
MySQL
|
|
-- Migration 978: Refund state machine for Hyperswitch reverse-charge (v1.0.6)
|
||
|
|
-- Before v1.0.6, RefundOrder marked orders as `refunded` immediately after
|
||
|
|
-- POSTing to /refunds — treating Hyperswitch's synchronous API ack as
|
||
|
|
-- confirmation that the money had moved. In reality the PSP returns `pending`
|
||
|
|
-- and only confirms via webhook (`refund_succeeded` / `refund_failed`).
|
||
|
|
-- Customers could see "refunded" in the UI while their bank account still
|
||
|
|
-- hadn't been credited.
|
||
|
|
--
|
||
|
|
-- This migration introduces an auditable refund row that tracks the full
|
||
|
|
-- lifecycle: pending → (succeeded | failed). Uniqueness on
|
||
|
|
-- hyperswitch_refund_id is the load-bearing constraint — it guarantees that
|
||
|
|
-- the webhook handler can safely retry (idempotent 200) because a duplicate
|
||
|
|
-- PSP notification lands on the same DB row.
|
||
|
|
|
||
|
|
CREATE TABLE IF NOT EXISTS public.refunds (
|
||
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
order_id UUID NOT NULL REFERENCES public.orders(id) ON DELETE CASCADE,
|
||
|
|
initiator_id UUID NOT NULL REFERENCES public.users(id) ON DELETE SET NULL,
|
||
|
|
hyperswitch_payment_id TEXT NOT NULL,
|
||
|
|
hyperswitch_refund_id TEXT UNIQUE,
|
||
|
|
amount_cents BIGINT NOT NULL,
|
||
|
|
currency VARCHAR(3) NOT NULL DEFAULT 'EUR',
|
||
|
|
reason TEXT NOT NULL DEFAULT '',
|
||
|
|
status TEXT NOT NULL DEFAULT 'pending',
|
||
|
|
error_message TEXT,
|
||
|
|
succeeded_at TIMESTAMPTZ,
|
||
|
|
failed_at TIMESTAMPTZ,
|
||
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_refunds_order_id ON public.refunds(order_id);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_refunds_status ON public.refunds(status);
|
||
|
|
CREATE INDEX IF NOT EXISTS idx_refunds_hyperswitch_payment_id
|
||
|
|
ON public.refunds(hyperswitch_payment_id);
|