支付与收款商户号分离:支付改读 pay_* 独立配置与证书目录,收款侧不动。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
150
utils/pay_mch_config.py
Normal file
150
utils/pay_mch_config.py
Normal file
@@ -0,0 +1,150 @@
|
||||
"""
|
||||
支付专用商户配置(与提现收款 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
|
||||
Reference in New Issue
Block a user