feat: add multicurrency tariff payments
This commit is contained in:
@@ -37,7 +37,7 @@ from bot.services.settings_override_service import (
|
||||
from bot.utils import MessageContent, send_message_via_queue
|
||||
from bot.utils.message_queue import get_queue_manager
|
||||
from config.settings import Settings
|
||||
from config.tariffs_config import TariffsConfig
|
||||
from config.tariffs_config import TariffsConfig, default_payment_currency_code_for_settings
|
||||
from db.dal import (
|
||||
ad_dal,
|
||||
app_settings_dal,
|
||||
|
||||
@@ -34,7 +34,7 @@ async def admin_stats_route(request: web.Request) -> web.Response:
|
||||
except Exception: # pragma: no cover - defensive
|
||||
payload["queue"] = None
|
||||
|
||||
payload["currency_symbol"] = settings.DEFAULT_CURRENCY_SYMBOL or "RUB"
|
||||
payload["currency_symbol"] = default_payment_currency_code_for_settings(settings)
|
||||
return _ok(payload)
|
||||
|
||||
|
||||
|
||||
@@ -21,9 +21,11 @@ async def admin_tariffs_get_route(request: web.Request) -> web.Response:
|
||||
"path": str(path),
|
||||
"catalog": {
|
||||
"default_tariff": "",
|
||||
"default_currency": "rub",
|
||||
"topup_packages_default": {"rub": [], "stars": []},
|
||||
"tariffs": [],
|
||||
},
|
||||
"provider_currency_support": _provider_currency_support_payload(settings, request.app),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -32,6 +34,7 @@ async def admin_tariffs_get_route(request: web.Request) -> web.Response:
|
||||
"exists": True,
|
||||
"path": str(path),
|
||||
"catalog": _tariffs_config_payload(config),
|
||||
"provider_currency_support": _provider_currency_support_payload(settings, request.app),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -58,4 +61,48 @@ async def admin_tariffs_save_route(request: web.Request) -> web.Response:
|
||||
|
||||
await refresh_webapp_runtime_after_settings_change(request, updates={}, deletes=[])
|
||||
|
||||
return _ok({"exists": True, "path": str(path), "catalog": _tariffs_config_payload(config)})
|
||||
return _ok(
|
||||
{
|
||||
"exists": True,
|
||||
"path": str(path),
|
||||
"catalog": _tariffs_config_payload(config),
|
||||
"provider_currency_support": _provider_currency_support_payload(settings, request.app),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _provider_currency_support_payload(settings: Settings, app: web.Application) -> List[Dict[str, Any]]:
|
||||
from bot.payment_providers import iter_provider_specs, resolve_provider_presentation
|
||||
|
||||
default_currency = default_payment_currency_code_for_settings(settings)
|
||||
providers: List[Dict[str, Any]] = []
|
||||
for spec in iter_provider_specs():
|
||||
presentation = resolve_provider_presentation(spec, settings)
|
||||
supported = spec.supported_currency_codes(settings)
|
||||
providers.append(
|
||||
{
|
||||
"id": spec.id,
|
||||
"provider_key": spec.provider_key,
|
||||
"label": presentation.webapp_label or spec.label,
|
||||
"telegram_label": presentation.telegram_label,
|
||||
"icon": presentation.webapp_icon,
|
||||
"enabled": spec.is_effectively_enabled(settings),
|
||||
"configured": spec.is_service_configured(app),
|
||||
"admin_only": spec.is_admin_only_enabled(settings),
|
||||
"price_source": spec.price_source,
|
||||
"currencies": list(supported) if supported is not None else None,
|
||||
"accepts_any_currency": supported is None,
|
||||
"supports_default_currency": spec.is_usable_for_payment_currency(
|
||||
settings,
|
||||
default_currency,
|
||||
),
|
||||
"directly_supports_default_currency": spec.supports_currency(
|
||||
settings,
|
||||
default_currency,
|
||||
),
|
||||
"default_currency": default_currency,
|
||||
"note": spec.currency_support_note,
|
||||
"docs_url": spec.currency_support_url,
|
||||
}
|
||||
)
|
||||
return providers
|
||||
|
||||
@@ -55,6 +55,11 @@ from bot.utils.text_sanitizer import (
|
||||
sanitize_username,
|
||||
)
|
||||
from config.settings import Settings
|
||||
from config.tariffs_config import (
|
||||
default_currency_key_for_settings,
|
||||
default_payment_currency_code_for_settings,
|
||||
payment_currency_code,
|
||||
)
|
||||
from db.dal import payment_dal, security_dal, subscription_dal, support_dal, user_dal
|
||||
from db.dal.user_dal import UserMergeConflictError
|
||||
from db.models import Payment, User, UserTelegramAvatar
|
||||
|
||||
@@ -103,6 +103,8 @@ async def create_payment_route(request: web.Request) -> web.Response:
|
||||
subscription_service: SubscriptionService = request.app["subscription_service"]
|
||||
cached = _get_cached_webapp_settings(request)
|
||||
tariffs_config = settings.tariffs_config
|
||||
default_currency = default_currency_key_for_settings(settings)
|
||||
default_currency_code = payment_currency_code(default_currency)
|
||||
traffic_mode = bool(settings.traffic_sale_mode)
|
||||
sale_mode = "subscription"
|
||||
traffic_gb_for_payment: Optional[float] = None
|
||||
@@ -155,17 +157,17 @@ async def create_payment_route(request: web.Request) -> web.Response:
|
||||
if requested_sale_mode == "premium_topup"
|
||||
else tariffs_config.topup_packages_for(tariff)
|
||||
)
|
||||
rub_packages = {
|
||||
currency_packages = {
|
||||
float(package.gb): float(package.price)
|
||||
for package in (packages.rub if packages else [])
|
||||
for package in (packages.for_currency(default_currency) if packages else [])
|
||||
}
|
||||
stars_packages = {
|
||||
float(package.gb): int(float(package.price))
|
||||
for package in (packages.stars if packages else [])
|
||||
}
|
||||
package_key = _resolve_numeric_option_key(rub_packages, traffic_gb)
|
||||
package_key = _resolve_numeric_option_key(currency_packages, traffic_gb)
|
||||
stars_package_key = _resolve_numeric_option_key(stars_packages, traffic_gb)
|
||||
price = rub_packages.get(package_key) if package_key is not None else None
|
||||
price = currency_packages.get(package_key) if package_key is not None else None
|
||||
stars_price = (
|
||||
stars_packages.get(stars_package_key) if stars_package_key is not None else None
|
||||
)
|
||||
@@ -196,17 +198,21 @@ async def create_payment_route(request: web.Request) -> web.Response:
|
||||
return _json_error(400, "invalid_plan", "Invalid traffic package")
|
||||
if traffic_gb <= 0:
|
||||
return _json_error(400, "invalid_plan", "Invalid traffic package")
|
||||
rub_packages = {
|
||||
currency_packages = {
|
||||
float(package.gb): float(package.price)
|
||||
for package in (tariff.traffic_packages.rub if tariff.traffic_packages else [])
|
||||
for package in (
|
||||
tariff.traffic_packages.for_currency(default_currency)
|
||||
if tariff.traffic_packages
|
||||
else []
|
||||
)
|
||||
}
|
||||
stars_packages = {
|
||||
float(package.gb): int(float(package.price))
|
||||
for package in (tariff.traffic_packages.stars if tariff.traffic_packages else [])
|
||||
}
|
||||
package_key = _resolve_numeric_option_key(rub_packages, traffic_gb)
|
||||
package_key = _resolve_numeric_option_key(currency_packages, traffic_gb)
|
||||
stars_package_key = _resolve_numeric_option_key(stars_packages, traffic_gb)
|
||||
price = rub_packages.get(package_key) if package_key is not None else None
|
||||
price = currency_packages.get(package_key) if package_key is not None else None
|
||||
stars_price = (
|
||||
stars_packages.get(stars_package_key) if stars_package_key is not None else None
|
||||
)
|
||||
@@ -224,7 +230,7 @@ async def create_payment_route(request: web.Request) -> web.Response:
|
||||
return _json_error(400, "invalid_plan", "Invalid subscription period")
|
||||
if months not in tariff.enabled_periods:
|
||||
return _json_error(400, "invalid_plan", "Subscription period is not available")
|
||||
price = tariff.period_price(months, "rub")
|
||||
price = tariff.period_price(months, default_currency)
|
||||
stars_price_raw = tariff.period_price(months, "stars")
|
||||
stars_price = int(stars_price_raw) if stars_price_raw and stars_price_raw > 0 else None
|
||||
if price is None and method != "stars":
|
||||
@@ -296,7 +302,7 @@ async def create_payment_route(request: web.Request) -> web.Response:
|
||||
active_tariff = None
|
||||
if not active_tariff or active_tariff.billing_model != "period":
|
||||
return _json_error(400, "invalid_plan", "Device top-up is not available")
|
||||
currency = "stars" if method == "stars" else "rub"
|
||||
currency = "stars" if method == "stars" else default_currency
|
||||
hwid_quote = await subscription_service.quote_hwid_device_topup(
|
||||
session,
|
||||
user_id=user_id,
|
||||
@@ -325,6 +331,7 @@ async def create_payment_route(request: web.Request) -> web.Response:
|
||||
months=payment_units,
|
||||
price=float(price or 0),
|
||||
stars_price=stars_price,
|
||||
currency=default_currency_code,
|
||||
lang=lang,
|
||||
sale_mode=sale_mode,
|
||||
traffic_gb=traffic_gb_for_payment,
|
||||
@@ -583,6 +590,7 @@ async def tariff_change_payment_route(request: web.Request) -> web.Response:
|
||||
tariff_key = str(payment_payload.tariff_key or "").strip()
|
||||
settings: Settings = request.app["settings"]
|
||||
config = settings.tariffs_config
|
||||
default_currency_code = default_payment_currency_code_for_settings(settings)
|
||||
if not config:
|
||||
return _json_error(404, "tariffs_unavailable", "Tariffs are not configured")
|
||||
if not tariff_key:
|
||||
@@ -618,6 +626,7 @@ async def tariff_change_payment_route(request: web.Request) -> web.Response:
|
||||
months=1,
|
||||
price=price,
|
||||
stars_price=None,
|
||||
currency=default_currency_code,
|
||||
lang=db_user.language_code or settings.DEFAULT_LANGUAGE,
|
||||
sale_mode=f"tariff_upgrade@{target.key}",
|
||||
)
|
||||
@@ -656,20 +665,29 @@ async def device_topup_options_route(request: web.Request) -> web.Response:
|
||||
active.get("extra_hwid_devices_valid_until_text") if active else None
|
||||
) or _billing_datetime_text(extra_hwid_valid_until)
|
||||
packages = tariff.hwid_device_packages
|
||||
rub_counts = {int(package.count) for package in (packages.rub if packages else [])}
|
||||
default_currency = default_currency_key_for_settings(settings)
|
||||
default_currency_code = payment_currency_code(default_currency)
|
||||
if packages and hasattr(packages, "for_currency"):
|
||||
default_packages = packages.for_currency(default_currency)
|
||||
else:
|
||||
default_packages = getattr(packages, default_currency, []) if packages else []
|
||||
currency_counts = {
|
||||
int(package.count)
|
||||
for package in default_packages
|
||||
}
|
||||
stars_counts = {int(package.count) for package in (packages.stars if packages else [])}
|
||||
plans = []
|
||||
for count in sorted(rub_counts | stars_counts):
|
||||
rub_quote = (
|
||||
for count in sorted(currency_counts | stars_counts):
|
||||
currency_quote = (
|
||||
await subscription_service.quote_hwid_device_topup(
|
||||
session,
|
||||
user_id=user_id,
|
||||
device_count=count,
|
||||
tariff_key=tariff.key,
|
||||
renewal=renewal_available,
|
||||
currency="rub",
|
||||
currency=default_currency,
|
||||
)
|
||||
if count in rub_counts
|
||||
if count in currency_counts
|
||||
else None
|
||||
)
|
||||
stars_quote = (
|
||||
@@ -684,7 +702,7 @@ async def device_topup_options_route(request: web.Request) -> web.Response:
|
||||
if count in stars_counts
|
||||
else None
|
||||
)
|
||||
if not rub_quote and not stars_quote:
|
||||
if not currency_quote and not stars_quote:
|
||||
continue
|
||||
sale_mode_for_plan = "hwid_devices_renewal" if renewal_available else "hwid_devices"
|
||||
plan = {
|
||||
@@ -695,13 +713,19 @@ async def device_topup_options_route(request: web.Request) -> web.Response:
|
||||
"sale_mode": sale_mode_for_plan,
|
||||
"months": count,
|
||||
"device_count": count,
|
||||
"price": float(rub_quote.get("price") if rub_quote else 0),
|
||||
"currency": settings.DEFAULT_CURRENCY_SYMBOL or "RUB",
|
||||
"price": float(currency_quote.get("price") if currency_quote else 0),
|
||||
"currency": default_currency_code,
|
||||
"title": f"+{count}",
|
||||
"subtitle": tariff.name(lang),
|
||||
"valid_from": _billing_iso_datetime((rub_quote or stars_quote).get("valid_from")),
|
||||
"valid_until": _billing_iso_datetime((rub_quote or stars_quote).get("valid_until")),
|
||||
"proration_ratio": float((rub_quote or stars_quote).get("proration_ratio") or 0),
|
||||
"valid_from": _billing_iso_datetime(
|
||||
(currency_quote or stars_quote).get("valid_from")
|
||||
),
|
||||
"valid_until": _billing_iso_datetime(
|
||||
(currency_quote or stars_quote).get("valid_until")
|
||||
),
|
||||
"proration_ratio": float(
|
||||
(currency_quote or stars_quote).get("proration_ratio") or 0
|
||||
),
|
||||
}
|
||||
if stars_quote and int(stars_quote.get("price") or 0) > 0:
|
||||
plan["stars_price"] = int(stars_quote["price"])
|
||||
@@ -929,12 +953,14 @@ async def _create_subscription_payment(
|
||||
price: float,
|
||||
stars_price: Optional[int],
|
||||
lang: str,
|
||||
currency: Optional[str] = None,
|
||||
sale_mode: str = "subscription",
|
||||
traffic_gb: Optional[float] = None,
|
||||
is_admin: bool = False,
|
||||
hwid_quote: Optional[Dict[str, Any]] = None,
|
||||
) -> web.Response:
|
||||
settings: Settings = request.app["settings"]
|
||||
payment_currency = (currency or default_payment_currency_code_for_settings(settings)).upper()
|
||||
sale_mode = str(sale_mode or "subscription")
|
||||
traffic_sale = _sale_mode_is_traffic(sale_mode)
|
||||
hwid_devices_sale = _sale_mode_is_hwid_devices(sale_mode)
|
||||
@@ -958,6 +984,17 @@ async def _create_subscription_payment(
|
||||
provider_spec.is_service_configured(request.app),
|
||||
)
|
||||
return _json_error(400, "payment_unavailable", "Payment method unavailable")
|
||||
if not provider_spec.is_usable_for_payment_currency(settings, payment_currency):
|
||||
logger.warning(
|
||||
"WebApp payment method does not support currency: method=%s currency=%s",
|
||||
method,
|
||||
payment_currency,
|
||||
)
|
||||
return _json_error(
|
||||
400,
|
||||
"unsupported_currency",
|
||||
"Payment method does not support this currency",
|
||||
)
|
||||
return await provider_spec.create_webapp_payment(
|
||||
WebAppPaymentContext(
|
||||
request=request,
|
||||
@@ -967,6 +1004,7 @@ async def _create_subscription_payment(
|
||||
months=months,
|
||||
price=price,
|
||||
stars_price=stars_price,
|
||||
currency=payment_currency,
|
||||
description=description,
|
||||
sale_mode=sale_mode,
|
||||
traffic_gb=traffic_gb,
|
||||
|
||||
@@ -472,6 +472,8 @@ def _serialize_plans(
|
||||
) -> List[Dict[str, Any]]:
|
||||
tariffs_config = settings.tariffs_config
|
||||
if tariffs_config:
|
||||
default_currency = default_currency_key_for_settings(settings)
|
||||
default_currency_code = payment_currency_code(default_currency)
|
||||
plans: List[Dict[str, Any]] = []
|
||||
for tariff in tariffs_config.enabled_tariffs:
|
||||
common = {
|
||||
@@ -481,7 +483,7 @@ def _serialize_plans(
|
||||
"billing_model": tariff.billing_model,
|
||||
"description": tariff.description(lang),
|
||||
"squad_uuids": tariff.squad_uuids,
|
||||
"currency": settings.DEFAULT_CURRENCY_SYMBOL or "RUB",
|
||||
"currency": default_currency_code,
|
||||
"hwid_device_limit": tariff.hwid_device_limit,
|
||||
"hwid_device_packages": _serialize_hwid_device_packages(
|
||||
settings,
|
||||
@@ -494,7 +496,7 @@ def _serialize_plans(
|
||||
}
|
||||
if tariff.billing_model == "period":
|
||||
for months in sorted(tariff.enabled_periods):
|
||||
price = tariff.period_price(int(months), "rub")
|
||||
price = tariff.period_price(int(months), default_currency)
|
||||
stars_price = tariff.period_price(int(months), "stars")
|
||||
if price is None and (stars_price is None or int(stars_price) <= 0):
|
||||
continue
|
||||
@@ -512,9 +514,13 @@ def _serialize_plans(
|
||||
plan["stars_price"] = int(stars_price)
|
||||
plans.append(plan)
|
||||
else:
|
||||
rub_packages = {
|
||||
currency_packages = {
|
||||
float(package.gb): float(package.price)
|
||||
for package in (tariff.traffic_packages.rub if tariff.traffic_packages else [])
|
||||
for package in (
|
||||
tariff.traffic_packages.for_currency(default_currency)
|
||||
if tariff.traffic_packages
|
||||
else []
|
||||
)
|
||||
}
|
||||
stars_packages = {
|
||||
float(package.gb): int(float(package.price))
|
||||
@@ -522,8 +528,8 @@ def _serialize_plans(
|
||||
tariff.traffic_packages.stars if tariff.traffic_packages else []
|
||||
)
|
||||
}
|
||||
for traffic_gb in sorted(set(rub_packages) | set(stars_packages)):
|
||||
price = rub_packages.get(traffic_gb)
|
||||
for traffic_gb in sorted(set(currency_packages) | set(stars_packages)):
|
||||
price = currency_packages.get(traffic_gb)
|
||||
stars_price = stars_packages.get(traffic_gb)
|
||||
if price is None and (stars_price is None or int(stars_price) <= 0):
|
||||
continue
|
||||
@@ -609,16 +615,19 @@ def _serialize_topup_packages(
|
||||
sale_mode: str = "topup",
|
||||
title_prefix: str = "",
|
||||
) -> List[Dict[str, Any]]:
|
||||
rub_packages = {
|
||||
float(package.gb): float(package.price) for package in (packages.rub if packages else [])
|
||||
default_currency = default_currency_key_for_settings(settings)
|
||||
default_currency_code = payment_currency_code(default_currency)
|
||||
currency_packages = {
|
||||
float(package.gb): float(package.price)
|
||||
for package in (packages.for_currency(default_currency) if packages else [])
|
||||
}
|
||||
stars_packages = {
|
||||
float(package.gb): int(float(package.price))
|
||||
for package in (packages.stars if packages else [])
|
||||
}
|
||||
plans: List[Dict[str, Any]] = []
|
||||
for traffic_gb in sorted(set(rub_packages) | set(stars_packages)):
|
||||
price = rub_packages.get(traffic_gb)
|
||||
for traffic_gb in sorted(set(currency_packages) | set(stars_packages)):
|
||||
price = currency_packages.get(traffic_gb)
|
||||
stars_price = stars_packages.get(traffic_gb)
|
||||
if price is None and (stars_price is None or int(stars_price) <= 0):
|
||||
continue
|
||||
@@ -632,7 +641,7 @@ def _serialize_topup_packages(
|
||||
"months": int(traffic_value) if traffic_value.is_integer() else traffic_value,
|
||||
"traffic_gb": traffic_value,
|
||||
"price": float(price or 0),
|
||||
"currency": settings.DEFAULT_CURRENCY_SYMBOL or "RUB",
|
||||
"currency": default_currency_code,
|
||||
"title": f"{title_prefix}{_format_traffic_title(traffic_value, lang)}",
|
||||
"subtitle": tariff.premium_name(lang)
|
||||
if sale_mode == "premium_topup"
|
||||
@@ -650,16 +659,19 @@ def _serialize_hwid_device_packages(
|
||||
packages: Optional[Any],
|
||||
lang: str,
|
||||
) -> List[Dict[str, Any]]:
|
||||
rub_packages = {
|
||||
int(package.count): float(package.price) for package in (packages.rub if packages else [])
|
||||
default_currency = default_currency_key_for_settings(settings)
|
||||
default_currency_code = payment_currency_code(default_currency)
|
||||
currency_packages = {
|
||||
int(package.count): float(package.price)
|
||||
for package in (packages.for_currency(default_currency) if packages else [])
|
||||
}
|
||||
stars_packages = {
|
||||
int(package.count): int(float(package.price))
|
||||
for package in (packages.stars if packages else [])
|
||||
}
|
||||
plans: List[Dict[str, Any]] = []
|
||||
for count in sorted(set(rub_packages) | set(stars_packages)):
|
||||
price = rub_packages.get(count)
|
||||
for count in sorted(set(currency_packages) | set(stars_packages)):
|
||||
price = currency_packages.get(count)
|
||||
stars_price = stars_packages.get(count)
|
||||
if price is None and (stars_price is None or int(stars_price) <= 0):
|
||||
continue
|
||||
@@ -672,7 +684,7 @@ def _serialize_hwid_device_packages(
|
||||
"months": int(count),
|
||||
"device_count": int(count),
|
||||
"price": float(price or 0),
|
||||
"currency": settings.DEFAULT_CURRENCY_SYMBOL or "RUB",
|
||||
"currency": default_currency_code,
|
||||
"title": f"+{count}",
|
||||
"subtitle": tariff.name(lang),
|
||||
}
|
||||
@@ -689,6 +701,8 @@ def _serialize_tariff_change_target(
|
||||
options: Dict[str, Any],
|
||||
lang: str,
|
||||
) -> Dict[str, Any]:
|
||||
default_currency = default_currency_key_for_settings(settings)
|
||||
default_currency_code = payment_currency_code(default_currency)
|
||||
actions: List[Dict[str, Any]] = []
|
||||
mode = str(options.get("mode") or "")
|
||||
if mode == "period_to_period":
|
||||
@@ -711,7 +725,7 @@ def _serialize_tariff_change_target(
|
||||
"kind": "payment",
|
||||
"title": "paid_diff",
|
||||
"price": paid_diff,
|
||||
"currency": settings.DEFAULT_CURRENCY_SYMBOL or "RUB",
|
||||
"currency": default_currency_code,
|
||||
}
|
||||
)
|
||||
elif mode == "period_to_traffic":
|
||||
@@ -733,13 +747,17 @@ def _serialize_tariff_change_target(
|
||||
"title": f"+{package.gb:g} GB",
|
||||
"traffic_gb": float(package.gb),
|
||||
"price": float(package.price),
|
||||
"currency": settings.DEFAULT_CURRENCY_SYMBOL or "RUB",
|
||||
"currency": default_currency_code,
|
||||
}
|
||||
for package in (tariff.traffic_packages.rub if tariff.traffic_packages else [])
|
||||
for package in (
|
||||
tariff.traffic_packages.for_currency(default_currency)
|
||||
if tariff.traffic_packages
|
||||
else []
|
||||
)
|
||||
)
|
||||
else:
|
||||
for months in tariff.enabled_periods:
|
||||
price = tariff.period_price(int(months), "rub")
|
||||
price = tariff.period_price(int(months), default_currency)
|
||||
if price:
|
||||
actions.append(
|
||||
{
|
||||
@@ -748,7 +766,7 @@ def _serialize_tariff_change_target(
|
||||
"months": int(months),
|
||||
"title": _format_months_title(int(months), lang),
|
||||
"price": float(price),
|
||||
"currency": settings.DEFAULT_CURRENCY_SYMBOL or "RUB",
|
||||
"currency": default_currency_code,
|
||||
}
|
||||
)
|
||||
return {
|
||||
@@ -772,10 +790,15 @@ def _serialize_payment_methods(
|
||||
from bot.payment_providers import get_provider_spec, resolve_provider_presentation
|
||||
|
||||
methods: List[Dict[str, Any]] = []
|
||||
payment_currency = default_payment_currency_code_for_settings(settings)
|
||||
for method in settings.payment_methods_order:
|
||||
method = method.lower()
|
||||
spec = get_provider_spec(method)
|
||||
if spec and spec.is_visible_for_user(settings, app, is_admin=is_admin):
|
||||
if (
|
||||
spec
|
||||
and spec.is_visible_for_user(settings, app, is_admin=is_admin)
|
||||
and spec.is_usable_for_payment_currency(settings, payment_currency)
|
||||
):
|
||||
presentation = resolve_provider_presentation(spec, settings, language=lang)
|
||||
methods.append(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user