diff --git a/orders/views/payment.py b/orders/views/payment.py index d6c345e..8a2e58a 100644 --- a/orders/views/payment.py +++ b/orders/views/payment.py @@ -535,18 +535,26 @@ def wx_h5_jsapi_pay_page(request): 不是扫码。流程: 1) 带 out_trade_no 进入 → 服务号静默 OAuth 取当前微信 openid - 2) 用该 openid 对未付款订单重新统一下单,服务端注入 pay 参数 - 3) 页面仅用户点击后 WeixinJSBridge.invoke(避免自动调起被微信秒关) + 2) 用该 openid 对未付款订单重新统一下单 + 3) 支付参数写入短时 cache,302 到干净 URL ?k=token(去掉 code/长 query) + 4) 前端 history.replaceState 成无 query 的目录 URL,再手动点支付 - sessionStorage 仅作无 OAuth / 旧链接兜底。 + 微信校验整页 URL;OAuth 回调带超长 query 时部分机型会秒关 cancel。 """ + import secrets from urllib.parse import quote + from django.shortcuts import redirect from jituan.services.club_resolver import get_club_official_credentials, oauth2_access_token from jituan.services.wechat_native_pay import ( build_official_jsapi_pay_params, build_h5_jsapi_bridge_url, ) + CACHE_PREFIX = 'wx_h5_jsapi_pay_pack_' + CACHE_TTL = 60 * 15 + CLEAN_PAY_URL = 'https://www.abas.asia/hqhd/dingdan/wx-h5-pay/' + + pack_key = (request.GET.get('k') or '').strip() out_trade_no = (request.GET.get('out_trade_no') or request.GET.get('dingdanid') or '').strip() club_id = (request.GET.get('club_id') or '').strip() or 'ufo' code = (request.GET.get('code') or '').strip() @@ -560,13 +568,27 @@ def wx_h5_jsapi_pay_page(request): 'return_url': return_url, 'oauth_openid_preview': '', 'pay_source': 'sessionStorage', + 'force_clean_url': True, } - if not out_trade_no: - # 兼容仅 sessionStorage 的旧跳转 + # ---- 干净页:用 k 取回 OAuth 后缓存的支付包 ---- + if pack_key and not code: + pack = cache.get(f'{CACHE_PREFIX}{pack_key}') + if not isinstance(pack, dict) or not pack.get('pay'): + ctx['server_error'] = '支付会话已过期,请返回点单页重新下单' + return render(request, 'wechat/h5_jsapi_pay.html', ctx) + pay = pack['pay'] + ctx['server_pay'] = pay + ctx['out_trade_no'] = pack.get('out_trade_no') or '' + ctx['club_id'] = pack.get('club_id') or club_id + ctx['return_url'] = pack.get('return_url') or '' + ctx['oauth_openid_preview'] = pack.get('openid_preview') or '' + ctx['pay_source'] = 'oauth_server_clean' + return render(request, 'wechat/h5_jsapi_pay.html', ctx) + + if not out_trade_no: return render(request, 'wechat/h5_jsapi_pay.html', ctx) - # 查未付款订单 order = None jine = None try: @@ -587,15 +609,20 @@ def wx_h5_jsapi_pay_page(request): ctx['server_error'] = f'俱乐部 {club_id} 未配置服务号 official_appid/secret' return render(request, 'wechat/h5_jsapi_pay.html', ctx) + if return_url: + cache.set(f'wx_h5_return_{out_trade_no}', return_url, CACHE_TTL) + else: + return_url = (cache.get(f'wx_h5_return_{out_trade_no}') or '').strip() + if not code: - redirect_uri = build_h5_jsapi_bridge_url(out_trade_no, club_id, return_url) + # OAuth 回调 URI 尽量短,不带 return_url + redirect_uri = build_h5_jsapi_bridge_url(out_trade_no, club_id, return_url='') oauth = ( 'https://open.weixin.qq.com/connect/oauth2/authorize' f'?appid={quote(appid)}' f'&redirect_uri={quote(redirect_uri, safe="")}' '&response_type=code&scope=snsapi_base&state=h5pay#wechat_redirect' ) - from django.shortcuts import redirect return redirect(oauth) wx = oauth2_access_token(code, club_id=club_id) @@ -605,7 +632,7 @@ def wx_h5_jsapi_pay_page(request): return render(request, 'wechat/h5_jsapi_pay.html', ctx) oid = openid.strip() - ctx['oauth_openid_preview'] = (oid[:6] + '...' + oid[-4:]) if len(oid) > 10 else oid + oid_preview = (oid[:6] + '...' + oid[-4:]) if len(oid) > 10 else oid xff = request.META.get('HTTP_X_FORWARDED_FOR') client_ip = (xff.split(',')[0].strip() if xff else '') or request.META.get('REMOTE_ADDR') or '127.0.0.1' @@ -621,13 +648,23 @@ def wx_h5_jsapi_pay_page(request): except Exception as exc: logger.exception('[WX_H5_PAY] OAuth 重下单失败 dingdan=%s', out_trade_no) ctx['server_error'] = f'统一下单失败:{exc}' + ctx['oauth_openid_preview'] = oid_preview return render(request, 'wechat/h5_jsapi_pay.html', ctx) - # 去掉前端不需要的私有字段副本;_diag 保留给诊断页 - ctx['server_pay'] = pay - ctx['club_id'] = club_id - ctx['pay_source'] = 'oauth_server' - return render(request, 'wechat/h5_jsapi_pay.html', ctx) + token = secrets.token_urlsafe(16) + cache.set( + f'{CACHE_PREFIX}{token}', + { + 'pay': pay, + 'out_trade_no': out_trade_no, + 'club_id': club_id, + 'return_url': return_url, + 'openid_preview': oid_preview, + }, + CACHE_TTL, + ) + return redirect(f'{CLEAN_PAY_URL}?k={quote(token)}') + def wx_native_pay_page(request): diff --git a/templates/wechat/h5_jsapi_pay.html b/templates/wechat/h5_jsapi_pay.html index 7513745..a1f9f3e 100644 --- a/templates/wechat/h5_jsapi_pay.html +++ b/templates/wechat/h5_jsapi_pay.html @@ -41,8 +41,9 @@