feat: 微信扫码改为HTTPS+服务号JSAPI,支持相册扫码直接出密码

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQueandCursor committed 2026-08-09 06:58:07 +08:00
1 parent d342c4b818
commit 2d5f2ada79
4 files changed
+278 -63

No files matched your search

+117 -9
View File
@@ -531,16 +531,124 @@ def alipay_pay_page(request):
def wx_native_pay_page(request):
"""微信 Native 扫码中转页:相册扫 HTTPS 码后跳转到 weixin:// 付款链接。"""
"""相册可扫的 HTTPS 中转页:OAuth 取服务号 openid → JSAPI → 直接出密码。
二维码故意不用 weixin:// 付款码(会被相册安全拦截)。
"""
from urllib.parse import quote
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,
get_native_pay_session,
build_native_bridge_url,
)
out_trade_no = (request.GET.get('out_trade_no') or request.GET.get('dingdanid') or '').strip()
code_url = ''
if out_trade_no:
cached = cache.get(f'wx_native_code_{out_trade_no}') or {}
if isinstance(cached, dict):
code_url = (cached.get('code_url') or '').strip()
elif isinstance(cached, str):
code_url = cached.strip()
return render(request, 'wechat/native_pay.html', {'code_url': code_url, 'out_trade_no': out_trade_no})
club_id = (request.GET.get('club_id') or '').strip()
code = (request.GET.get('code') or '').strip()
if not out_trade_no:
return render(request, 'wechat/native_pay.html', {'error': '缺少订单号'})
session = get_native_pay_session(out_trade_no)
if not session:
# 兼容:从业务单反查
try:
from products.models import Czjilu
row = Czjilu.query.filter(dingdan_id=out_trade_no).first()
if row and int(getattr(row, 'zhuangtai', -1) or -1) in (9,):
session = {
'jine': str(row.jine),
'body': (row.shuoming or '微信支付')[:40],
'notify_url': '',
'club_id': (getattr(row, 'club_id', None) or club_id or ''),
}
except Exception:
session = None
if not session:
try:
from orders.models import Order
row = Order.query.filter(OrderID=out_trade_no).first()
if row and int(getattr(row, 'Status', -1) or -1) == 9:
session = {
'jine': str(row.Amount),
'body': getattr(settings, 'WEIXIN_PAY_DESCRIPTION', '星阙订单'),
'notify_url': getattr(settings, 'WEIXIN_NOTIFY_URL', ''),
'club_id': (getattr(row, 'ClubID', None) or club_id or ''),
}
except Exception:
session = None
if not session:
return render(request, 'wechat/native_pay.html', {
'error': '支付已过期,请返回小程序重新发起',
})
club_id = club_id or (session.get('club_id') or '')
notify_url = (session.get('notify_url') or '').strip()
if not notify_url:
# 按单号前缀回落业务回调(与现网一致)
if out_trade_no.startswith('CzHY'):
notify_url = getattr(settings, 'HUIYUAN_PAY_NOTIFY_URL', '')
elif out_trade_no.startswith('CzYJ'):
notify_url = getattr(settings, 'YAJIN_PAY_NOTIFY_URL', '')
elif out_trade_no.startswith('CzJF'):
notify_url = getattr(settings, 'JIFEN_PAY_NOTIFY_URL', '')
elif out_trade_no.startswith('CzSJ'):
notify_url = getattr(settings, 'SHANGJIA_CZ_PAY_NOTIFY_URL', '')
elif out_trade_no.startswith('FK'):
notify_url = getattr(settings, 'FAKUAN_PAY_NOTIFY_URL', '')
elif out_trade_no.startswith('KH'):
notify_url = getattr(settings, 'KAOHE_PAY_NOTIFY_URL', '')
elif out_trade_no.startswith('CzZP'):
notify_url = getattr(settings, 'ZHUANPAN_PAY_NOTIFY_URL', '')
else:
notify_url = getattr(settings, 'WEIXIN_NOTIFY_URL', '')
appid, secret, _club = get_club_official_credentials(club_id)
if not appid or not secret:
return render(request, 'wechat/native_pay.html', {
'error': '俱乐部未配置服务号(official_appid),无法相册支付。请联系管理员配置后重试,或返回小程序点「本机支付」。',
})
# 无 code:跳转网页授权(静默)
if not code:
redirect_uri = build_native_bridge_url(out_trade_no, club_id)
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=pay#wechat_redirect'
)
from django.shortcuts import redirect
return redirect(oauth)
wx = oauth2_access_token(code, club_id=club_id)
openid = (wx or {}).get('openid') or ''
if not openid:
return render(request, 'wechat/native_pay.html', {
'error': (wx or {}).get('errmsg') or '微信授权失败,请返回重试',
})
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'
try:
pay = build_official_jsapi_pay_params(
dingdanid=out_trade_no,
jine=session.get('jine'),
body=session.get('body') or '微信支付',
notify_url=notify_url,
openid=openid,
club_id=club_id,
client_ip=client_ip,
)
except Exception as exc:
logger.exception('相册支付 JSAPI 下单失败 dingdan=%s', out_trade_no)
return render(request, 'wechat/native_pay.html', {
'error': f'下单失败:{exc}',
})
return render(request, 'wechat/native_pay.html', {'pay': pay, 'out_trade_no': out_trade_no})
class PaymentVerifyView(APIView):