fix: H5支付OAuth后跳干净URL再调起,避免长query秒关
This commit is contained in:
1 parent
6c5c9a378a
commit
ba46f73e97
2 files changed
+72
-18
No files matched your search
+51
-14
@@ -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):
|
||||
|
||||
@@ -41,8 +41,9 @@
|
||||
<div class="title">微信支付(诊断模式)</div>
|
||||
<div class="banner" id="banner">收款商户号加载中…</div>
|
||||
<div class="warn">
|
||||
请<strong>手动点下方绿色按钮</strong>调起支付(不要依赖自动弹窗)。<br/>
|
||||
若闪一下就关:看有没有灰字「当前页面的URL未注册」——有=目录仍未生效;没有=把下方绿日志整段复制发回。
|
||||
请<strong>手动点下方绿色按钮</strong>调起支付。<br/>
|
||||
秒关时看有没有灰字「当前页面的URL未注册」。<br/>
|
||||
目录必须挂在<strong>公众号 AppID wxd400adf1911c534f</strong>下(不要挂在小程序 AppID 下)。
|
||||
</div>
|
||||
<div class="sub" id="tip">初始化…</div>
|
||||
<button class="btn" type="button" id="payBtn">立即支付 / 重试</button>
|
||||
@@ -95,9 +96,22 @@
|
||||
'club_id = ' + club + '<br/>' +
|
||||
'pay来源 = ' + PAY_SOURCE + '<br/>' +
|
||||
'OAuth openid = ' + (SERVER_OID || '(无)') + '<br/>' +
|
||||
'请到<strong>这个商户号</strong>的「产品中心 → JSAPI支付 → 支付授权目录」配:<br/>' +
|
||||
'请到商户 <strong>1748512512</strong> → 选中公众号 AppID <strong>wxd400adf1911c534f</strong><br/>' +
|
||||
'「产品中心 → JSAPI支付 → 支付授权目录」添加:<br/>' +
|
||||
'https://www.abas.asia/hqhd/dingdan/<br/>' +
|
||||
'以及精确目录:https://www.abas.asia/hqhd/dingdan/wx-h5-pay/';
|
||||
'https://www.abas.asia/hqhd/dingdan/wx-h5-pay/';
|
||||
}
|
||||
|
||||
function cleanPayUrl() {
|
||||
var clean = 'https://www.abas.asia/hqhd/dingdan/wx-h5-pay/';
|
||||
try {
|
||||
if (String(location.href || '').split('#')[0] !== clean) {
|
||||
history.replaceState(null, '', clean);
|
||||
push('已 replaceState 成干净校验 URL', clean);
|
||||
}
|
||||
} catch (e) {
|
||||
push('replaceState 失败', String(e && e.message || e));
|
||||
}
|
||||
}
|
||||
|
||||
function now() {
|
||||
@@ -243,6 +257,7 @@
|
||||
var invokeAt = 0;
|
||||
|
||||
function invokePay() {
|
||||
cleanPayUrl();
|
||||
dumpEnv('点击调起前');
|
||||
if (!pay || !pay.timeStamp || !pay.nonceStr || !pay.package || !pay.paySign) {
|
||||
setTip('支付参数丢失:' + (SERVER_ERROR || '请返回重新下单'), true);
|
||||
@@ -373,6 +388,8 @@
|
||||
push('提示:请勿单独打开本页;须从点单页跳转');
|
||||
return;
|
||||
}
|
||||
cleanPayUrl();
|
||||
dumpEnv('干净 URL 后');
|
||||
setTip('参数已就绪。请<strong>手动点击</strong>「立即支付」(已关闭自动调起)');
|
||||
push('已关闭自动调起,等待用户点击');
|
||||
})();
|
||||
|
||||
Reference in new issue
Block a user