from __future__ import annotations import json import os from pathlib import Path from django.conf import settings from django.core.management.base import CommandError def _is_demo_data(value: str) -> bool: normalized = "".join(ch for ch in str(value).lower() if ch.isalnum()) return "demodata" in normalized def _is_dummy_payment_app(app_label: str) -> bool: normalized = str(app_label).lower().replace(":", ".") parts = [part for part in normalized.split(".") if part] return "payment_dummy" in parts or normalized == "payment_dummy" def _load_config_payload() -> dict: config_path = Path(__file__).resolve().parent / "ocyan.json" if not config_path.exists(): return {} with config_path.open("r", encoding="utf-8") as handle: return json.load(handle) def get_declared_plugins() -> list[str]: payload = _load_config_payload() return [str(plugin) for plugin in payload.get("ocyan_plugins", [])] def get_declared_payment_apps(installed_apps: list[str] | None = None) -> list[str]: declared_plugins = [ plugin for plugin in get_declared_plugins() if "payment" in plugin.lower() ] if declared_plugins: return declared_plugins installed_apps = installed_apps or list(settings.INSTALLED_APPS) return [app for app in installed_apps if "payment" in app.lower()] def get_checkout_apps() -> list[str]: return [app for app in settings.INSTALLED_APPS if "checkout" in app.lower()] def idea_marketplace_payments_enabled() -> bool: return bool(getattr(settings, "IDEA_MARKETPLACE_PAYMENTS_ENABLED", False)) def validate_payment_provider_config() -> None: installed_apps = list(settings.INSTALLED_APPS) payment_apps = get_declared_payment_apps(installed_apps) checkout_apps = get_checkout_apps() config_plugins = get_declared_plugins() if not idea_marketplace_payments_enabled(): if any(_is_dummy_payment_app(app) for app in config_plugins): raise CommandError( "Dummy payment app is declared in ocyan.json. Remove it even when payments are disabled." ) return if not payment_apps: raise CommandError("No payment app declared for this project.") if not checkout_apps: raise CommandError("No checkout app found in INSTALLED_APPS.") if not any("oscar_checkout" in app.lower() for app in checkout_apps): raise CommandError("Oscar checkout app is not active.") if any(_is_demo_data(app) for app in installed_apps): raise CommandError( "Demo data plugin detected in INSTALLED_APPS. Remove all demodata plugins before launch." ) if any(_is_dummy_payment_app(app) for app in config_plugins): raise CommandError( "Dummy payment app is declared in ocyan.json. Use a real provider plugin before production launch." ) if any("mollie" in app.lower() for app in payment_apps): mollie_settings = ( getattr(settings, "PAYMENT_MOLLIE", None) or getattr(settings, "payment_mollie", None) or {} ) config_key = str(mollie_settings.get("api_key", "")).strip() env_key = str(os.environ.get("MOLLIE_API_KEY", "")).strip() effective_key = env_key or config_key if not effective_key or effective_key.upper() == "CHANGE_ME": raise CommandError( "Mollie payment provider is enabled but no valid API key is configured. " "Set MOLLIE_API_KEY or settings.payment_mollie.api_key to a real key." ) if not effective_key.startswith("live_"): raise CommandError( "Mollie key must be a live key for production launch (expected prefix 'live_')." ) if config_plugins: if any(_is_demo_data(plugin) for plugin in config_plugins): raise CommandError( "Demo data plugin detected in ocyan.json. Remove it before launch." )