feat: 微信扫码改为HTTPS+服务号JSAPI,支持相册扫码直接出密码
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
d342c4b818
commit
2d5f2ada79
4 files changed
+278
-63
No files matched your search
+117
-9
@@ -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):
|
||||
|
||||
Reference in new issue
Block a user