21fb14facf
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m45s
The last money-intake slice: reverse a paid order best-effort, exactly once. All refunds are admin-triggered (E7) — no rail pushes an unsolicited refund (Robokassa via its refund API / cabinet, VK via support, Telegram via refundStarPayment), so this ships the engine they all converge on, not a webhook. The Refund method matches the paid order, appends a refund ledger row (idempotent on (provider, provider_refund_id) — distinct from the fund's payment id, so both coexist), and revokes the funded chips floored at 0 (never negative — D27, balances_chips_chk). When the chips were already spent, the unrecoverable remainder is recorded as a per-account loss + abuse flag in the new additive payments.account_risk table (read by the E7 report). The refund ledger row's chip delta is what was actually reclaimed (the ledger stays reconcilable); the full reversal rides in the snapshot; the order stays paid. Additive migration (a new table only) -> rollback-safe, no contour wipe. Robokassa refund-status polling is deferred (a worker not worth it at low chargeback volume); failed events are not wired (no rail signals a hard post-charge server decline). Tests: integration (full revoke; revoke-after-spend = floor-0 + loss + abuse; duplicate idempotent; unpaid-order guard). Docs: PAYMENTS(+ru) §9, PLAN (E5 -> DONE).
26 lines
1.3 KiB
SQL
26 lines
1.3 KiB
SQL
-- Per-account payment risk: the loss and abuse signal an external/admin refund leaves
|
|
-- behind when the refunded chips were already spent. A refund revokes chips best-effort
|
|
-- and never drives a balance negative (D27, balances_chips_chk); the unrecoverable
|
|
-- remainder is a recorded loss and flips an abuse flag the /_gm financial report reads
|
|
-- (D40, E7). Mutable per-account state (upserted on each such refund), so — unlike the
|
|
-- ledger — it carries no append-only trigger. Additive: a new table only, so goose
|
|
-- applies it forward with no rewrite of existing data (the contour is not wiped). The
|
|
-- payments role inherits ALL on it via the schema default privileges set in 00010.
|
|
-- +goose Up
|
|
|
|
CREATE TABLE payments.account_risk (
|
|
account_id uuid NOT NULL,
|
|
-- abuse flips true the first time a refund cannot fully reclaim its chips (spent).
|
|
abuse boolean DEFAULT false NOT NULL,
|
|
-- loss_chips accumulates the unrecoverable chips across such refunds (bigint: an
|
|
-- accumulator, mapped to int64 by go-jet — not the numeric->float64 trap).
|
|
loss_chips bigint DEFAULT 0 NOT NULL,
|
|
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
|
CONSTRAINT account_risk_pkey PRIMARY KEY (account_id),
|
|
CONSTRAINT account_risk_loss_chips_chk CHECK ((loss_chips >= 0))
|
|
);
|
|
|
|
-- +goose Down
|
|
|
|
DROP TABLE IF EXISTS payments.account_risk;
|