diff --git a/jituan/services/club_user.py b/jituan/services/club_user.py index af6cd8f..c62f9b0 100644 --- a/jituan/services/club_user.py +++ b/jituan/services/club_user.py @@ -1,9 +1,13 @@ """用户所属俱乐部解析。""" +import logging + from django.db import IntegrityError, transaction from jituan.constants import CLUB_ID_DEFAULT from jituan.models import UserWxOpenid +logger = logging.getLogger(__name__) + def resolve_invite_party_club(user, request_club_id=None): """ @@ -202,9 +206,9 @@ def _looks_like_wx_openid(value): def get_wx_openid_for_user(user, club_id=None): """ - 按俱乐部取微信支付 JSAPI 用 openid。 - 多俱乐部用户 openid 在 user_wx_openid;xq 老用户可回落 User.OpenID; - 星之界等俱乐部用户常 UserName=openid 且 OpenID 为空,再回落 UserName。 + 按俱乐部取微信支付 JSAPI 用 openid(须与 club.pay_app_id 对应)。 + 优先 user_wx_openid(yonghuid, club_id);禁止回落到其他俱乐部的 openid。 + 仅当目标俱乐部为 xq 且用户无任何 user_wx_openid 记录时,才回落 legacy User.OpenID。 """ if not user: return '' @@ -214,6 +218,22 @@ def get_wx_openid_for_user(user, club_id=None): binding = UserWxOpenid.query.filter(yonghuid=uid, club_id=cid).first() if binding and binding.openid: return binding.openid + other_bindings = list( + UserWxOpenid.query.filter(yonghuid=uid) + .exclude(club_id=cid) + .values_list('club_id', flat=True)[:5] + ) + if other_bindings: + logger.warning( + '[PAY_OPENID] uid=%s 在俱乐部 %s 无 openid 绑定,但已有其他俱乐部绑定 %s;' + '禁止跨小程序回落 legacy openid,请在本小程序重新登录', + uid, cid, other_bindings, + ) + return '' + + if cid != CLUB_ID_DEFAULT: + return '' + legacy = getattr(user, 'OpenID', None) or getattr(user, 'openid', None) if legacy: return legacy.strip() @@ -227,5 +247,12 @@ def get_payment_openid(request, user=None, club_id=None): """下单/充值:按请求俱乐部解析 openid。""" from jituan.services.club_write import resolve_club_id_for_write user = user or getattr(request, 'user', None) - cid = club_id or resolve_club_id_for_write(request, user) - return get_wx_openid_for_user(user, cid), cid + cid = (club_id or '').strip() or resolve_club_id_for_write(request, user) + openid = get_wx_openid_for_user(user, cid) + if not openid and user: + uid = getattr(user, 'UserUID', None) or getattr(user, 'yonghuid', None) + logger.warning( + '[PAY_OPENID] 无法解析支付 openid uid=%s club=%s user_club=%s', + uid, cid, get_user_club_id(user), + ) + return openid, cid diff --git a/products/views/chongzhi_payment.py b/products/views/chongzhi_payment.py index a727a08..78e2aef 100644 --- a/products/views/chongzhi_payment.py +++ b/products/views/chongzhi_payment.py @@ -176,10 +176,14 @@ class ShangjiaChongzhi(APIView): }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) else: try: - pay_openid, _ = get_payment_openid(request) + pay_club = resolve_club_id_for_write(request, request.user) + pay_openid, _ = get_payment_openid(request, club_id=pay_club) if not pay_openid: chongzhi_jilu.delete() - return Response({'code': 400, 'message': '用户openid不存在'}, status=status.HTTP_400_BAD_REQUEST) + return Response({ + 'code': 400, + 'message': '当前小程序未绑定微信openid,请退出后重新登录再支付', + }, status=status.HTTP_400_BAD_REQUEST) pay_params = self.generate_wechat_pay_params( dingdanid=dingdanid, jine=jine, diff --git a/products/views/huiyuan_payment.py b/products/views/huiyuan_payment.py index 17e8e12..32484c6 100644 --- a/products/views/huiyuan_payment.py +++ b/products/views/huiyuan_payment.py @@ -187,7 +187,7 @@ class HuiyuanGoumai(APIView): }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) else: try: - pay_openid, _ = get_payment_openid(request) + pay_openid, pay_club = get_payment_openid(request, club_id=club_id) if not pay_openid: from jituan.services.member_recharge import MEMBER_CANCELLED_STATUS chongzhi_jilu.zhuangtai = MEMBER_CANCELLED_STATUS @@ -195,14 +195,15 @@ class HuiyuanGoumai(APIView): chongzhi_jilu.save(update_fields=['zhuangtai', 'shuoming']) return Response({ 'code': 400, - 'message': '用户openid不存在', + 'message': '当前小程序未绑定微信openid,请退出后重新登录再支付', 'dingdanid': dingdanid, }, status=status.HTTP_400_BAD_REQUEST) pay_params = self.generate_wechat_pay_params( dingdanid=dingdanid, jine=jine, openid=pay_openid, - pay_type='huiyuan' + pay_type='huiyuan', + club_id=pay_club, ) except Exception as e: from jituan.services.member_recharge import MEMBER_CANCELLED_STATUS @@ -234,13 +235,17 @@ class HuiyuanGoumai(APIView): 'message': '服务器内部错误,请稍后重试' }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) - def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type): + def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type, club_id=None): """生成微信支付参数(完整版,无省略)""" # 微信支付配置 - 从settings中获取 from jituan.services.club_write import resolve_club_id_for_write from jituan.services.wechat_pay import get_wechat_v2_config, log_wechat_v2_pay_diag, parse_wechat_v2_unifiedorder_response - pay_cfg = get_wechat_v2_config(resolve_club_id_for_write(self.request, self.request.user)) - log_wechat_v2_pay_diag(pay_cfg, scene='huiyuan', openid=openid, order_hint=dingdanid) + pay_club = club_id or resolve_club_id_for_write(self.request, self.request.user) + pay_cfg = get_wechat_v2_config(pay_club) + uid = getattr(self.request.user, 'UserUID', '') + log_wechat_v2_pay_diag( + pay_cfg, scene='huiyuan', openid=openid, order_hint=f'{dingdanid} uid={uid}', + ) APPID = pay_cfg['appid'] MCHID = pay_cfg['mch_id'] KEY = pay_cfg['key'] diff --git a/products/views/jifen_payment.py b/products/views/jifen_payment.py index dc5e525..c09048d 100644 --- a/products/views/jifen_payment.py +++ b/products/views/jifen_payment.py @@ -152,10 +152,14 @@ class JifenBuchong(APIView): }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) else: try: - pay_openid, _ = get_payment_openid(request) + pay_club = resolve_club_id_for_write(request, request.user) + pay_openid, _ = get_payment_openid(request, club_id=pay_club) if not pay_openid: chongzhi_jilu.delete() - return Response({'code': 400, 'message': '用户openid不存在'}, status=status.HTTP_400_BAD_REQUEST) + return Response({ + 'code': 400, + 'message': '当前小程序未绑定微信openid,请退出后重新登录再支付', + }, status=status.HTTP_400_BAD_REQUEST) pay_params = self.generate_wechat_pay_params( dingdanid=dingdanid, jine=jine, diff --git a/products/views/yajin_payment.py b/products/views/yajin_payment.py index 40f460c..e20e2d5 100644 --- a/products/views/yajin_payment.py +++ b/products/views/yajin_payment.py @@ -166,10 +166,14 @@ class YajinGoumai(APIView): }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) else: try: - pay_openid, _ = get_payment_openid(request) + pay_club = resolve_club_id_for_write(request, request.user) + pay_openid, _ = get_payment_openid(request, club_id=pay_club) if not pay_openid: chongzhi_jilu.delete() - return Response({'code': 400, 'message': '用户openid不存在'}, status=status.HTTP_400_BAD_REQUEST) + return Response({ + 'code': 400, + 'message': '当前小程序未绑定微信openid,请退出后重新登录再支付', + }, status=status.HTTP_400_BAD_REQUEST) pay_params = self.generate_wechat_pay_params( dingdanid=dingdanid, jine=jine, diff --git a/users/views/pay.py b/users/views/pay.py index 06b1d8c..b9e333b 100644 --- a/users/views/pay.py +++ b/users/views/pay.py @@ -160,12 +160,13 @@ class FaKuanPayView(APIView): 'message': f'支付参数生成失败: {str(e)}' }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) else: - pay_openid, _ = get_payment_openid(request) + pay_club = penalty_cz_club_id(fadan) + pay_openid, _ = get_payment_openid(request, club_id=pay_club) if not pay_openid: chongzhi_jilu.delete() return Response({ 'code': 400, - 'message': '用户openid不存在,无法发起微信支付', + 'message': '当前小程序未绑定微信openid,请退出后重新登录再支付', }, status=status.HTTP_400_BAD_REQUEST) try: pay_params = self.generate_wechat_pay_params(