feat(billing): PaymentProvider + Mock, self-service пополнение баланса

Шаг 0.13 плана production-готовности:
- интерфейс PaymentProvider (createPayment/verifyWebhook/getPaymentStatus)
- MockPaymentProvider с настоящим webhook (sha256-подпись, идемпотентность)
- applySucceededPayment: транзакция с guard, credit, авто-снятие billingBlocked
- роуты payments + confirm-test (только mock) + публичный webhook
- Billing.tsx: Пополнить/Подтвердить (тест), фикс setLocation в рендере
- миграция 0080 billing_payments, 13 новых тестов (78/78)
This commit is contained in:
2026-09-07 22:45:30 +03:00
parent 23c49adcd1
commit 701af06290
11 changed files with 1039 additions and 14 deletions

View File

@@ -0,0 +1,22 @@
-- Шаг 0.13 плана production-готовности: платежи самообслуживания биллинга.
-- Пополнение баланса админом организации через платёжного провайдера (сейчас — mock).
-- Без RLS: запись — только серверные роуты (auth + billing.manage) и webhook провайдера,
-- чтение — только через роуты с явным фильтром organization_id.
CREATE TABLE IF NOT EXISTS billing_payments (
id SERIAL PRIMARY KEY,
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
provider VARCHAR(30) NOT NULL,
external_id VARCHAR(255) NOT NULL,
amount NUMERIC(10,2) NOT NULL,
currency VARCHAR(10) NOT NULL DEFAULT 'RUB',
status VARCHAR(20) NOT NULL DEFAULT 'pending',
idempotency_key VARCHAR(255) NOT NULL,
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE (external_id),
UNIQUE (idempotency_key)
);
CREATE INDEX IF NOT EXISTS billing_payments_org_status_idx
ON billing_payments (organization_id, status);