-- =====================================================================
-- PAYMENT BROKER SCHEMA — generic multi-client ZarinPal gateway
-- Served on pay.flatrender.ir (the single ZarinPal-verified callback domain).
-- Other sites (meezi.ir, bargevasat.ir, FlatRender) register as client_apps
-- and route payments through this broker.
--
-- NOTE: migrations auto-run only on FIRST volume creation. On an existing
-- DB volume, apply this manually:
--   docker exec -i fr2-postgres psql -U postgres -d flatrender < 31_payment_broker.sql
-- =====================================================================

CREATE SCHEMA IF NOT EXISTS payment;
SET search_path TO payment, public;

-- ---------------------------------------------------------------------
-- client_apps — each site that pays through the broker
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS payment.client_apps (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id               UUID,                          -- optional link to identity.tenants
    name                    TEXT NOT NULL,                 -- "meezi.ir", "FlatRender"
    slug                    TEXT NOT NULL UNIQUE,          -- "meezi"
    api_key                 TEXT NOT NULL UNIQUE,          -- public id (pk_...)
    secret                  TEXT NOT NULL,                 -- shared HMAC secret (sk_...) — signs in+out

    -- ZarinPal: per-client override; NULL → broker default merchant/sandbox
    zarinpal_merchant_id    TEXT,
    zarinpal_sandbox        BOOLEAN,

    allowed_return_origins  TEXT[] NOT NULL DEFAULT '{}',  -- e.g. {'https://meezi.ir'}; empty = permissive
    webhook_url             TEXT,                          -- server-to-server result notification
    is_active               BOOLEAN NOT NULL DEFAULT TRUE,

    created_at              TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at              TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ---------------------------------------------------------------------
-- transactions — one row per payment attempt
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS payment.transactions (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    client_app_id       UUID NOT NULL REFERENCES payment.client_apps(id) ON DELETE RESTRICT,

    status              TEXT NOT NULL DEFAULT 'Created',   -- Created|Pending|Paid|Failed|Cancelled|Expired
    gateway             TEXT NOT NULL DEFAULT 'ZarinPal',

    amount_rial         BIGINT NOT NULL,                   -- canonical Rial
    currency            TEXT NOT NULL DEFAULT 'IRR',
    description         TEXT,

    client_ref          TEXT,                              -- the client's own order id
    return_url          TEXT NOT NULL,                     -- where the user is sent back
    metadata            JSONB,                             -- echoed back to the client
    payer_mobile        TEXT,
    payer_email         TEXT,

    authority           TEXT,                              -- ZarinPal authority token
    ref_id              TEXT,                              -- ZarinPal ref_id (receipt)
    card_pan            TEXT,
    fee_rial            BIGINT,
    gateway_response    JSONB,
    failure_reason      TEXT,

    paid_at             TIMESTAMPTZ,
    failed_at           TIMESTAMPTZ,
    expires_at          TIMESTAMPTZ,

    created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_pay_txn_client   ON payment.transactions(client_app_id, created_at DESC);
CREATE UNIQUE INDEX IF NOT EXISTS idx_pay_txn_authority ON payment.transactions(authority) WHERE authority IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_pay_txn_clientref ON payment.transactions(client_app_id, client_ref) WHERE client_ref IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_pay_txn_status   ON payment.transactions(status);

-- ---------------------------------------------------------------------
-- webhook_deliveries — outbound signed notifications with retry
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS payment.webhook_deliveries (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    transaction_id      UUID NOT NULL REFERENCES payment.transactions(id) ON DELETE CASCADE,
    url                 TEXT NOT NULL,
    payload             JSONB NOT NULL,
    signature           TEXT NOT NULL,
    attempts            INT NOT NULL DEFAULT 0,
    delivered           BOOLEAN NOT NULL DEFAULT FALSE,
    last_status         INT,
    last_error          TEXT,
    next_attempt_at     TIMESTAMPTZ,
    created_at          TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at          TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_pay_wh_pending ON payment.webhook_deliveries(delivered, next_attempt_at) WHERE delivered = FALSE;

-- ---------------------------------------------------------------------
-- updated_at triggers (helper tg_set_updated_at() created in 00_setup.sql)
-- ---------------------------------------------------------------------
DROP TRIGGER IF EXISTS tg_pay_client_apps_updated ON payment.client_apps;
CREATE TRIGGER tg_pay_client_apps_updated BEFORE UPDATE ON payment.client_apps
    FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at();

DROP TRIGGER IF EXISTS tg_pay_transactions_updated ON payment.transactions;
CREATE TRIGGER tg_pay_transactions_updated BEFORE UPDATE ON payment.transactions
    FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at();

DROP TRIGGER IF EXISTS tg_pay_webhook_updated ON payment.webhook_deliveries;
CREATE TRIGGER tg_pay_webhook_updated BEFORE UPDATE ON payment.webhook_deliveries
    FOR EACH ROW EXECUTE FUNCTION public.tg_set_updated_at();
