fix: 星阙V3提现强制用全局证书配置,修复序列号与私钥不匹配

club表私钥路径/序列号错填会导致微信报证书序列号错误。
xq 提现改读 settings+写死序列号;build_authorization 双重兜底。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-07-10 22:23:31 +08:00
parent 65a40b5f09
commit ca67dfa863
4 changed files with 47 additions and 16 deletions

View File

@@ -379,7 +379,8 @@ WECHAT_PAY_V3_CONFIG = {
'APPID': app_secrets.WECHAT_PAY_V3_APPID,
'API_V3_KEY': app_secrets.WECHAT_PAY_V3_API_V3_KEY,
'PRIVATE_KEY_PATH': app_secrets.WECHAT_PAY_V3_PRIVATE_KEY_PATH,
'CERT_SERIAL_NO': app_secrets.WECHAT_PAY_V3_CERT_SERIAL_NO,
# 与服务器 apiclient_key.pem 对应;勿用 club 表错误值
'CERT_SERIAL_NO': '481784CF56E872D9D8122BAC64E6BC7390803582',
'PLATFORM_CERT_DIR': app_secrets.WECHAT_PAY_V3_PLATFORM_CERT_DIR,
'NOTIFY_URL': 'https://www.abas.asia/hqhd/yonghu/tixiancallback',
'TRANSFER_SCENE_ID': '1005',

View File

@@ -6,7 +6,7 @@ from django.conf import settings
from django.core.management.base import BaseCommand
from django.db import transaction
from jituan.constants import CLUB_ID_DEFAULT
from jituan.services.wechat_pay import WX_V3_CERT_SERIAL_NO_OVERRIDE
from jituan.models import (
Club,
ClubHuiyuanPrice,
@@ -45,7 +45,7 @@ class Command(BaseCommand):
'pay_app_id': wx_v3.get('APPID', '') or getattr(settings, 'WEIXIN_APPID', ''),
'mch_key': getattr(settings, 'WEIXIN_SHANGHUMIYAO', ''),
'api_v3_key': wx_v3.get('API_V3_KEY', ''),
'cert_serial_no': wx_v3.get('CERT_SERIAL_NO', ''),
'cert_serial_no': WX_V3_CERT_SERIAL_NO_OVERRIDE,
'private_key_path': wx_v3.get('PRIVATE_KEY_PATH', ''),
'platform_cert_dir': wx_v3.get('PLATFORM_CERT_DIR', ''),
'config_json': {

View File

@@ -1,6 +1,7 @@
"""各俱乐部微信支付 V2MD5配置与验签。"""
import hashlib
import logging
import os
from django.conf import settings
@@ -209,10 +210,26 @@ def _apply_v3_cert_serial_override(cfg):
return cfg
def _log_v3_config(cfg):
key_path = (cfg.get('PRIVATE_KEY_PATH') or '').strip()
logger.warning(
'[WX_V3_CONFIG] club=%s appid=%s mch=%s serial=%s key_path=%s key_exists=%s',
cfg.get('club_id'),
cfg.get('APPID'),
cfg.get('MCHID'),
cfg.get('CERT_SERIAL_NO'),
key_path,
bool(key_path and os.path.isfile(key_path)),
)
def get_wechat_v3_config(club_id=None):
"""
微信 V3转账/查单)配置。
各俱乐部(含 xq优先读 club 表;字段为空时按项回落 settings.WECHAT_PAY_V3_CONFIG。
星阙(xq)证书序列号、私钥、api_v3_key、商户号强制用 settings 全局配置,
避免 club 表错填导致「证书序列号与私钥不匹配」。仅 AppID 读 club 小程序配置。
其他俱乐部仍读 club 表,但序列号统一写死覆盖。
"""
cid = (club_id or '').strip() or CLUB_ID_DEFAULT
wx = dict(getattr(settings, 'WECHAT_PAY_V3_CONFIG', {}) or {})
@@ -220,35 +237,43 @@ def get_wechat_v3_config(club_id=None):
'APPID': wx.get('APPID', ''),
'MCHID': wx.get('MCHID', ''),
'PRIVATE_KEY_PATH': wx.get('PRIVATE_KEY_PATH', ''),
'CERT_SERIAL_NO': wx.get('CERT_SERIAL_NO', ''),
'CERT_SERIAL_NO': WX_V3_CERT_SERIAL_NO_OVERRIDE,
'API_V3_KEY': wx.get('API_V3_KEY', ''),
'PLATFORM_CERT_DIR': wx.get('PLATFORM_CERT_DIR', ''),
'TRANSFER_SCENE_ID': wx.get('TRANSFER_SCENE_ID', '1005'),
'club_id': cid,
}
if cid == CLUB_ID_DEFAULT:
cfg = dict(fallback)
try:
club = Club.query.get(club_id=cid)
mini_appid = get_club_miniapp_appid(club)
if mini_appid:
cfg['APPID'] = mini_appid
except Club.DoesNotExist:
pass
_log_v3_config(cfg)
return cfg
try:
club = Club.query.get(club_id=cid)
except Club.DoesNotExist:
logger.warning('club %s 不存在,回落全局 V3 支付配置', cid)
return _apply_v3_cert_serial_override(fallback)
cfg = _apply_v3_cert_serial_override(fallback)
_log_v3_config(cfg)
return cfg
cfg_json = club.config_json or {}
return _apply_v3_cert_serial_override({
'APPID': get_club_miniapp_appid(club) or (
fallback['APPID'] if cid == CLUB_ID_DEFAULT else ''
),
cfg = _apply_v3_cert_serial_override({
'APPID': get_club_miniapp_appid(club) or '',
'MCHID': club.mch_id or fallback['MCHID'],
'PRIVATE_KEY_PATH': (
club.private_key_path
or cfg_json.get('private_key_path')
or fallback['PRIVATE_KEY_PATH']
),
'CERT_SERIAL_NO': (
club.cert_serial_no
or cfg_json.get('cert_serial_no')
or fallback['CERT_SERIAL_NO']
),
'CERT_SERIAL_NO': fallback['CERT_SERIAL_NO'],
'API_V3_KEY': (
club.api_v3_key
or cfg_json.get('api_v3_key')
@@ -266,6 +291,8 @@ def get_wechat_v3_config(club_id=None):
),
'club_id': cid,
})
_log_v3_config(cfg)
return cfg
def club_id_for_tixian_yonghuid(yonghuid):

View File

@@ -38,7 +38,10 @@ def build_authorization(method, url, body, club_id=None):
cfg = _get_v3_config(club_id)
private_key = load_private_key(club_id)
mchid = cfg['MCHID']
serial_no = cfg['CERT_SERIAL_NO']
serial_no = (cfg.get('CERT_SERIAL_NO') or '').strip().upper()
if not serial_no:
from jituan.services.wechat_pay import WX_V3_CERT_SERIAL_NO_OVERRIDE
serial_no = WX_V3_CERT_SERIAL_NO_OVERRIDE
parsed = urlparse(url)
path = parsed.path