From 29225e9b65ce59ccdd172d2774497d8954f371d9 Mon Sep 17 00:00:00 2001 From: marco Date: Sat, 6 Jun 2026 18:48:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(wxpay):=20=E6=8F=90=E7=8E=B0=20debug=20?= =?UTF-8?q?=E5=85=8D=E4=BA=BA=E5=B7=A5=E5=AE=A1=E6=A0=B8=E7=9B=B4=E5=8F=91?= =?UTF-8?q?=20+=20=E4=BF=AE=E5=A4=8D=20user=5Fdisplay=5Fname=20=E8=A2=AB?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=8B=92=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - WithdrawRequest 加 skip_review 字段;/withdraw 在 skip_review 且非生产(APP_ENV!=prod) 时,建单后立即 execute_withdraw_transfer 直发、跳过人工审核。双闸:仅 debug 客户端下发 + 服务端非 prod 才认,生产恒走人工审核。便于本地/联调直接测提现到账链路。 - 修 _auth_display_name:微信 user_display_name 校验极严,空格和标点也按"控制字符"拒收 (实测 'wonderable ai' 因空格被 400,'wonderableai'/'周周'/'傻瓜比价用户8888' 才过)。 原清洗只去星形面字符(ord>0xFFFF),漏掉 BMP emoji/空格/标点/控制符/零宽符。改为白名单: 只保留 Unicode 文字(L*)+ 数字(N*),其余全剔,清空兜底手机号尾号。修复昵称含空格/emoji 的用户发起免确认收款授权直接 400、提现打款失败的问题。 Co-Authored-By: Claude Opus 4.8 --- app/api/v1/wallet.py | 12 +++++++++++- app/repositories/wallet.py | 16 +++++++++++++--- app/schemas/welfare.py | 7 +++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/app/api/v1/wallet.py b/app/api/v1/wallet.py index 7da6613..c0bc017 100644 --- a/app/api/v1/wallet.py +++ b/app/api/v1/wallet.py @@ -196,9 +196,19 @@ def withdraw(req: WithdrawRequest, user: CurrentUser, db: DbSession) -> Withdraw except crud_wallet.InsufficientCashError as e: raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="insufficient cash balance") from e + # 调试直发(非生产):skip_review=true 时跳过人工审核,立即发起微信转账(等价 admin approve)。 + # 双闸保护——客户端仅 debug 包下发此 flag,服务端仅非 prod 才认;任一道闸拦住即恢复正常审核, + # 生产绝不会被客户端 flag 绕过审核。用于本地联调"提现→微信转账/免确认到账"全链路。 + if req.skip_review and not settings.is_prod and order.status == "reviewing": + logger.warning( + "withdraw skip_review(非prod调试直发,跳过人工审核立即打款) user_id=%d bill=%s", + user.id, order.out_bill_no, + ) + order = crud_wallet.execute_withdraw_transfer(db, order) + acc = crud_wallet.get_or_create_account(db, user.id) logger.info( - "withdraw submitted user_id=%d cents=%d bill=%s status=%s(待审核)", + "withdraw submitted user_id=%d cents=%d bill=%s status=%s", user.id, req.amount_cents, order.out_bill_no, order.status, ) # 此刻 status=reviewing,尚未打款 → 无 package_info;App 据 status 提示"已提交,等待审核"。 diff --git a/app/repositories/wallet.py b/app/repositories/wallet.py index 4e49d4c..a0db153 100644 --- a/app/repositories/wallet.py +++ b/app/repositories/wallet.py @@ -7,6 +7,7 @@ from __future__ import annotations import re +import unicodedata import uuid from datetime import datetime, timedelta, timezone @@ -421,10 +422,19 @@ def create_withdraw( def _auth_display_name(user: User | None) -> str: - """微信授权页展示的"开通账号"昵称(≤32,utf8,去表情)。取昵称兜底手机号尾号。""" + """微信授权页展示的"开通账号"昵称(≤32,utf8)。 + + 微信对 user_display_name 校验极严:**连空格和标点(. 等)都算"控制字符"拒收**——实测 + 'wonderable ai'(带空格)被 400 拒、'wonderableai'/'周周'/'傻瓜比价用户8888' 才过。 + 故只保留 文字(Unicode L*,中英文/CJK)与数字(N*),其余(emoji/符号/空格/标点/控制符/ + 零宽连接符/变体选择符/星形面字符)一律剔除;清空则兜底手机号尾号。 + """ raw = ((user.nickname if user else None) or (user.wechat_nickname if user else None) or "").strip() - # 去掉非 BMP 字符(emoji 等),微信不支持 - name = "".join(ch for ch in raw if ord(ch) <= 0xFFFF).strip() + kept = [ + ch for ch in raw + if ord(ch) <= 0xFFFF and unicodedata.category(ch)[0] in ("L", "N") + ] + name = "".join(kept).strip() if not name and user and user.phone: tail = user.phone[-4:] if len(user.phone) >= 4 else user.phone name = f"傻瓜比价用户{tail}" diff --git a/app/schemas/welfare.py b/app/schemas/welfare.py index ee0a5c9..98bf96b 100644 --- a/app/schemas/welfare.py +++ b/app/schemas/welfare.py @@ -119,6 +119,13 @@ class WithdrawRequest(BaseModel): out_bill_no: str | None = Field( None, description="客户端幂等键(商户单号):同号重试不重复转账。不传则服务端生成" ) + skip_review: bool = Field( + False, + description=( + "调试直发:跳过人工审核立即打款。仅非生产(APP_ENV!=prod)生效," + "客户端仅 debug 包下发(开发设置开关)。生产恒走人工审核。" + ), + ) class WithdrawResultOut(BaseModel):