feat(payments): live payment-availability kill switch + per-account override
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 21s
CI / ui (pull_request) Successful in 1m15s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 2m0s
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 21s
CI / ui (pull_request) Successful in 1m15s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 2m0s
Let an operator disable purchases live from the admin — a whole rail/channel or one account — and show the user a localized reason on their next attempt, so a provider outage or a misconfig is explained instead of a silent dead button. - rail kill switch (payments.rail_status, per rail direct:web / direct:android / vk / telegram): enabled + a per-language message, edited on the catalog page. Fail-open — a rail with no row stays enabled, so payments are never accidentally killed. The intake gate (CanPurchase in handleWalletOrder, before the order) returns payment_unavailable + the localized message, orthogonal to the security gates. - per-account override (payments.account_payment_override, a row only for non-default): allow / deny / default, edited on the user card. "allow" bypasses ONLY the ops rail switch, never the security gates (trusted platform, the email anchor, the VK-iOS freeze, the min client version). - wire: an additive ExecuteResponse.message envelope field (frozen-contract-safe); the gateway forwards a backend domain-error message; the client shows it on a payment_unavailable buy attempt. - admin: rail toggles on the catalog page, the override control on the user card. - tests: the pure gate (unit, TDD), the store + gate + override end-to-end (integration, migration 00016), the client (svelte-check / vitest). - docs: PAYMENTS (+ru), the decisions log (D45/D46). Fiscalization stays cabinet-side (owner decision) — no itemized-receipt code. Contour-safe: additive migration (two new tables, no wipe), the wire add is additive, and fail-open so nothing is disabled until an operator acts.
This commit is contained in:
@@ -268,6 +268,19 @@ web+PWA / native Android+iOS через Capacitor). Владелец (самоз
|
||||
`shop` (web/android/…) — аддитивная колонка. Используется в финансовом отчёте (D40) для
|
||||
разбивки «из какого магазина/канала платёж». На зачисление и на сегмент кошелька не влияет
|
||||
(всегда `direct`, D42).
|
||||
- **D45. Рубильник платежей per-rail + локализованное сообщение.** Оператор выключает покупки на
|
||||
рельсе/канале (`direct:web`/`direct:android`/`vk`/`telegram`) живьём из `/_gm` (таблица
|
||||
`payments.rail_status`, редактируется на странице каталога). **Fail-open:** нет строки → рельс
|
||||
включён (случайно не убить платежи). Выключенный рельс на попытке покупки возвращает
|
||||
`payment_unavailable` + сообщение админа на языке юзера (RU/EN; пусто → встроенное «временно
|
||||
недоступно»). Сообщение едет клиенту через аддитивное `ExecuteResponse.message` (envelope-слой,
|
||||
frozen-contract-safe). Гейт ортогонален security-гейтам.
|
||||
- **D46. Per-user override покупок (allow/deny/default).** На карточке юзера (`/_gm`) —
|
||||
переопределение на аккаунт: `allow` (всегда разрешить), `deny` (всегда запретить), `default` (по
|
||||
рельс-рубильнику). Хранится в `payments.account_payment_override` строкой только для не-default
|
||||
(нет строки = default; снять = удалить строку). **`allow` обходит ТОЛЬКО ops-рубильник, НЕ
|
||||
security-гейты** (D36 email-якорь, VK-iOS-фриз, trusted-платформа, min-client-version) — те
|
||||
проверяются отдельно в CreateOrder.
|
||||
|
||||
## Заметки к оформлению документов
|
||||
|
||||
@@ -283,14 +296,16 @@ web+PWA / native Android+iOS через Capacitor). Владелец (самоз
|
||||
ведёт к пропускам. Текст — только для фиксации решённого и пояснений. Усилить
|
||||
feedback-память `prefer-interview-mode` после plan mode.
|
||||
|
||||
## Все развилки закрыты (D1-D44)
|
||||
## Все развилки закрыты (D1-D46)
|
||||
|
||||
Интервью завершено. Дальше — оформление документов и реализация по релизам.
|
||||
|
||||
**Дополнение 2026-07-14 (интервью с владельцем).** D41 ревизована (владелец переходит на
|
||||
ИП: 54-ФЗ через облачную кассу Robokassa вместо авточека НПД); добавлены D42-D44 — сплит
|
||||
`direct`-рельса Robokassa на магазины по каналу (web/android; ios позже) при едином кошельке
|
||||
`direct` и входе email-only в standalone-приложениях. Реализация — новый этап E10 в `PLAN.md`.
|
||||
`direct` и входе email-only в standalone-приложениях (этап E10 в `PLAN.md`). Плюс D45-D46 —
|
||||
рубильник платежей per-rail + локализованное сообщение + per-user override (этап E11). Фискализация
|
||||
(B4) — кабинетная на стороне Robokassa, itemized-код не делаем (решение владельца).
|
||||
|
||||
## План внедрения (черновик PLAN.md — «слоями»)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user