diff --git a/jituan/services/wechat_native_pay.py b/jituan/services/wechat_native_pay.py
index 52a495c..f55adb5 100644
--- a/jituan/services/wechat_native_pay.py
+++ b/jituan/services/wechat_native_pay.py
@@ -1,22 +1,22 @@
-"""微信 Native 扫码支付(trade_type=NATIVE)。
+"""微信扫码支付:小程序码(本机相册可扫,直接弹密码)。
-「一瞬间出密码」= 二维码必须是微信返回的 weixin://wxpay/bizpayurl?...
-他人微信摄像头扫屏幕 → 系统直接弹付款,中间没有网页。
+为什么不用 weixin:// 付款码:
+- 那才是「另一台手机摄像头扫 → 瞬间出密码」的官方 Native
+- 但微信禁止「扫一扫 → 相册」识别付款码(保障支付安全)
-本机相册扫这种付款码会被微信官方拦截(保障支付安全)——不是商户能关的。
-本机付款请走小程序「本机支付」JSAPI,同样是一瞬间出密码。
-
-相册可扫的 HTTPS 中转页仍保留(wx-native-pay),但不印进主二维码,
-因为那条路必然经过网页授权,做不到和 weixin:// 一样的「无感弹窗」。
+为什么改用小程序码(微信官方文档也写了可用小程序码替代收款码):
+- 相册可以识别小程序码
+- 扫完直接进小程序支付页 → wx.requestPayment 出密码
+- 用户体感就是:相册一扫,马上出支付,没有乱七八糟的 H5 网页
"""
from __future__ import annotations
+import base64
import hashlib
import logging
import random
import string
import time
-from urllib.parse import quote
import requests
from django.core.cache import cache
@@ -31,28 +31,23 @@ from utils.money import yuan_to_fen
logger = logging.getLogger(__name__)
-CACHE_KEY_PREFIX = 'wx_native_code_'
SESSION_KEY_PREFIX = 'wx_native_pay_'
+SCENE_KEY_PREFIX = 'wx_native_scene_'
CACHE_TTL_SECONDS = 60 * 30
-BRIDGE_URL_BASE = 'https://www.abas.asia/hqhd/dingdan/wx-native-pay/'
-
-
-def _cache_key(dingdanid: str) -> str:
- return f'{CACHE_KEY_PREFIX}{dingdanid}'
+# getwxacodeunlimit scene ≤ 32 字节;前缀 + 随机
+SCENE_PREFIX = 'np'
+PAY_PAGE = 'pages/wx-scan-pay/wx-scan-pay'
def _session_key(dingdanid: str) -> str:
return f'{SESSION_KEY_PREFIX}{dingdanid}'
-def build_native_bridge_url(dingdanid: str, club_id=None) -> str:
- q = f'out_trade_no={quote(dingdanid or "", safe="")}'
- if club_id:
- q += f'&club_id={quote(str(club_id), safe="")}'
- return f'{BRIDGE_URL_BASE}?{q}'
+def _scene_key(scene: str) -> str:
+ return f'{SCENE_KEY_PREFIX}{scene}'
-def save_native_pay_session(dingdanid, *, jine, body, notify_url, club_id=None):
+def save_native_pay_session(dingdanid, *, jine, body, notify_url, club_id=None, scene=''):
cache.set(
_session_key(dingdanid),
{
@@ -60,6 +55,8 @@ def save_native_pay_session(dingdanid, *, jine, body, notify_url, club_id=None):
'body': (body or '微信支付').strip() or '微信支付',
'notify_url': (notify_url or '').strip(),
'club_id': (club_id or '').strip(),
+ 'scene': (scene or '').strip(),
+ 'dingdanid': (dingdanid or '').strip(),
},
CACHE_TTL_SECONDS,
)
@@ -70,17 +67,70 @@ def get_native_pay_session(dingdanid: str):
return data if isinstance(data, dict) else None
-def _native_pay_response(code_url: str, *, bridge_url: str = '') -> dict:
- from jituan.services.alipay_pay import qr_link_to_base64
+def resolve_scene_to_dingdanid(scene: str) -> str:
+ scene = (scene or '').strip()
+ if not scene:
+ return ''
+ dingdanid = cache.get(_scene_key(scene)) or ''
+ return str(dingdanid).strip()
- out = {
- 'qr_code': code_url,
- 'qr_image': qr_link_to_base64(code_url),
- 'pay_method': PAY_METHOD_WECHAT_NATIVE,
+
+def _new_scene() -> str:
+ # np + 22 位 = 24,远小于 32
+ rnd = ''.join(random.choices(string.ascii_letters + string.digits, k=22))
+ return f'{SCENE_PREFIX}{rnd}'
+
+
+def _png_to_data_url(png_bytes: bytes) -> str:
+ b64 = base64.b64encode(png_bytes).decode('ascii')
+ return f'data:image/png;base64,{b64}'
+
+
+def _fetch_mini_program_code_png(*, club_id: str, scene: str) -> bytes:
+ from utils.weixin_token import get_club_mini_access_token, is_weixin_token_invalid
+
+ access_token = get_club_mini_access_token(club_id)
+ if not access_token:
+ raise Exception(f'俱乐部 {club_id} 获取小程序 access_token 失败,无法生成支付码')
+
+ post_data = {
+ 'scene': scene,
+ 'page': PAY_PAGE,
+ 'width': 430,
+ 'auto_color': False,
+ 'line_color': {'r': 0, 'g': 0, 'b': 0},
+ 'check_path': False,
}
- if bridge_url:
- out['bridge_url'] = bridge_url
- return out
+
+ def _req(tok: str):
+ url = f'https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={tok}'
+ return requests.post(url, json=post_data, timeout=15)
+
+ wx_resp = _req(access_token)
+ if wx_resp.headers.get('content-type', '').startswith('application/json'):
+ try:
+ err = wx_resp.json()
+ if is_weixin_token_invalid(err.get('errcode'), err.get('errmsg', '')):
+ access_token = get_club_mini_access_token(club_id, force_refresh=True)
+ if access_token:
+ wx_resp = _req(access_token)
+ except Exception:
+ pass
+
+ content_type = wx_resp.headers.get('content-type', '')
+ if wx_resp.status_code != 200 or 'image' not in content_type:
+ errmsg = '未知错误'
+ try:
+ error_info = wx_resp.json()
+ errmsg = error_info.get('errmsg', errmsg)
+ logger.error('[WX_NATIVE_MINICODE] fail club=%s err=%s', club_id, error_info)
+ except Exception:
+ logger.error(
+ '[WX_NATIVE_MINICODE] fail club=%s status=%s body=%s',
+ club_id, wx_resp.status_code, wx_resp.content[:300],
+ )
+ raise Exception(f'生成小程序支付码失败: {errmsg}')
+ return wx_resp.content
def generate_wechat_native_pay_params(
@@ -93,100 +143,64 @@ def generate_wechat_native_pay_params(
client_ip='127.0.0.1',
scene='native',
):
- """统一下单 NATIVE,二维码印官方 weixin:// 付款码(他人扫瞬间出密码)。"""
+ """生成「相册可扫」小程序码二维码。"""
dingdanid = (dingdanid or '').strip()
notify_url = (notify_url or '').strip()
body = (body or '微信支付').strip() or '微信支付'
+ club_id = (club_id or '').strip()
if not dingdanid:
raise Exception('订单号不能为空')
if not notify_url:
raise Exception('缺少微信回调地址 notify_url')
+ if not club_id:
+ raise Exception('缺少 club_id,无法生成小程序支付码')
+
+ cached = get_native_pay_session(dingdanid) or {}
+ cached_img = (cached.get('qr_image') or '').strip()
+ cached_scene = (cached.get('scene') or '').strip()
+ if cached_img and cached_scene and resolve_scene_to_dingdanid(cached_scene) == dingdanid:
+ logger.info(
+ '[WX_NATIVE_MINICODE] reuse dingdan=%s club=%s scene=%s',
+ dingdanid, club_id, cached_scene,
+ )
+ return {
+ 'qr_code': f'minicode:{cached_scene}',
+ 'qr_image': cached_img,
+ 'scene': cached_scene,
+ 'pay_method': PAY_METHOD_WECHAT_NATIVE,
+ }
+
+ pay_scene = _new_scene()
+ png = _fetch_mini_program_code_png(club_id=club_id, scene=pay_scene)
+ qr_image = _png_to_data_url(png)
- # 给相册/链接兜底页留会话(主二维码仍是 weixin://)
save_native_pay_session(
dingdanid,
jine=jine,
body=body,
notify_url=notify_url,
club_id=club_id,
+ scene=pay_scene,
)
- bridge = build_native_bridge_url(dingdanid, club_id)
+ # 把图片也塞进 session,便于复用
+ sess = get_native_pay_session(dingdanid) or {}
+ sess['qr_image'] = qr_image
+ cache.set(_session_key(dingdanid), sess, CACHE_TTL_SECONDS)
+ cache.set(_scene_key(pay_scene), dingdanid, CACHE_TTL_SECONDS)
- cached = cache.get(_cache_key(dingdanid)) or {}
- cached_url = (cached.get('code_url') or '').strip()
- if cached_url:
- logger.info(
- '[WX_NATIVE] reuse code_url dingdan=%s club=%s scene=%s',
- dingdanid, club_id, scene,
- )
- return _native_pay_response(cached_url, bridge_url=bridge)
-
- pay_cfg = get_wechat_v2_config(club_id)
- log_wechat_v2_pay_diag(
- pay_cfg, scene=f'native:{scene}', openid='', order_hint=dingdanid,
- )
- appid = pay_cfg.get('appid') or ''
- mch_id = pay_cfg.get('mch_id') or ''
- key = pay_cfg.get('key') or ''
- if not all([appid, mch_id, key]):
- raise Exception(
- f'微信 Native 配置不完整 club={pay_cfg.get("club_id")} '
- f'appid={bool(appid)} mch_id={bool(mch_id)} mch_key={bool(key)}'
- )
-
- nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
- total_fee = yuan_to_fen(jine)
- ip = (client_ip or '127.0.0.1').split(',')[0].strip() or '127.0.0.1'
-
- sign_data = {
- 'appid': appid,
- 'mch_id': mch_id,
- 'nonce_str': nonce_str,
- 'body': body,
- 'out_trade_no': dingdanid,
- 'total_fee': total_fee,
- 'spbill_create_ip': ip,
- 'notify_url': notify_url,
- 'trade_type': 'NATIVE',
- }
- sorted_items = sorted(sign_data.items(), key=lambda x: x[0])
- sign_string = '&'.join(f'{k}={v}' for k, v in sorted_items if v) + f'&key={key}'
- sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
-
- xml_data = f"""
-{appid}
-{body}
-{mch_id}
-{nonce_str}
-{notify_url}
-{dingdanid}
-{ip}
-{total_fee}
-NATIVE
-{sign}
-"""
-
- response = requests.post(
- 'https://api.mch.weixin.qq.com/pay/unifiedorder',
- data=xml_data.encode('utf-8'),
- headers={'Content-Type': 'application/xml'},
- timeout=10,
- )
- parsed = parse_wechat_v2_unifiedorder_response(
- response.content,
- log_prefix=f'WX_NATIVE_{scene}',
- expect='code_url',
- )
- code_url = parsed['code_url']
- cache.set(_cache_key(dingdanid), {'code_url': code_url}, CACHE_TTL_SECONDS)
logger.warning(
- '[WX_NATIVE] unifiedorder ok dingdan=%s club=%s mch=%s scene=%s code=%s',
- dingdanid, pay_cfg.get('club_id'), mch_id, scene, code_url[:48],
+ '[WX_NATIVE_MINICODE] ok dingdan=%s club=%s biz_scene=%s pay_scene=%s',
+ dingdanid, club_id, scene, pay_scene,
)
- return _native_pay_response(code_url, bridge_url=bridge)
+ return {
+ 'qr_code': f'minicode:{pay_scene}',
+ 'qr_image': qr_image,
+ 'scene': pay_scene,
+ 'pay_method': PAY_METHOD_WECHAT_NATIVE,
+ }
-def build_official_jsapi_pay_params(
+def build_mini_jsapi_pay_params(
*,
dingdanid,
jine,
@@ -196,21 +210,38 @@ def build_official_jsapi_pay_params(
club_id=None,
client_ip='127.0.0.1',
):
- """服务号 AppID + 服务号 openid 做 JSAPI,供相册 HTTPS 中转页调起。"""
- pay_cfg = get_wechat_v2_config(club_id, app_source='official')
+ """小程序 AppID + 小程序 openid → JSAPI,供相册扫码落地页调起密码。"""
+ from jituan.constants import FUBEI_BIZ_CZJILU, FUBEI_BIZ_ORDER
+ from jituan.services.mini_pay_router import maybe_create_fubei_jsapi_params
+
+ pay_cfg = get_wechat_v2_config(club_id, app_source='mini')
log_wechat_v2_pay_diag(
- pay_cfg, scene='native_album_jsapi', openid=openid, order_hint=dingdanid,
+ pay_cfg, scene='native_minicode_jsapi', openid=openid, order_hint=dingdanid,
)
appid = pay_cfg.get('appid') or ''
mch_id = pay_cfg.get('mch_id') or ''
key = pay_cfg.get('key') or ''
+ if not openid:
+ raise Exception('缺少小程序 openid')
+
+ biz = FUBEI_BIZ_CZJILU if str(dingdanid).startswith('Cz') or str(dingdanid).startswith('FK') or str(dingdanid).startswith('KH') else FUBEI_BIZ_ORDER
+ fb = maybe_create_fubei_jsapi_params(
+ club_id=club_id,
+ out_trade_no=dingdanid,
+ amount_yuan=jine,
+ body=body or '微信支付',
+ openid=openid,
+ notify_url=notify_url,
+ biz_type=biz,
+ )
+ if fb is not None:
+ return fb
+
if not all([appid, mch_id, key]):
raise Exception(
- f'服务号支付配置不完整 club={pay_cfg.get("club_id")} '
- f'请配置 official_appid 与商户密钥'
+ f'小程序支付配置不完整 club={pay_cfg.get("club_id")} '
+ f'appid={bool(appid)} mch_id={bool(mch_id)} mch_key={bool(key)}'
)
- if not openid:
- raise Exception('缺少服务号 openid')
nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
total_fee = yuan_to_fen(jine)
@@ -255,7 +286,7 @@ def build_official_jsapi_pay_params(
)
parsed = parse_wechat_v2_unifiedorder_response(
response.content,
- log_prefix='WX_NATIVE_ALBUM_JSAPI',
+ log_prefix='WX_NATIVE_MINICODE_JSAPI',
expect='prepay_id',
)
prepay_id = parsed['prepay_id']
@@ -283,3 +314,101 @@ def build_official_jsapi_pay_params(
'signType': sign_type,
'paySign': pay_sign,
}
+
+
+# 兼容旧 H5 中转页导入(若仍有请求)
+def build_native_bridge_url(dingdanid: str, club_id=None) -> str:
+ from urllib.parse import quote
+ q = f'out_trade_no={quote(dingdanid or "", safe="")}'
+ if club_id:
+ q += f'&club_id={quote(str(club_id), safe="")}'
+ return f'https://www.abas.asia/hqhd/dingdan/wx-native-pay/?{q}'
+
+
+def build_official_jsapi_pay_params(**kwargs):
+ """保留给旧 H5 页;新主路径已改小程序码。"""
+ kwargs = dict(kwargs)
+ # 复用 mini 逻辑会错 openid 体系;仍走服务号
+ from jituan.services.wechat_pay import get_wechat_v2_config as _cfg
+ pay_cfg = _cfg(kwargs.get('club_id'), app_source='official')
+ log_wechat_v2_pay_diag(
+ pay_cfg,
+ scene='native_album_jsapi_legacy',
+ openid=kwargs.get('openid'),
+ order_hint=kwargs.get('dingdanid'),
+ )
+ appid = pay_cfg.get('appid') or ''
+ mch_id = pay_cfg.get('mch_id') or ''
+ key = pay_cfg.get('key') or ''
+ openid = kwargs.get('openid') or ''
+ if not all([appid, mch_id, key, openid]):
+ raise Exception('服务号支付配置不完整')
+ dingdanid = kwargs.get('dingdanid')
+ jine = kwargs.get('jine')
+ body = kwargs.get('body') or '微信支付'
+ notify_url = kwargs.get('notify_url')
+ client_ip = kwargs.get('client_ip') or '127.0.0.1'
+ nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
+ total_fee = yuan_to_fen(jine)
+ ip = (client_ip or '127.0.0.1').split(',')[0].strip() or '127.0.0.1'
+ sign_data = {
+ 'appid': appid,
+ 'mch_id': mch_id,
+ 'nonce_str': nonce_str,
+ 'body': body,
+ 'out_trade_no': dingdanid,
+ 'total_fee': total_fee,
+ 'spbill_create_ip': ip,
+ 'notify_url': notify_url,
+ 'trade_type': 'JSAPI',
+ 'openid': openid,
+ }
+ sorted_items = sorted(sign_data.items(), key=lambda x: x[0])
+ sign_string = '&'.join(f'{k}={v}' for k, v in sorted_items if v) + f'&key={key}'
+ sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
+ xml_data = f"""
+{appid}
+{body}
+{mch_id}
+{nonce_str}
+{notify_url}
+{openid}
+{dingdanid}
+{ip}
+{total_fee}
+JSAPI
+{sign}
+"""
+ response = requests.post(
+ 'https://api.mch.weixin.qq.com/pay/unifiedorder',
+ data=xml_data.encode('utf-8'),
+ headers={'Content-Type': 'application/xml'},
+ timeout=10,
+ )
+ parsed = parse_wechat_v2_unifiedorder_response(
+ response.content, log_prefix='WX_NATIVE_OA_JSAPI', expect='prepay_id',
+ )
+ prepay_id = parsed['prepay_id']
+ timestamp = str(int(time.time()))
+ pay_nonce = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
+ package = f'prepay_id={prepay_id}'
+ sign_type = 'MD5'
+ pay_sign_data = {
+ 'appId': appid,
+ 'timeStamp': timestamp,
+ 'nonceStr': pay_nonce,
+ 'package': package,
+ 'signType': sign_type,
+ }
+ pay_sign_string = '&'.join(
+ f'{k}={pay_sign_data[k]}' for k in sorted(pay_sign_data.keys())
+ ) + f'&key={key}'
+ pay_sign = hashlib.md5(pay_sign_string.encode('utf-8')).hexdigest().upper()
+ return {
+ 'appId': appid,
+ 'timeStamp': timestamp,
+ 'nonceStr': pay_nonce,
+ 'package': package,
+ 'signType': sign_type,
+ 'paySign': pay_sign,
+ }
diff --git a/orders/urls.py b/orders/urls.py
index 5a19390..5dccd07 100644
--- a/orders/urls.py
+++ b/orders/urls.py
@@ -1,6 +1,6 @@
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
-from .views import CreateOrderView, WechatPayNotifyView, AlipayPayNotifyView, alipay_pay_page, wx_native_pay_page, \
+from .views import CreateOrderView, WechatPayNotifyView, AlipayPayNotifyView, alipay_pay_page, wx_native_pay_page, WxNativeScanPayView, \
PaymentFailView, PaymentVerifyView, ContinuePayView, DingdanHuoquView, DingdanXiangqingView, JiedanView, \
ShangpinLeixingView, ShangjiaPaifaView, ShangjiaDingdanHuoquView, ShangjiaJiesuanView, \
ShangjiaDingdanXiangqingView, ShangjiaChufaShenqingView, ShangjiaTuikuanShenqingView, \
@@ -25,6 +25,7 @@ urlpatterns = [
path('fubei-notify/', csrf_exempt(FubeiPayNotifyView.as_view()), name='付呗支付回调'),
path('alipay-pay/', csrf_exempt(alipay_pay_page), name='支付宝H5中转页'),
path('wx-native-pay/', csrf_exempt(wx_native_pay_page), name='微信Native扫码中转页'),
+ path('wx-native-scan-pay', WxNativeScanPayView.as_view(), name='微信小程序码扫码支付'),
path('shibai', PaymentFailView.as_view(), name='老板支付失败接口'),
path('fucha', PaymentVerifyView.as_view(), name='老板支付成功复查接口'),
path('dingdanhuoqu', DingdanHuoquView.as_view(), name='老板订单获取'),
diff --git a/orders/views/__init__.py b/orders/views/__init__.py
index 73c5b70..503cc05 100644
--- a/orders/views/__init__.py
+++ b/orders/views/__init__.py
@@ -11,6 +11,7 @@ from .payment import (
ContinuePayView,
alipay_pay_page,
wx_native_pay_page,
+ WxNativeScanPayView,
)
from .boss_orders import (
DingdanHuoquView,
@@ -75,6 +76,7 @@ __all__ = [
"ContinuePayView",
"alipay_pay_page",
"wx_native_pay_page",
+ "WxNativeScanPayView",
"DingdanHuoquView", "DingdanXiangqingView", "JiedanView",
"DingdanXiangqingView2", "JiedanView2",
"ShangpinLeixingView", "ShangjiaPaifaView", "ShangjiaDingdanHuoquView",
diff --git a/orders/views/payment.py b/orders/views/payment.py
index db60449..ffc296b 100644
--- a/orders/views/payment.py
+++ b/orders/views/payment.py
@@ -651,6 +651,83 @@ def wx_native_pay_page(request):
return render(request, 'wechat/native_pay.html', {'pay': pay, 'out_trade_no': out_trade_no})
+class WxNativeScanPayView(APIView):
+ """相册扫小程序码落地:用当前用户 openid 对未付款单做 JSAPI,直接出密码。"""
+ authentication_classes = [JWTAuthentication]
+ permission_classes = [IsAuthenticated]
+
+ def post(self, request):
+ from jituan.services.wechat_native_pay import (
+ build_mini_jsapi_pay_params,
+ get_native_pay_session,
+ resolve_scene_to_dingdanid,
+ )
+
+ scene = (request.data.get('scene') or '').strip()
+ dingdanid = (request.data.get('dingdanid') or request.data.get('out_trade_no') or '').strip()
+ if scene and not dingdanid:
+ dingdanid = resolve_scene_to_dingdanid(scene)
+ if not dingdanid:
+ return Response({'code': 400, 'message': '无效支付码'}, status=status.HTTP_400_BAD_REQUEST)
+
+ session = get_native_pay_session(dingdanid)
+ if not session:
+ return Response({'code': 400, 'message': '支付已过期,请返回重新发起'}, status=status.HTTP_400_BAD_REQUEST)
+
+ club_id = (session.get('club_id') or '').strip()
+ notify_url = (session.get('notify_url') or '').strip()
+ if not notify_url:
+ if dingdanid.startswith('CzHY'):
+ notify_url = getattr(settings, 'HUIYUAN_PAY_NOTIFY_URL', '')
+ elif dingdanid.startswith('CzYJ'):
+ notify_url = getattr(settings, 'YAJIN_PAY_NOTIFY_URL', '')
+ elif dingdanid.startswith('CzJF'):
+ notify_url = getattr(settings, 'JIFEN_PAY_NOTIFY_URL', '')
+ elif dingdanid.startswith('CzSJ'):
+ notify_url = getattr(settings, 'SHANGJIA_CZ_PAY_NOTIFY_URL', '')
+ elif dingdanid.startswith('FK'):
+ notify_url = getattr(settings, 'FAKUAN_PAY_NOTIFY_URL', '')
+ elif dingdanid.startswith('KH'):
+ notify_url = getattr(settings, 'KAOHE_PAY_NOTIFY_URL', '')
+ elif dingdanid.startswith('CzZP'):
+ notify_url = getattr(settings, 'ZHUANPAN_PAY_NOTIFY_URL', '')
+ else:
+ notify_url = getattr(settings, 'WEIXIN_NOTIFY_URL', '')
+
+ pay_openid, _ = get_payment_openid(request, club_id=club_id or None)
+ if not pay_openid:
+ return Response({
+ 'code': 400,
+ 'message': '当前小程序未绑定微信,请退出后重新登录再支付',
+ }, status=status.HTTP_400_BAD_REQUEST)
+
+ 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_params = build_mini_jsapi_pay_params(
+ dingdanid=dingdanid,
+ jine=session.get('jine'),
+ body=session.get('body') or '微信支付',
+ notify_url=notify_url,
+ openid=pay_openid,
+ club_id=club_id or None,
+ client_ip=client_ip,
+ )
+ except Exception as exc:
+ logger.exception('小程序码扫码 JSAPI 下单失败 dingdan=%s', dingdanid)
+ return Response({
+ 'code': 500,
+ 'message': f'下单失败:{exc}',
+ }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+ return Response({
+ 'code': 200,
+ 'message': 'ok',
+ 'dingdanid': dingdanid,
+ 'payParams': pay_params,
+ })
+
+
class PaymentVerifyView(APIView):
"""
订单支付成功复查接口 - 前端支付成功后调用,确认订单状态