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):