""" 支付专用商户配置(与提现收款 WechatPayMchConfig / wechat_mch_service 完全分离)。 读取优先级: 1) XcxSysPeizhi.pay_* 2) 过渡回退:旧 weixin_* / settings.WEIXIN_* 历史订单退款:若订单 pay_mch_id 与当前支付商户不一致,但等于旧 weixin_mchid,则用旧 weixin_* 退款。 """ import logging import os from django.conf import settings from utils.xcx_sys_config import wx_cfg, _val logger = logging.getLogger(__name__) def pay_cert_base_dir(): base = getattr(settings, 'WECHAT_PAY_CERT_BASE_DIR', '') or '' if base: return base return os.path.join(str(settings.BASE_DIR), 'zhifuzhengshu') def _pay_val(pay_attr, legacy_db_attr, settings_attr, default=''): """pay_* 优先;空则回退旧 weixin_* / settings。""" v = _val(pay_attr, None, None) if v is not None and v != '': return v return _val(legacy_db_attr, settings_attr, default) or default class PayWxConfig: """支付商户配置代理(属性名兼容旧 WEIXIN_*,便于替换调用点)。""" @property def WEIXIN_MCHID(self): return _pay_val('pay_mch_id', 'weixin_mchid', 'WEIXIN_MCHID', '') @property def WEIXIN_SHANGHUMIYAO(self): return _pay_val('pay_api_v2_key', 'weixin_shanghu_miyao', 'WEIXIN_SHANGHUMIYAO', '') @property def WEIXIN_NOTIFY_URL(self): return _pay_val('pay_notify_url', 'weixin_notify_url', 'WEIXIN_NOTIFY_URL', '') @property def WEIXIN_CERT_PATH(self): return _pay_val('pay_cert_path', 'weixin_cert_path', 'WEIXIN_CERT_PATH', '') @property def WEIXIN_KEY_PATH(self): return _pay_val('pay_key_path', 'weixin_key_path', 'WEIXIN_KEY_PATH', '') @property def PAY_API_V3_KEY(self): return _val('pay_api_v3_key', None, '') or '' @property def PAY_CERT_SERIAL_NO(self): return _val('pay_cert_serial_no', None, '') or '' # 别名,语义更清晰 @property def PAY_MCH_ID(self): return self.WEIXIN_MCHID @property def PAY_API_V2_KEY(self): return self.WEIXIN_SHANGHUMIYAO pay_cfg = PayWxConfig() def get_current_pay_mch_id(): return (pay_cfg.PAY_MCH_ID or '').strip() def pay_cfg_as_dict(): return { 'MCHID': pay_cfg.WEIXIN_MCHID, 'KEY': pay_cfg.WEIXIN_SHANGHUMIYAO, 'NOTIFY_URL': pay_cfg.WEIXIN_NOTIFY_URL, 'CERT_PATH': pay_cfg.WEIXIN_CERT_PATH, 'KEY_PATH': pay_cfg.WEIXIN_KEY_PATH, 'API_V3_KEY': pay_cfg.PAY_API_V3_KEY, 'CERT_SERIAL_NO': pay_cfg.PAY_CERT_SERIAL_NO, } def get_pay_wx_cfg(mch_id=None): """ 按商户号取支付配置(用于退款反查)。 - mch_id 空 / 等于当前支付商户 → 当前 pay_cfg - mch_id 等于旧 weixin_mchid 且与当前不同 → 用旧 weixin_*(换号后历史单退款) - 否则若 zhifuzhengshu/{mch_id}/ 下有证书,拼路径但仍需密钥;密钥仅当前/旧库有,故仍回退当前并打日志 """ current = pay_cfg_as_dict() want = (mch_id or '').strip() current_mch = (current.get('MCHID') or '').strip() if not want or want == current_mch: return current legacy_mch = (wx_cfg.WEIXIN_MCHID or '').strip() if want == legacy_mch and want != current_mch: return { 'MCHID': wx_cfg.WEIXIN_MCHID, 'KEY': wx_cfg.WEIXIN_SHANGHUMIYAO, 'NOTIFY_URL': wx_cfg.WEIXIN_NOTIFY_URL, 'CERT_PATH': wx_cfg.WEIXIN_CERT_PATH, 'KEY_PATH': wx_cfg.WEIXIN_KEY_PATH, 'API_V3_KEY': '', 'CERT_SERIAL_NO': '', } cert_path = os.path.join(pay_cert_base_dir(), want, 'apiclient_cert.pem') key_path = os.path.join(pay_cert_base_dir(), want, 'apiclient_key.pem') if os.path.isfile(cert_path) and os.path.isfile(key_path): logger.warning( '支付商户 %s 与当前配置 %s 不一致,仅找到证书文件,密钥仍用当前配置,退款可能失败', want, current_mch, ) out = dict(current) out['MCHID'] = want out['CERT_PATH'] = cert_path out['KEY_PATH'] = key_path return out logger.warning( '订单支付商户 %s 无可用历史配置,回退当前支付商户 %s', want, current_mch, ) return current def apply_pay_cfg_to_proxy(cfg_dict): """把 get_pay_wx_cfg 结果转成带 WEIXIN_* 属性的简单对象,兼容旧代码写法。""" class _Cfg: pass o = _Cfg() o.WEIXIN_MCHID = cfg_dict.get('MCHID') or '' o.WEIXIN_SHANGHUMIYAO = cfg_dict.get('KEY') or '' o.WEIXIN_NOTIFY_URL = cfg_dict.get('NOTIFY_URL') or '' o.WEIXIN_CERT_PATH = cfg_dict.get('CERT_PATH') or '' o.WEIXIN_KEY_PATH = cfg_dict.get('KEY_PATH') or '' return o