fix: defer payment provider init to load_unipay(), graceful failure
- Move env var reading and key file open() from import-time CONF dict into _build_provider_conf() called during load_unipay() - Each provider is wrapped in try/except — if env vars are missing, key files don't exist, or instantiation fails, the provider is set to None (disabled) and a warning is printed - Program continues to start even if all payment channels fail - Existing code already checks PROVIDERS[name] is None in all business functions (create_payment, notify handlers, etc.) - Existing get_provider() in notify.py already returns None on instantiation error
This commit is contained in:
parent
b40300ad35
commit
a688d3d562
102
unipay/init.py
102
unipay/init.py
@ -1,5 +1,6 @@
|
|||||||
# init.py
|
# init.py
|
||||||
import os
|
import os
|
||||||
|
import traceback
|
||||||
from appPublic.log import debug,exception
|
from appPublic.log import debug,exception
|
||||||
from ahserver.configuredServer import add_startup
|
from ahserver.configuredServer import add_startup
|
||||||
from ahserver.serverenv import ServerEnv
|
from ahserver.serverenv import ServerEnv
|
||||||
@ -7,40 +8,85 @@ from .notify import get_provider, get_provider_channel
|
|||||||
from .paylog import PaymentLog, unipay_accounting
|
from .paylog import PaymentLog, unipay_accounting
|
||||||
from .payfee import get_pay_fee, sor_get_pay_fee, get_paychannels, get_pay_feerate
|
from .payfee import get_pay_fee, sor_get_pay_fee, get_paychannels, get_pay_feerate
|
||||||
|
|
||||||
# 从 env 或配置载入 provider conf(这里只示例)
|
# ──────────────────────────────────────────────
|
||||||
CONF = {
|
# 延迟加载:只存储 env key 名称,不在此处打开文件
|
||||||
|
# 避免 import 阶段因文件缺失或 env 未设置而崩溃
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
CONF_ENV = {
|
||||||
"transfer": {
|
"transfer": {
|
||||||
"pop3server": os.getenv("POP3SERVER", ""),
|
"pop3server": "POP3SERVER",
|
||||||
"mail": os.getenv("MAIL", ""),
|
"mail": "MAIL",
|
||||||
"password": os.getenv("PASSWORD", ""),
|
"password": "PASSWORD",
|
||||||
"from_mail": os.getenv("FROM_MAIL", ""),
|
"from_mail": "FROM_MAIL",
|
||||||
"account_no": os.getenv("ACCOUNT_NO", "")
|
"account_no": "ACCOUNT_NO"
|
||||||
},
|
},
|
||||||
"wechat": {
|
"wechat": {
|
||||||
"mchid": os.getenv("WXP_MCHID",""),
|
"mchid": "WXP_MCHID",
|
||||||
"appid": os.getenv("WXP_APPID", ""),
|
"appid": "WXP_APPID",
|
||||||
"cert_serial_no": os.getenv("WXP_SERIAL",""),
|
"cert_serial_no": "WXP_SERIAL",
|
||||||
"private_key_pem": open(os.getenv("WXP_PRIVKEY","./merchant_private_key.pem"),"rb").read(),
|
"private_key_pem_file": "WXP_PRIVKEY",
|
||||||
"api_v3_key": os.getenv("WXP_API_V3_KEY","")
|
"api_v3_key": "WXP_API_V3_KEY"
|
||||||
},
|
},
|
||||||
"paypal": {
|
"paypal": {
|
||||||
"client_id": os.getenv("PP_ID",""),
|
"client_id": "PP_ID",
|
||||||
"client_secret": os.getenv("PP_SECRET",""),
|
"client_secret": "PP_SECRET",
|
||||||
"sandbox": True
|
"sandbox": True # 静态值
|
||||||
},
|
},
|
||||||
"alipay": {
|
"alipay": {
|
||||||
"app_id": os.getenv("ALIPAY_APPID",""),
|
"app_id": "ALIPAY_APPID",
|
||||||
"app_private_key_pem": open(os.getenv("ALIPAY_PRIV","./alipay_priv.pem"),"rb").read(),
|
"app_private_key_pem_file": "ALIPAY_PRIV",
|
||||||
"alipay_public_key_pem": open(os.getenv("ALIPAY_PUB",""),"rb").read()
|
"alipay_public_key_pem_file": "ALIPAY_PUB"
|
||||||
},
|
},
|
||||||
"stripe": {
|
"stripe": {
|
||||||
"api_key": os.getenv("STRIPE_KEY","")
|
"api_key": "STRIPE_KEY"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PROVIDERS = {}
|
PROVIDERS = {}
|
||||||
|
|
||||||
|
|
||||||
|
def _build_provider_conf(provider_name: str) -> dict:
|
||||||
|
"""从环境变量构造 provider 配置。打开文件、处理默认值。
|
||||||
|
如果关键环境变量缺失或文件打不开,抛异常由调用方处理。"""
|
||||||
|
conf_def = CONF_ENV.get(provider_name, {})
|
||||||
|
conf = {}
|
||||||
|
for key, env_name in conf_def.items():
|
||||||
|
if isinstance(env_name, bool):
|
||||||
|
conf[key] = env_name
|
||||||
|
continue
|
||||||
|
val = os.getenv(env_name, "")
|
||||||
|
conf[key] = val
|
||||||
|
|
||||||
|
# 特殊处理:读取密钥文件
|
||||||
|
if provider_name == "wechat":
|
||||||
|
privkey_path = conf.get("private_key_pem_file", "")
|
||||||
|
if not privkey_path:
|
||||||
|
raise FileNotFoundError(f"环境变量 WXP_PRIVKEY 未设置")
|
||||||
|
with open(privkey_path, "rb") as f:
|
||||||
|
conf["private_key_pem"] = f.read()
|
||||||
|
del conf["private_key_pem_file"]
|
||||||
|
|
||||||
|
elif provider_name == "alipay":
|
||||||
|
priv_path = conf.get("app_private_key_pem_file", "")
|
||||||
|
pub_path = conf.get("alipay_public_key_pem_file", "")
|
||||||
|
if not priv_path:
|
||||||
|
raise FileNotFoundError(f"环境变量 ALIPAY_PRIV 未设置")
|
||||||
|
if not pub_path:
|
||||||
|
raise FileNotFoundError(f"环境变量 ALIPAY_PUB 未设置")
|
||||||
|
with open(priv_path, "rb") as f:
|
||||||
|
conf["app_private_key_pem"] = f.read()
|
||||||
|
with open(pub_path, "rb") as f:
|
||||||
|
conf["alipay_public_key_pem"] = f.read()
|
||||||
|
del conf["app_private_key_pem_file"]
|
||||||
|
del conf["alipay_public_key_pem_file"]
|
||||||
|
|
||||||
|
return conf
|
||||||
|
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
# 业务函数(不变)
|
||||||
|
# ──────────────────────────────────────────────
|
||||||
|
|
||||||
# 下单接口(统一)
|
# 下单接口(统一)
|
||||||
async def create_payment(request, params_kw=None):
|
async def create_payment(request, params_kw=None):
|
||||||
env = request._run_ns
|
env = request._run_ns
|
||||||
@ -189,11 +235,19 @@ async def setup_callback_path(app):
|
|||||||
# callback url= "/unipay/notify/{provider}"
|
# callback url= "/unipay/notify/{provider}"
|
||||||
|
|
||||||
def load_unipay():
|
def load_unipay():
|
||||||
PROVIDERS["transfer"] = get_provider("transfer", CONF["transfer"])
|
"""注册各支付渠道到 PROVIDERS。初始化失败的渠道设为 None(disabled)。"""
|
||||||
PROVIDERS["wechat"] = get_provider("wechat", CONF["wechat"])
|
for name in ("transfer", "wechat", "paypal", "alipay", "stripe"):
|
||||||
PROVIDERS["paypal"] = get_provider("paypal", CONF["paypal"])
|
try:
|
||||||
PROVIDERS["alipay"] = get_provider("alipay", CONF["alipay"])
|
conf = _build_provider_conf(name)
|
||||||
PROVIDERS["stripe"] = get_provider("stripe", CONF["stripe"])
|
PROVIDERS[name] = get_provider(name, conf)
|
||||||
|
if PROVIDERS[name] is not None:
|
||||||
|
print(f"[unipay] {name} 初始化成功")
|
||||||
|
else:
|
||||||
|
print(f"[unipay] {name} 初始化返回 None(渠道 disabled)")
|
||||||
|
except Exception as e:
|
||||||
|
PROVIDERS[name] = None
|
||||||
|
print(f"[unipay] {name} 初始化失败,已禁用: {e}", flush=True)
|
||||||
|
|
||||||
env = ServerEnv()
|
env = ServerEnv()
|
||||||
env.get_paychannels = get_paychannels
|
env.get_paychannels = get_paychannels
|
||||||
env.get_pay_feerate = get_pay_feerate
|
env.get_pay_feerate = get_pay_feerate
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user