Региональные цены: UAH/BYN/VND/UZS + обобщённый список по валюте

plan_prices уже поддерживал мультивалютность (USD/RUB/CNY/TRY/IRR) — просто
добавляю ещё 4 по аналогии, по локалям уже сделанным в этой сессии
(uk/be/vi/uz). Цифры — прикидка (~45% от рыночной конвертации, тот же
порядок, что уже неявно в RUB/CNY/TRY), не сверено с реальными
конкурентами по каждому рынку — легко поправить UPDATE'ом по этой же
таблице.

list_active_plans_by_currency — обобщённая версия list_active_plans_usd,
нужна для мультивалютного /plans и бота.
This commit is contained in:
2026-07-11 00:54:48 +07:00
parent a84d5dc820
commit a89721fbee
2 changed files with 99 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
-- Расширение регионального прайса: UAH/BYN/VND/UZS — по аналогии с уже
-- заведёнными USD/RUB/CNY/TRY/IRR (см. 0001_init.sql). Валюты выбраны по
-- уже сделанным в этой сессии локалям (uk/be/vi/uz).
--
-- Курс и скидка (~45% от чистой рыночной конвертации, тот же порядок, что
-- уже неявно заложен в RUB/CNY/TRY) — ПРИКИДКА для старта, не сверено с
-- реальными локальными конкурентами по каждому рынку. Легко поправить —
-- это просто UPDATE по этой же таблице, менять код не нужно.
--
-- VND/UZS — целыми единицами (без копеек/копий), та же логика, что уже у
-- IRR: у донга и сума нет ходовой разменной монеты, дробные суммы не имеют
-- смысла для конечного пользователя.
INSERT INTO plan_prices (plan_name, currency, amount) VALUES
-- =================================================================
-- УКРАИНА (UAH)
-- =================================================================
('GHOST_1M', 'UAH', 11900), -- 119.00 ₴
('GHOST_6M', 'UAH', 59900), -- 599.00 ₴
('GHOST_12M', 'UAH', 89900), -- 899.00 ₴
('NETRUNNER_1M', 'UAH', 21900), -- 219.00 ₴
('NETRUNNER_6M', 'UAH', 109900), -- 1 099.00 ₴
('NETRUNNER_12M', 'UAH', 149900), -- 1 499.00 ₴
('MAINFRAME_1M', 'UAH', 49900), -- 499.00 ₴
('MAINFRAME_6M', 'UAH', 229900), -- 2 299.00 ₴
('MAINFRAME_12M', 'UAH', 349900), -- 3 499.00 ₴
-- =================================================================
-- БЕЛАРУСЬ (BYN)
-- =================================================================
('GHOST_1M', 'BYN', 900), -- 9.00 Br
('GHOST_6M', 'BYN', 4400), -- 44.00 Br
('GHOST_12M', 'BYN', 6500), -- 65.00 Br
('NETRUNNER_1M', 'BYN', 1650), -- 16.50 Br
('NETRUNNER_6M', 'BYN', 8200), -- 82.00 Br
('NETRUNNER_12M', 'BYN', 10900), -- 109.00 Br
('MAINFRAME_1M', 'BYN', 3600), -- 36.00 Br
('MAINFRAME_6M', 'BYN', 17500), -- 175.00 Br
('MAINFRAME_12M', 'BYN', 26000), -- 260.00 Br
-- =================================================================
-- ВЬЕТНАМ (VND) — целыми донгами, без копеек (см. заголовок)
-- =================================================================
('GHOST_1M', 'VND', 69000), -- 69 000 ₫
('GHOST_6M', 'VND', 329000), -- 329 000 ₫
('GHOST_12M', 'VND', 499000), -- 499 000 ₫
('NETRUNNER_1M', 'VND', 125000), -- 125 000 ₫
('NETRUNNER_6M', 'VND', 619000), -- 619 000 ₫
('NETRUNNER_12M', 'VND', 829000), -- 829 000 ₫
('MAINFRAME_1M', 'VND', 279000), -- 279 000 ₫
('MAINFRAME_6M', 'VND', 1329000), -- 1 329 000 ₫
('MAINFRAME_12M', 'VND', 1999000), -- 1 999 000 ₫
-- =================================================================
-- УЗБЕКИСТАН (UZS) — целыми сумами, без копеек (см. заголовок)
-- =================================================================
('GHOST_1M', 'UZS', 35000), -- 35 000 сум
('GHOST_6M', 'UZS', 169000), -- 169 000 сум
('GHOST_12M', 'UZS', 249000), -- 249 000 сум
('NETRUNNER_1M', 'UZS', 63000), -- 63 000 сум
('NETRUNNER_6M', 'UZS', 319000), -- 319 000 сум
('NETRUNNER_12M', 'UZS', 419000), -- 419 000 сум
('MAINFRAME_1M', 'UZS', 139000), -- 139 000 сум
('MAINFRAME_6M', 'UZS', 669000), -- 669 000 сум
('MAINFRAME_12M', 'UZS', 999000) -- 999 000 сум
ON CONFLICT (plan_name, currency) DO UPDATE
SET amount = EXCLUDED.amount;
+25
View File
@@ -124,6 +124,15 @@ pub trait AppRepository: Send + Sync {
/// `0001_init.sql`), а не потому что CryptoBot требует именно её. /// `0001_init.sql`), а не потому что CryptoBot требует именно её.
async fn list_active_plans_usd(&self) -> Result<Vec<(String, i64)>, sqlx::Error>; async fn list_active_plans_usd(&self) -> Result<Vec<(String, i64)>, sqlx::Error>;
/// Обобщённая версия `list_active_plans_usd` — тарифы в произвольной
/// валюте (см. `modules::geoip` — валюта определяется по IP/языку).
/// Пустой Vec, если для этой валюты нет ни одной активной строки в
/// `plan_prices` (вызывающая сторона должна фолбэкнуться на USD).
async fn list_active_plans_by_currency(
&self,
currency: &str,
) -> Result<Vec<(String, i64)>, sqlx::Error>;
async fn create_invoice( async fn create_invoice(
&self, &self,
user_id: Uuid, user_id: Uuid,
@@ -728,6 +737,22 @@ impl AppRepository for PgRepository {
.await .await
} }
#[instrument(skip(self))]
async fn list_active_plans_by_currency(
&self,
currency: &str,
) -> Result<Vec<(String, i64)>, sqlx::Error> {
sqlx::query_as(
"SELECT pp.plan_name, pp.amount FROM plan_prices pp \
JOIN plans p ON p.name = pp.plan_name \
WHERE pp.currency = $1 AND pp.is_active = TRUE AND p.is_active = TRUE \
ORDER BY pp.amount ASC",
)
.bind(currency)
.fetch_all(&self.pool)
.await
}
#[instrument(skip(self))] #[instrument(skip(self))]
async fn create_invoice( async fn create_invoice(
&self, &self,