"""Сборка платёжных клиентов и публичный payload для админки.""" from __future__ import annotations from typing import Any from config import Settings, get_settings from services.anypay import Anypay from services.cryptopay import CryptoPay from services.digiseller import Digiseller from services.env_file import ( BOOL_ENV_KEYS, PAYMENT_ENV_KEYS, SECRET_ENV_KEYS, env_file_path, env_writable, mask_secret, upsert_env_keys, ) from services.heleket import Heleket from services.lava import Lava def build_payment_clients(settings: Settings) -> dict[str, Any]: return { "cryptopay": CryptoPay( settings.crypto_bot_token, testnet=settings.crypto_pay_testnet, ), "heleket": Heleket( settings.heleket_merchant_id, settings.heleket_api_key, callback_url=settings.heleket_callback_url, currency=settings.heleket_currency, ), "digiseller": Digiseller( settings.digiseller_seller_id, settings.digiseller_api_key, product_ids=settings.digiseller_product_ids, pay_url=settings.digiseller_pay_url, credit_from=settings.digiseller_credit_from, ), "lava": Lava( settings.lava_shop_id, settings.lava_secret_key, additional_key=settings.lava_additional_key, hook_url=settings.lava_hook_url, success_url=settings.lava_success_url, fail_url=settings.lava_fail_url, expire_minutes=settings.lava_expire_minutes, ), "anypay": Anypay( settings.anypay_api_id, settings.anypay_api_key, settings.anypay_project_id, currency=settings.anypay_currency, method=settings.anypay_method, success_url=settings.anypay_success_url, fail_url=settings.anypay_fail_url, default_email=settings.anypay_email or "pay@anypay.local", check_ip=settings.anypay_check_ip, ), } def apply_payment_runtime(app: Any, settings: Settings) -> dict[str, Any]: clients = build_payment_clients(settings) app["settings"] = settings app["cryptopay"] = clients["cryptopay"] app["heleket"] = clients["heleket"] app["digiseller"] = clients["digiseller"] app["lava"] = clients["lava"] app["anypay"] = clients["anypay"] inject = app.get("inject") if inject is not None: inject.settings = settings inject.cryptopay = clients["cryptopay"] inject.heleket = clients["heleket"] inject.digiseller = clients["digiseller"] inject.lava = clients["lava"] inject.anypay = clients["anypay"] return clients def payments_public_dict(settings: Settings) -> dict[str, Any]: path = env_file_path() return { "env_path": str(path), "env_writable": env_writable(path), "crypto": { "enabled": bool(settings.crypto_bot_token), "token_set": bool(settings.crypto_bot_token), "token_hint": mask_secret(settings.crypto_bot_token), "testnet": bool(settings.crypto_pay_testnet), }, "heleket": { "enabled": bool(settings.heleket_merchant_id and settings.heleket_api_key), "merchant_id": settings.heleket_merchant_id, "api_key_set": bool(settings.heleket_api_key), "api_key_hint": mask_secret(settings.heleket_api_key), "callback_url": settings.heleket_callback_url, "currency": settings.heleket_currency or "RUB", }, "lava": { "enabled": bool(settings.lava_shop_id and settings.lava_secret_key), "shop_id": settings.lava_shop_id, "secret_key_set": bool(settings.lava_secret_key), "secret_key_hint": mask_secret(settings.lava_secret_key), "additional_key_set": bool(settings.lava_additional_key), "additional_key_hint": mask_secret(settings.lava_additional_key), "hook_url": settings.lava_hook_url, "success_url": settings.lava_success_url, "fail_url": settings.lava_fail_url, "expire_minutes": settings.lava_expire_minutes, }, "anypay": { "enabled": bool( settings.anypay_api_id and settings.anypay_api_key and settings.anypay_project_id ), "api_id": settings.anypay_api_id, "api_key_set": bool(settings.anypay_api_key), "api_key_hint": mask_secret(settings.anypay_api_key), "project_id": settings.anypay_project_id, "currency": settings.anypay_currency or "RUB", "method": settings.anypay_method, "success_url": settings.anypay_success_url, "fail_url": settings.anypay_fail_url, "email": settings.anypay_email, "check_ip": bool(settings.anypay_check_ip), "notify_url": ( f"{settings.webapp_url.rstrip('/')}/api/payments/anypay" if settings.webapp_url else "/api/payments/anypay" ), }, "digiseller": { "enabled": bool(settings.digiseller_seller_id and settings.digiseller_api_key), "seller_id": settings.digiseller_seller_id, "api_key_set": bool(settings.digiseller_api_key), "api_key_hint": mask_secret(settings.digiseller_api_key), "product_ids": ",".join(str(x) for x in settings.digiseller_product_ids), "pay_url": settings.digiseller_pay_url, "credit_from": settings.digiseller_credit_from or "cnt_goods", "callback_url": settings.digiseller_callback_url, }, "stars": { "enabled": bool(settings.stars_enabled), "rub_per_star": settings.stars_rub_per_star, }, "topup_code_pay_url": settings.topup_code_pay_url, } def _is_masked_placeholder(value: str) -> bool: v = (value or "").strip() if not v: return True return "…" in v or "•" in v or "..." in v def apply_payments_body(body: dict[str, Any]) -> Settings: """Применить тело POST админки: .env + os.environ + новый Settings.""" updates: dict[str, str | None] = {} mapping = { "CRYPTO_BOT_TOKEN": body.get("crypto_bot_token"), "CRYPTO_PAY_TESTNET": body.get("crypto_pay_testnet"), "HELEKET_MERCHANT_ID": body.get("heleket_merchant_id"), "HELEKET_API_KEY": body.get("heleket_api_key"), "HELEKET_CALLBACK_URL": body.get("heleket_callback_url"), "HELEKET_CURRENCY": body.get("heleket_currency"), "LAVA_SHOP_ID": body.get("lava_shop_id"), "LAVA_SECRET_KEY": body.get("lava_secret_key"), "LAVA_ADDITIONAL_KEY": body.get("lava_additional_key"), "LAVA_HOOK_URL": body.get("lava_hook_url"), "LAVA_SUCCESS_URL": body.get("lava_success_url"), "LAVA_FAIL_URL": body.get("lava_fail_url"), "LAVA_EXPIRE_MINUTES": body.get("lava_expire_minutes"), "ANYPAY_API_ID": body.get("anypay_api_id"), "ANYPAY_API_KEY": body.get("anypay_api_key"), "ANYPAY_PROJECT_ID": body.get("anypay_project_id"), "ANYPAY_CURRENCY": body.get("anypay_currency"), "ANYPAY_METHOD": body.get("anypay_method"), "ANYPAY_SUCCESS_URL": body.get("anypay_success_url"), "ANYPAY_FAIL_URL": body.get("anypay_fail_url"), "ANYPAY_EMAIL": body.get("anypay_email"), "ANYPAY_CHECK_IP": body.get("anypay_check_ip"), "DIGISELLER_SELLER_ID": body.get("digiseller_seller_id"), "DIGISELLER_API_KEY": body.get("digiseller_api_key"), "DIGISELLER_PRODUCT_IDS": body.get("digiseller_product_ids"), "DIGISELLER_PAY_URL": body.get("digiseller_pay_url"), "DIGISELLER_CREDIT_FROM": body.get("digiseller_credit_from"), "DIGISELLER_CALLBACK_URL": body.get("digiseller_callback_url"), "STARS_ENABLED": body.get("stars_enabled"), "STARS_RUB_PER_STAR": body.get("stars_rub_per_star"), "TOPUP_CODE_PAY_URL": body.get("topup_code_pay_url"), } for env_key, raw in mapping.items(): if env_key not in PAYMENT_ENV_KEYS: continue if raw is None: continue if env_key in BOOL_ENV_KEYS: if isinstance(raw, bool): updates[env_key] = "true" if raw else "false" else: updates[env_key] = ( "true" if str(raw).strip().lower() in {"1", "true", "yes", "on"} else "false" ) continue text = str(raw).strip() if env_key in SECRET_ENV_KEYS: if text == "__clear__": updates[env_key] = None elif _is_masked_placeholder(text): continue else: updates[env_key] = text continue updates[env_key] = text if text else None if updates: upsert_env_keys(updates) return get_settings(require_bot_token=False)