import os from yuxi.channel.extensions.alipay.types import AlipayAccount, AlipayDmPolicy, AlipayMode class AlipayConfig: ENV_MAP = { "app_id": "ALIPAY_APP_ID", "app_private_key": "ALIPAY_PRIVATE_KEY", "alipay_public_key": "ALIPAY_PUBLIC_KEY", "aes_key": "ALIPAY_AES_KEY", "mode": "ALIPAY_MODE", "dm_policy": "ALIPAY_DM_POLICY", "subscribe_msg": "ALIPAY_SUBSCRIBE_MSG", "remove_markdown": "ALIPAY_REMOVE_MD", } def list_account_ids(self, config: dict | None = None) -> list[str]: if self._resolve_app_id(): return ["default"] return [] def resolve_account(self, account_id: str = "default") -> AlipayAccount: app_id = self._resolve_app_id() return AlipayAccount( account_id=account_id, app_id=app_id or "", app_private_key=self._env_or_config("app_private_key") or "", alipay_public_key=self._env_or_config("alipay_public_key") or "", aes_key=self._env_or_config("aes_key"), mode=AlipayMode(self._env_or_config("mode") or "production"), gateway_url=( "https://openapi.alipaydev.com/gateway.do" if self._env_or_config("mode") == "sandbox" else "https://openapi.alipay.com/gateway.do" ), name=self._env_or_config("name") or "", enabled=self._env_or_config("enabled") != "false", dm_policy=AlipayDmPolicy(self._env_or_config("dm_policy") or "open"), allow_from=[], remove_markdown=self._env_or_config("remove_markdown") != "false", subscribe_msg=self._env_or_config("subscribe_msg") or "", ) def is_configured(self, account: dict | None = None) -> bool: return bool( self._resolve_app_id() and self._env_or_config("app_private_key") and self._env_or_config("alipay_public_key") ) def _resolve_app_id(self) -> str | None: return self._env_or_config("app_id") @staticmethod def _env_or_config(key: str) -> str | None: env_key = AlipayConfig.ENV_MAP.get(key, "") if env_key: val = os.getenv(env_key) if val: return val return None def config_schema(self) -> dict: return { "$schema": "https://json-schema.org/draft-07/schema#", "type": "object", "title": "支付宝渠道配置", "properties": { "app_id": { "type": "string", "title": "AppID", "description": "支付宝开放平台应用 AppID", }, "app_private_key": { "type": "string", "title": "应用私钥", "x-ui-password": True, "description": "RSA2 应用私钥 (PEM 格式)", }, "alipay_public_key": { "type": "string", "title": "支付宝公钥", "description": "支付宝公钥 (PEM 格式)", }, "aes_key": { "type": "string", "title": "AES 解密密钥", "x-ui-password": True, "description": "AES 密钥(可选,16 字符)", }, "mode": { "type": "string", "enum": ["production", "sandbox"], "default": "production", "title": "运行模式", }, "dm_policy": { "type": "string", "enum": ["open", "pairing", "allowlist", "disabled"], "default": "open", "title": "DM 策略", }, "subscribe_msg": { "type": "string", "title": "关注欢迎语", "description": "用户关注生活号后自动回复的文本", }, "remove_markdown": { "type": "boolean", "default": True, "title": "移除 Markdown 格式", "description": "自动移除 AI 回复中的 Markdown 符号", }, }, "required": ["app_id", "app_private_key", "alipay_public_key"], } def is_enabled(self, account: dict | None = None, config: dict | None = None) -> bool: if account is None: return self._env_or_config("enabled") != "false" return account.get("enabled", True) def disabled_reason(self, account: dict | None = None, config: dict | None = None) -> str: if not account: return "未配置账户" if not account.get("enabled", True): return "账户已被禁用" return "" def unconfigured_reason(self, account: dict | None = None, config: dict | None = None) -> str: if not account: return "未配置账户" missing = [] if not account.get("app_id"): missing.append("AppId") if not account.get("app_private_key"): missing.append("应用私钥") if not account.get("alipay_public_key"): missing.append("支付宝公钥") return f"缺少: {', '.join(missing)}" if missing else "" def describe_account(self, account: dict | None = None, config: dict | None = None) -> dict: if not account: return {"account_id": ""} return { "account_id": account.get("account_id", "default"), "app_id": account.get("app_id", ""), "name": account.get("name", ""), "mode": account.get("mode", "production"), "dm_policy": account.get("dm_policy", "open"), "enabled": account.get("enabled", True), } def resolve_allow_from(self, config: dict, account_id: str | None = None) -> list[str] | None: account = self.resolve_account(account_id or "default") return account.allow_from or None def format_allow_from(self, config: dict, account_id: str | None, allow_from: list) -> list[str]: return [str(item) for item in allow_from] def has_configured_state(self, config: dict | None = None) -> bool: return self.is_configured() def has_persisted_auth_state(self, config: dict | None = None) -> bool: return bool(self._resolve_app_id() and self._env_or_config("app_private_key")) def default_account_id(self, config: dict | None = None) -> str: return "default"