feat: add multicurrency tariff payments

This commit is contained in:
3252a8
2026-05-31 22:17:28 +03:00
parent df8f2636d2
commit 80e5f0c80d
35 changed files with 1242 additions and 227 deletions
@@ -11,7 +11,11 @@ from bot.middlewares.i18n import JsonI18n
from bot.utils.config_link import prepare_config_links
from bot.utils.date_utils import add_months, month_start
from config.settings import Settings
from config.tariffs_config import Tariff
from config.tariffs_config import (
Tariff,
default_currency_key_for_settings,
default_payment_currency_code_for_settings,
)
from db.dal import (
payment_dal,
promo_code_dal,
@@ -70,7 +70,7 @@ class HwidDeviceMixin:
package_set = tariff.hwid_device_packages
if not package_set:
return None
packages = package_set.for_currency("stars" if currency == "stars" else "rub")
packages = package_set.for_currency(currency)
return next((pkg for pkg in packages if int(pkg.count) == int(device_count)), None)
def _quote_hwid_package_price(
@@ -173,7 +173,7 @@ class HwidDeviceMixin:
valid_from=valid_from,
valid_until=valid_until,
now=now,
currency="stars" if currency == "stars" else "rub",
currency=currency,
)
quote.update(
{
@@ -227,7 +227,11 @@ class HwidDeviceMixin:
)
return None
packages = (
[*tariff.hwid_device_packages.rub, *tariff.hwid_device_packages.stars]
[
package
for currency_packages in tariff.hwid_device_packages.root.values()
for package in currency_packages
]
if tariff.hwid_device_packages
else []
)
@@ -249,7 +249,8 @@ class SubscriptionLifecycleMixin:
)
update_data["period_start_at"] = None
update_data["effective_monthly_price_rub"] = (
target.period_price(1, "rub") or target.min_period_price_rub()
target.period_price(1, default_currency_key_for_settings(self.settings))
or target.min_period_price(default_currency_key_for_settings(self.settings))
)
if mode == "recalc_days" and options.get("recalc_days") is not None:
update_data["end_date"] = now + timedelta(days=int(options["recalc_days"]))
@@ -120,7 +120,7 @@ class PaymentContextMixin:
months=int(months or 0),
traffic_gb=traffic_gb,
amount=float(payment_amount or 0),
currency=self.settings.DEFAULT_CURRENCY_SYMBOL,
currency=default_payment_currency_code_for_settings(self.settings),
end_date_text=end_date_text,
dashboard_url=dashboard_url,
provider_label=provider_label,
@@ -41,7 +41,21 @@ class RenewalMixin:
return False
months = sub.duration_months or 1
amount = self.settings.subscription_options.get(months)
currency = default_payment_currency_code_for_settings(self.settings)
amount = None
tariffs_config = self._tariffs_config() if callable(getattr(self, "_tariffs_config", None)) else None
if tariffs_config and callable(getattr(self, "_resolve_tariff", None)):
try:
tariff = self._resolve_tariff(getattr(sub, "tariff_key", None))
except Exception:
tariff = None
if tariff and tariff.billing_model == "period":
amount = tariff.period_price(
months,
default_currency_key_for_settings(self.settings),
)
if amount is None:
amount = self.settings.subscription_options.get(months)
if not amount:
logging.error(f"Auto-renew price missing for {months} months")
return False
@@ -53,7 +67,7 @@ class RenewalMixin:
}
resp = await yk.create_payment(
amount=float(amount),
currency="RUB",
currency=currency,
description=f"Auto-renewal for {months} months",
metadata=metadata,
payment_method_id=default_pm.provider_payment_method_id,
@@ -331,11 +331,12 @@ class TariffMixin:
remaining_days = max(0, (sub.end_date - now).days) if sub.end_date else 0
effective = float(sub.effective_monthly_price_rub or 0)
current_model = current_tariff.billing_model if current_tariff else "period"
default_currency = default_currency_key_for_settings(self.settings)
if current_model == "period" and target_tariff.billing_model == "period":
target_monthly = (
target_tariff.period_price(1, "rub")
or target_tariff.min_period_price_rub()
target_tariff.period_price(1, default_currency)
or target_tariff.min_period_price(default_currency)
or effective
or 1
)
@@ -355,11 +356,14 @@ class TariffMixin:
"remaining_days": remaining_days,
"recalc_days": max(0, days_after),
"paid_diff_rub": paid_diff,
"paid_diff": paid_diff,
"target_monthly_rub": float(target_monthly),
"target_monthly_price": float(target_monthly),
"currency": default_currency,
}
if current_model == "period" and target_tariff.billing_model == "traffic":
rub_per_gb = target_tariff.rub_per_gb_for_conversion()
rub_per_gb = target_tariff.currency_per_gb_for_conversion(default_currency)
remaining_value = remaining_days * (effective / 30) if effective else 0
converted_gb = math.floor(remaining_value / rub_per_gb) if rub_per_gb else 0
return {
@@ -367,9 +371,15 @@ class TariffMixin:
"remaining_days": remaining_days,
"converted_gb": max(0, converted_gb),
"rub_per_gb": rub_per_gb,
"currency_per_gb": rub_per_gb,
"currency": default_currency,
}
return {"mode": "traffic_to_period", "remaining_days": remaining_days}
return {
"mode": "traffic_to_period",
"remaining_days": remaining_days,
"currency": default_currency,
}
@staticmethod
def _aware_utc(value: Optional[datetime]) -> Optional[datetime]:
@@ -434,6 +444,7 @@ class TariffMixin:
credit = await self._hwid_conversion_credit(session, sub, at=now)
value_rub = float(credit.get("value_rub") or 0)
options["converted_hwid_value_rub"] = round(value_rub, 2)
options["converted_hwid_value"] = round(value_rub, 2)
options["convertible_hwid_purchase_ids"] = list(credit.get("purchase_ids") or [])
options["nonconverted_hwid_devices"] = int(credit.get("skipped_devices") or 0)
if value_rub <= 0: