refactor: move freekassa env-config into module
This commit is contained in:
@@ -250,63 +250,6 @@ SETTINGS_MANIFEST: List[SettingField] = [
|
||||
subsection="YooKassa",
|
||||
),
|
||||
*_payment_presentation_fields("YOOKASSA", "YooKassa", default_icon="CreditCard"),
|
||||
# FreeKassa
|
||||
SettingField("FREEKASSA_ENABLED", "bool", "payments", "Включена", subsection="FreeKassa"),
|
||||
SettingField(
|
||||
"FREEKASSA_MERCHANT_ID", "string", "payments", "Merchant ID", subsection="FreeKassa"
|
||||
),
|
||||
SettingField(
|
||||
"FREEKASSA_FIRST_SECRET",
|
||||
"string",
|
||||
"payments",
|
||||
"First secret",
|
||||
subsection="FreeKassa",
|
||||
secret=True,
|
||||
),
|
||||
SettingField(
|
||||
"FREEKASSA_SECOND_SECRET",
|
||||
"string",
|
||||
"payments",
|
||||
"Second secret",
|
||||
"Используется для проверки подписи входящих уведомлений",
|
||||
subsection="FreeKassa",
|
||||
secret=True,
|
||||
),
|
||||
SettingField(
|
||||
"FREEKASSA_API_KEY", "string", "payments", "API key", subsection="FreeKassa", secret=True
|
||||
),
|
||||
SettingField(
|
||||
"FREEKASSA_PAYMENT_URL",
|
||||
"url",
|
||||
"payments",
|
||||
"Payment URL",
|
||||
placeholder="https://pay.freekassa.ru/",
|
||||
subsection="FreeKassa",
|
||||
),
|
||||
SettingField(
|
||||
"FREEKASSA_PAYMENT_METHOD_ID",
|
||||
"int",
|
||||
"payments",
|
||||
"Метод оплаты по умолчанию",
|
||||
subsection="FreeKassa",
|
||||
),
|
||||
SettingField(
|
||||
"FREEKASSA_PAYMENT_IP",
|
||||
"string",
|
||||
"payments",
|
||||
"IP сервера",
|
||||
"Передаётся в подпись запроса при создании платежа",
|
||||
subsection="FreeKassa",
|
||||
),
|
||||
SettingField(
|
||||
"FREEKASSA_TRUSTED_IPS",
|
||||
"string",
|
||||
"payments",
|
||||
"Доверенные IP",
|
||||
"Через запятую — IP-адреса, с которых принимаются нотификации",
|
||||
subsection="FreeKassa",
|
||||
),
|
||||
*_payment_presentation_fields("FREEKASSA", "FreeKassa", default_icon="Smartphone"),
|
||||
# Platega
|
||||
SettingField("PLATEGA_ENABLED", "bool", "payments", "Включена", subsection="Platega"),
|
||||
SettingField(
|
||||
|
||||
@@ -59,14 +59,11 @@ async def select_subscription_period_callback_handler(
|
||||
|
||||
if price_rub is None:
|
||||
if traffic_mode and not price_source and stars_price is not None:
|
||||
from bot.payment_providers import iter_provider_specs
|
||||
|
||||
currency_methods_enabled = any(
|
||||
[
|
||||
settings.FREEKASSA_ENABLED,
|
||||
settings.PLATEGA_ENABLED,
|
||||
settings.SEVERPAY_ENABLED,
|
||||
settings.YOOKASSA_ENABLED,
|
||||
settings.CRYPTOPAY_ENABLED,
|
||||
]
|
||||
spec.price_source != "stars" and spec.is_enabled(settings)
|
||||
for spec in iter_provider_specs()
|
||||
)
|
||||
if currency_methods_enabled:
|
||||
logging.error(
|
||||
|
||||
@@ -10,6 +10,8 @@ from urllib.parse import parse_qsl
|
||||
|
||||
from aiogram import Bot, F, Router, types
|
||||
from aiohttp import web
|
||||
from pydantic import Field, field_validator
|
||||
from pydantic_settings import SettingsConfigDict
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
@@ -22,6 +24,8 @@ from db.dal import payment_dal
|
||||
|
||||
from .base import (
|
||||
PaymentProviderSpec,
|
||||
ProviderEnvConfig,
|
||||
ProviderManifestField,
|
||||
ServiceFactoryContext,
|
||||
WebAppPaymentContext,
|
||||
)
|
||||
@@ -50,12 +54,77 @@ from .shared import (
|
||||
_LOG = "freekassa"
|
||||
|
||||
|
||||
class FreeKassaConfig(ProviderEnvConfig):
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
env_prefix="FREEKASSA_",
|
||||
extra="ignore",
|
||||
)
|
||||
|
||||
ENABLED: bool = Field(default=False)
|
||||
MERCHANT_ID: Optional[str] = None
|
||||
FIRST_SECRET: Optional[str] = None
|
||||
SECOND_SECRET: Optional[str] = None
|
||||
PAYMENT_URL: str = Field(default="https://pay.freekassa.ru/")
|
||||
API_KEY: Optional[str] = None
|
||||
PAYMENT_IP: Optional[str] = None
|
||||
PAYMENT_METHOD_ID: Optional[int] = None
|
||||
TRUSTED_IPS: str = Field(
|
||||
default="168.119.157.136,168.119.60.227,178.154.197.79,51.250.54.238"
|
||||
)
|
||||
|
||||
@field_validator("PAYMENT_METHOD_ID", mode="before")
|
||||
@classmethod
|
||||
def _empty_to_none_int(cls, v):
|
||||
if isinstance(v, str):
|
||||
v = v.strip()
|
||||
if not v:
|
||||
return None
|
||||
return v
|
||||
|
||||
@field_validator(
|
||||
"MERCHANT_ID", "FIRST_SECRET", "SECOND_SECRET", "API_KEY", "PAYMENT_IP",
|
||||
mode="before",
|
||||
)
|
||||
@classmethod
|
||||
def _strip_optional(cls, v):
|
||||
if isinstance(v, str) and not v.strip():
|
||||
return None
|
||||
return v
|
||||
|
||||
@property
|
||||
def webhook_path(self) -> str:
|
||||
return "/webhook/freekassa"
|
||||
|
||||
@property
|
||||
def trusted_ips_list(self) -> list:
|
||||
return [item.strip() for item in (self.TRUSTED_IPS or "").split(",") if item.strip()]
|
||||
|
||||
|
||||
class FreeKassaPresentation(ProviderEnvConfig):
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
env_prefix="PAYMENT_FREEKASSA_",
|
||||
extra="ignore",
|
||||
)
|
||||
|
||||
WEBAPP_LABEL_RU: Optional[str] = None
|
||||
WEBAPP_LABEL_EN: Optional[str] = None
|
||||
WEBAPP_ICON: Optional[str] = None
|
||||
TELEGRAM_LABEL_RU: Optional[str] = None
|
||||
TELEGRAM_LABEL_EN: Optional[str] = None
|
||||
TELEGRAM_EMOJI: Optional[str] = None
|
||||
|
||||
|
||||
class FreeKassaService(HttpClientMixin):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
bot: Bot,
|
||||
settings: Settings,
|
||||
config: FreeKassaConfig,
|
||||
i18n: JsonI18n,
|
||||
async_session_factory: sessionmaker,
|
||||
subscription_service: SubscriptionService,
|
||||
@@ -63,29 +132,30 @@ class FreeKassaService(HttpClientMixin):
|
||||
):
|
||||
self.bot = bot
|
||||
self.settings = settings
|
||||
self.config = config
|
||||
self.i18n = i18n
|
||||
self.async_session_factory = async_session_factory
|
||||
self.subscription_service = subscription_service
|
||||
self.referral_service = referral_service
|
||||
|
||||
self.shop_id: Optional[str] = settings.FREEKASSA_MERCHANT_ID
|
||||
self.api_key: Optional[str] = settings.FREEKASSA_API_KEY
|
||||
self.second_secret: Optional[str] = settings.FREEKASSA_SECOND_SECRET
|
||||
self.shop_id: Optional[str] = config.MERCHANT_ID
|
||||
self.api_key: Optional[str] = config.API_KEY
|
||||
self.second_secret: Optional[str] = config.SECOND_SECRET
|
||||
self.default_currency: str = (settings.DEFAULT_CURRENCY_SYMBOL or "RUB").upper()
|
||||
self.server_ip: Optional[str] = settings.FREEKASSA_PAYMENT_IP
|
||||
self.payment_method_id: Optional[int] = settings.FREEKASSA_PAYMENT_METHOD_ID
|
||||
self.server_ip: Optional[str] = config.PAYMENT_IP
|
||||
self.payment_method_id: Optional[int] = config.PAYMENT_METHOD_ID
|
||||
|
||||
self.api_base_url: str = "https://api.fk.life/v1"
|
||||
self._init_http_client(total_timeout=15)
|
||||
self._nonce_lock = asyncio.Lock()
|
||||
self._last_nonce = int(time.time() * 1000)
|
||||
|
||||
self.configured: bool = bool(settings.FREEKASSA_ENABLED and self.shop_id and self.api_key)
|
||||
self.configured: bool = bool(config.ENABLED and self.shop_id and self.api_key)
|
||||
if not self.configured:
|
||||
logging.warning(
|
||||
"FreeKassaService initialized but not fully configured. Payments disabled."
|
||||
)
|
||||
if settings.FREEKASSA_ENABLED and not self.server_ip:
|
||||
if config.ENABLED and not self.server_ip:
|
||||
logging.warning(
|
||||
"FreeKassaService: FREEKASSA_PAYMENT_IP is not set. Requests may be rejected by the provider." # noqa: E501
|
||||
)
|
||||
@@ -185,7 +255,7 @@ class FreeKassaService(HttpClientMixin):
|
||||
|
||||
try:
|
||||
client_ip = request_client_ip(request, trusted_proxies=self.settings.trusted_proxies)
|
||||
if not ip_in_allowlist(client_ip, self.settings.freekassa_trusted_ips):
|
||||
if not ip_in_allowlist(client_ip, self.config.trusted_ips_list):
|
||||
return web.Response(status=403)
|
||||
|
||||
raw_body = await request.read()
|
||||
@@ -431,9 +501,12 @@ async def pay_fk_callback_handler(
|
||||
|
||||
|
||||
def create_service(ctx: ServiceFactoryContext) -> FreeKassaService:
|
||||
bundle = ctx.config_for("freekassa_service")
|
||||
config = bundle.config if bundle and isinstance(bundle.config, FreeKassaConfig) else FreeKassaConfig()
|
||||
return FreeKassaService(
|
||||
bot=ctx.bot,
|
||||
settings=ctx.settings,
|
||||
config=config,
|
||||
i18n=ctx.i18n,
|
||||
async_session_factory=ctx.async_session_factory,
|
||||
subscription_service=ctx.subscription_service,
|
||||
@@ -479,6 +552,56 @@ async def create_webapp_payment(ctx: WebAppPaymentContext) -> web.Response:
|
||||
)
|
||||
|
||||
|
||||
_PRESENTATION_MANIFEST = tuple(
|
||||
ProviderManifestField(
|
||||
key=key, type=type_, label=label, description=description,
|
||||
placeholder=placeholder, subsection="FreeKassa",
|
||||
target="presentation", attr=attr,
|
||||
)
|
||||
for key, type_, label, description, placeholder, attr in (
|
||||
("PAYMENT_FREEKASSA_WEBAPP_LABEL_RU", "string", "WebApp button text (RU)",
|
||||
"Custom Russian text shown in the Web App payment method button.", "", "WEBAPP_LABEL_RU"),
|
||||
("PAYMENT_FREEKASSA_WEBAPP_LABEL_EN", "string", "WebApp button text (EN)",
|
||||
"Custom English text shown in the Web App payment method button.", "", "WEBAPP_LABEL_EN"),
|
||||
("PAYMENT_FREEKASSA_WEBAPP_ICON", "icon", "WebApp button icon",
|
||||
"Lucide icon name rendered inside the Web App payment method button.",
|
||||
"Smartphone", "WEBAPP_ICON"),
|
||||
("PAYMENT_FREEKASSA_TELEGRAM_LABEL_RU", "string", "Telegram button text (RU)",
|
||||
"Custom Russian text shown in Telegram bot payment buttons.", "", "TELEGRAM_LABEL_RU"),
|
||||
("PAYMENT_FREEKASSA_TELEGRAM_LABEL_EN", "string", "Telegram button text (EN)",
|
||||
"Custom English text shown in Telegram bot payment buttons.", "", "TELEGRAM_LABEL_EN"),
|
||||
("PAYMENT_FREEKASSA_TELEGRAM_EMOJI", "string", "Telegram button emoji",
|
||||
"Emoji prepended to the Telegram bot payment button when customized.",
|
||||
"📱", "TELEGRAM_EMOJI"),
|
||||
)
|
||||
)
|
||||
|
||||
_CONFIG_MANIFEST = (
|
||||
ProviderManifestField("FREEKASSA_ENABLED", "bool", "Включена",
|
||||
subsection="FreeKassa", attr="ENABLED"),
|
||||
ProviderManifestField("FREEKASSA_MERCHANT_ID", "string", "Merchant ID",
|
||||
subsection="FreeKassa", attr="MERCHANT_ID"),
|
||||
ProviderManifestField("FREEKASSA_FIRST_SECRET", "string", "First secret",
|
||||
subsection="FreeKassa", secret=True, attr="FIRST_SECRET"),
|
||||
ProviderManifestField("FREEKASSA_SECOND_SECRET", "string", "Second secret",
|
||||
subsection="FreeKassa", secret=True, attr="SECOND_SECRET"),
|
||||
ProviderManifestField("FREEKASSA_API_KEY", "string", "API key",
|
||||
subsection="FreeKassa", secret=True, attr="API_KEY"),
|
||||
ProviderManifestField("FREEKASSA_PAYMENT_URL", "url", "Payment URL",
|
||||
placeholder="https://pay.freekassa.ru/",
|
||||
subsection="FreeKassa", attr="PAYMENT_URL"),
|
||||
ProviderManifestField("FREEKASSA_PAYMENT_METHOD_ID", "int", "Payment method ID",
|
||||
description="See https://merchant.freekassa.net/settings/currencies",
|
||||
subsection="FreeKassa", attr="PAYMENT_METHOD_ID"),
|
||||
ProviderManifestField("FREEKASSA_PAYMENT_IP", "string", "Server IP",
|
||||
description="Public IP address reported to FreeKassa.",
|
||||
subsection="FreeKassa", attr="PAYMENT_IP"),
|
||||
ProviderManifestField("FREEKASSA_TRUSTED_IPS", "string", "Trusted IPs",
|
||||
description="Comma-separated IP addresses accepted for FreeKassa webhooks.",
|
||||
subsection="FreeKassa", attr="TRUSTED_IPS"),
|
||||
)
|
||||
|
||||
|
||||
SPEC = PaymentProviderSpec(
|
||||
id="freekassa",
|
||||
provider_key="freekassa",
|
||||
@@ -489,12 +612,15 @@ SPEC = PaymentProviderSpec(
|
||||
telegram_labels={"ru": "СБП", "en": "SBP"},
|
||||
telegram_emoji="📱",
|
||||
pending_status="pending_freekassa",
|
||||
enabled=lambda settings: settings.FREEKASSA_ENABLED,
|
||||
enabled=lambda config: bool(getattr(config, "ENABLED", False)),
|
||||
service_key="freekassa_service",
|
||||
callback_prefix="pay_fk",
|
||||
router=router,
|
||||
create_service=create_service,
|
||||
webhook_path=lambda settings: settings.freekassa_webhook_path,
|
||||
webhook_path=lambda source: "/webhook/freekassa",
|
||||
webhook_route=freekassa_webhook_route,
|
||||
create_webapp_payment=create_webapp_payment,
|
||||
config_class=FreeKassaConfig,
|
||||
presentation_class=FreeKassaPresentation,
|
||||
manifest_fields=_CONFIG_MANIFEST + _PRESENTATION_MANIFEST,
|
||||
)
|
||||
|
||||
@@ -38,13 +38,6 @@ class PaymentSettings(BaseModel):
|
||||
yookassa_payment_subject: str
|
||||
yookassa_autopayments_enabled: bool
|
||||
yookassa_autopayments_require_card_binding: bool
|
||||
freekassa_enabled: bool
|
||||
freekassa_merchant_id: Optional[str]
|
||||
freekassa_second_secret: Optional[str]
|
||||
freekassa_api_key: Optional[str]
|
||||
freekassa_payment_ip: Optional[str]
|
||||
freekassa_payment_method_id: Optional[int]
|
||||
freekassa_trusted_ips: List[str]
|
||||
platega_enabled: bool
|
||||
platega_base_url: str
|
||||
platega_merchant_id: Optional[str]
|
||||
@@ -226,31 +219,12 @@ class Settings(BaseSettings):
|
||||
PLATEGA_RETURN_URL: Optional[str] = Field(default=None)
|
||||
PLATEGA_FAILED_URL: Optional[str] = Field(default=None)
|
||||
|
||||
FREEKASSA_ENABLED: bool = Field(default=False)
|
||||
FREEKASSA_MERCHANT_ID: Optional[str] = None
|
||||
FREEKASSA_FIRST_SECRET: Optional[str] = None
|
||||
FREEKASSA_SECOND_SECRET: Optional[str] = None
|
||||
FREEKASSA_PAYMENT_URL: str = Field(default="https://pay.freekassa.ru/")
|
||||
FREEKASSA_API_KEY: Optional[str] = None
|
||||
FREEKASSA_PAYMENT_IP: Optional[str] = None
|
||||
FREEKASSA_PAYMENT_METHOD_ID: Optional[int] = None
|
||||
FREEKASSA_TRUSTED_IPS: str = Field(
|
||||
default="168.119.157.136,168.119.60.227,178.154.197.79,51.250.54.238",
|
||||
description="Comma-separated FreeKassa webhook IP allowlist.",
|
||||
)
|
||||
|
||||
YOOKASSA_ENABLED: bool = Field(default=True)
|
||||
STARS_ENABLED: bool = Field(default=True)
|
||||
PAYMENT_METHODS_ORDER: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Comma-separated list of payment methods to show (e.g., severpay,wata,freekassa,yookassa,platega,stars,cryptopay)", # noqa: E501
|
||||
)
|
||||
PAYMENT_FREEKASSA_WEBAPP_LABEL_RU: Optional[str] = None
|
||||
PAYMENT_FREEKASSA_WEBAPP_LABEL_EN: Optional[str] = None
|
||||
PAYMENT_FREEKASSA_WEBAPP_ICON: Optional[str] = None
|
||||
PAYMENT_FREEKASSA_TELEGRAM_LABEL_RU: Optional[str] = None
|
||||
PAYMENT_FREEKASSA_TELEGRAM_LABEL_EN: Optional[str] = None
|
||||
PAYMENT_FREEKASSA_TELEGRAM_EMOJI: Optional[str] = None
|
||||
PAYMENT_PLATEGA_SBP_WEBAPP_LABEL_RU: Optional[str] = None
|
||||
PAYMENT_PLATEGA_SBP_WEBAPP_LABEL_EN: Optional[str] = None
|
||||
PAYMENT_PLATEGA_SBP_WEBAPP_ICON: Optional[str] = None
|
||||
@@ -528,13 +502,6 @@ class Settings(BaseSettings):
|
||||
yookassa_payment_subject=self.YOOKASSA_PAYMENT_SUBJECT,
|
||||
yookassa_autopayments_enabled=self.YOOKASSA_AUTOPAYMENTS_ENABLED,
|
||||
yookassa_autopayments_require_card_binding=self.YOOKASSA_AUTOPAYMENTS_REQUIRE_CARD_BINDING,
|
||||
freekassa_enabled=self.FREEKASSA_ENABLED,
|
||||
freekassa_merchant_id=self.FREEKASSA_MERCHANT_ID,
|
||||
freekassa_second_secret=self.FREEKASSA_SECOND_SECRET,
|
||||
freekassa_api_key=self.FREEKASSA_API_KEY,
|
||||
freekassa_payment_ip=self.FREEKASSA_PAYMENT_IP,
|
||||
freekassa_payment_method_id=self.FREEKASSA_PAYMENT_METHOD_ID,
|
||||
freekassa_trusted_ips=self.freekassa_trusted_ips,
|
||||
platega_enabled=self.PLATEGA_ENABLED,
|
||||
platega_base_url=self.PLATEGA_BASE_URL,
|
||||
platega_merchant_id=self.PLATEGA_MERCHANT_ID,
|
||||
@@ -657,11 +624,6 @@ class Settings(BaseSettings):
|
||||
def trusted_proxies(self) -> List[str]:
|
||||
return _split_csv(self.TRUSTED_PROXIES)
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
def freekassa_trusted_ips(self) -> List[str]:
|
||||
return _split_csv(self.FREEKASSA_TRUSTED_IPS)
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
def telegram_webhook_path(self) -> str:
|
||||
@@ -707,18 +669,6 @@ class Settings(BaseSettings):
|
||||
return f"{base.rstrip('/')}{self.cryptopay_webhook_path}"
|
||||
return None
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
def freekassa_webhook_path(self) -> str:
|
||||
return "/webhook/freekassa"
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
def freekassa_full_webhook_url(self) -> Optional[str]:
|
||||
base = self.WEBHOOK_BASE_URL
|
||||
if base:
|
||||
return f"{base.rstrip('/')}{self.freekassa_webhook_path}"
|
||||
return None
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
@@ -1152,23 +1102,6 @@ def get_settings() -> Settings:
|
||||
logging.warning(
|
||||
"WARNING: LKNPD credentials are incomplete. Receipt sending will be disabled."
|
||||
)
|
||||
if _settings_instance.FREEKASSA_ENABLED:
|
||||
if (
|
||||
not _settings_instance.FREEKASSA_MERCHANT_ID
|
||||
or not _settings_instance.FREEKASSA_API_KEY
|
||||
):
|
||||
logging.warning(
|
||||
"CRITICAL: FreeKassa is enabled but SHOP_ID or API key is missing. FreeKassa payments will not work." # noqa: E501
|
||||
)
|
||||
if not _settings_instance.FREEKASSA_SECOND_SECRET:
|
||||
logging.warning(
|
||||
"WARNING: FreeKassa second secret is not set. Incoming payment notifications cannot be verified." # noqa: E501
|
||||
)
|
||||
if not _settings_instance.subscription_options:
|
||||
logging.warning(
|
||||
"CRITICAL: FreeKassa is enabled but no subscription prices are configured (RUB_PRICE_*). Users will not see payment buttons." # noqa: E501
|
||||
)
|
||||
|
||||
if _settings_instance.PLATEGA_ENABLED:
|
||||
if (
|
||||
not _settings_instance.PLATEGA_MERCHANT_ID
|
||||
|
||||
+12
-8
@@ -60,21 +60,25 @@ class RequestSecurityTests(unittest.IsolatedAsyncioTestCase):
|
||||
|
||||
class FreeKassaServiceTests(unittest.TestCase):
|
||||
def _make_service(self) -> FreeKassaService:
|
||||
from bot.payment_providers.freekassa import FreeKassaConfig
|
||||
|
||||
settings = SimpleNamespace(
|
||||
FREEKASSA_ENABLED=True,
|
||||
FREEKASSA_MERCHANT_ID="123456",
|
||||
FREEKASSA_API_KEY="api-key",
|
||||
FREEKASSA_SECOND_SECRET="second-secret",
|
||||
DEFAULT_CURRENCY_SYMBOL="RUB",
|
||||
FREEKASSA_PAYMENT_IP="203.0.113.10",
|
||||
FREEKASSA_PAYMENT_METHOD_ID=44,
|
||||
FREEKASSA_TRUSTED_IPS="127.0.0.1,203.0.113.0/24",
|
||||
trusted_proxies=["127.0.0.1"],
|
||||
freekassa_trusted_ips=["127.0.0.1", "203.0.113.0/24"],
|
||||
)
|
||||
config = FreeKassaConfig(
|
||||
ENABLED=True,
|
||||
MERCHANT_ID="123456",
|
||||
API_KEY="api-key",
|
||||
SECOND_SECRET="second-secret",
|
||||
PAYMENT_IP="203.0.113.10",
|
||||
PAYMENT_METHOD_ID=44,
|
||||
TRUSTED_IPS="127.0.0.1,203.0.113.0/24",
|
||||
)
|
||||
return FreeKassaService(
|
||||
bot=object(),
|
||||
settings=settings,
|
||||
config=config,
|
||||
i18n=object(),
|
||||
async_session_factory=object(),
|
||||
subscription_service=object(),
|
||||
|
||||
Reference in New Issue
Block a user