feat: 待付款续付 jixuzhifu,复用原单号且不误改支付状态
仅 Status=9 可续付;续付前渠道查单,已付走现有 fucha 履约;本接口不把未付标成已付。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from django.urls import path
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from .views import CreateOrderView, WechatPayNotifyView, AlipayPayNotifyView, alipay_pay_page, \
|
||||
PaymentFailView, PaymentVerifyView, DingdanHuoquView, DingdanXiangqingView, JiedanView, \
|
||||
PaymentFailView, PaymentVerifyView, ContinuePayView, DingdanHuoquView, DingdanXiangqingView, JiedanView, \
|
||||
ShangpinLeixingView, ShangjiaPaifaView, ShangjiaDingdanHuoquView, ShangjiaJiesuanView, \
|
||||
ShangjiaDingdanXiangqingView, ShangjiaChufaShenqingView, ShangjiaTuikuanShenqingView, \
|
||||
ShangjiaChexiaoView, DashouDingdanHuoquView, ShangpinLeixingHuoquView, QiangdanView, \
|
||||
@@ -17,6 +17,7 @@ from jituan.services.fubei_notify import FubeiPayNotifyView
|
||||
|
||||
urlpatterns = [
|
||||
path('xiadan', CreateOrderView.as_view(), name='下单订单生成接口'),
|
||||
path('jixuzhifu', ContinuePayView.as_view(), name='待付款继续支付'),
|
||||
#path('pay-notify/', WechatPayNotifyView.as_view(), name='老板下单微信支付回调'),
|
||||
path('pay-notify/', csrf_exempt(WechatPayNotifyView.as_view()), name='老板下单微信支付回调'),
|
||||
path('alipay-notify/', csrf_exempt(AlipayPayNotifyView.as_view()), name='老板下单支付宝支付回调'),
|
||||
|
||||
@@ -8,6 +8,7 @@ from .payment import (
|
||||
AlipayPayNotifyView,
|
||||
PaymentVerifyView,
|
||||
CreateOrderView,
|
||||
ContinuePayView,
|
||||
alipay_pay_page,
|
||||
)
|
||||
from .boss_orders import (
|
||||
@@ -68,7 +69,8 @@ from .merchant_penalty import (
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"PaymentFailView", "WechatPayNotifyView", "AlipayPayNotifyView", "PaymentVerifyView", "CreateOrderView",
|
||||
"PaymentFailView", "WechatPayNotifyView", "AlipayPayNotifyView", "PaymentVerifyView", "CreateOrderView",
|
||||
"ContinuePayView",
|
||||
"alipay_pay_page",
|
||||
"DingdanHuoquView", "DingdanXiangqingView", "JiedanView",
|
||||
"DingdanXiangqingView2", "JiedanView2",
|
||||
|
||||
@@ -1202,4 +1202,165 @@ class CreateOrderView(APIView):
|
||||
raise e
|
||||
|
||||
|
||||
class ContinuePayView(APIView):
|
||||
"""
|
||||
待付款续付:复用原 OrderID 重新拉起支付,不新建订单。
|
||||
|
||||
安全约束(硬保证):
|
||||
1. 本接口绝不把 Status 从 9 改成已付;已付只由微信/支付宝回调或 /dingdan/fucha
|
||||
在「渠道查单确认 SUCCESS」后写入(与现网一致)。
|
||||
2. 续付前先查渠道:若其实已付成功,则走现有复查履约,避免「用户已付仍显示待付款」。
|
||||
3. 仅 Status==9 且本人订单可续付;其它状态直接拒绝,不会误改。
|
||||
"""
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [JSONParser]
|
||||
|
||||
def post(self, request):
|
||||
try:
|
||||
dingdanid = (request.data.get('dingdanid') or request.data.get('dingdan_id') or '').strip()
|
||||
pay_method = (request.data.get('pay_method') or 'wechat').strip().lower()
|
||||
if not dingdanid:
|
||||
return Response({'code': 1, 'msg': '订单ID不能为空', 'data': None})
|
||||
if pay_method not in ('wechat', 'alipay'):
|
||||
return Response({'code': 1, 'msg': '不支持的支付方式', 'data': None})
|
||||
|
||||
current_user = request.user
|
||||
boss_id = f"Boss{current_user.UserUID}"
|
||||
|
||||
dingdan = Order.query.filter(OrderID=dingdanid).first()
|
||||
if not dingdan:
|
||||
return Response({'code': 2, 'msg': '订单不存在', 'data': None})
|
||||
if dingdan.User1ID != boss_id:
|
||||
logger.warning('续付越权 dingdanid=%s uid=%s', dingdanid, current_user.UserUID)
|
||||
return Response({'code': 403, 'msg': '无权操作此订单', 'data': None})
|
||||
|
||||
# 已进入正常履约/服务流程:不得再拉起支付,也不得改状态
|
||||
if dingdan.Status in (1, 7):
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '订单已支付',
|
||||
'data': {
|
||||
'dingdanid': dingdanid,
|
||||
'zhuangtai': dingdan.Status,
|
||||
'already_paid': True,
|
||||
},
|
||||
})
|
||||
if dingdan.Status != 9:
|
||||
return Response({
|
||||
'code': 3,
|
||||
'msg': '仅待付款订单可继续支付',
|
||||
'data': {'zhuangtai': dingdan.Status},
|
||||
})
|
||||
|
||||
club_id = getattr(dingdan, 'ClubID', None) or resolve_club_id_from_request(request)
|
||||
|
||||
# 续付前强制查渠道:已付则履约,杜绝「付了还待付款」
|
||||
from jituan.services.pay_confirm import confirm_remote_payment
|
||||
remote = confirm_remote_payment(
|
||||
dingdanid,
|
||||
club_id,
|
||||
prefer_amount=getattr(dingdan, 'Amount', None),
|
||||
)
|
||||
if remote.get('ok'):
|
||||
PaymentVerifyView()._process_payment_success(
|
||||
dingdan,
|
||||
remote.get('transaction_id'),
|
||||
current_user.UserUID,
|
||||
)
|
||||
dingdan.refresh_from_db()
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '订单已支付',
|
||||
'data': {
|
||||
'dingdanid': dingdanid,
|
||||
'zhuangtai': dingdan.Status,
|
||||
'already_paid': True,
|
||||
},
|
||||
})
|
||||
|
||||
# 仍未付:复用原单号重新下预支付单(不改 Status,保持 9)
|
||||
helper = CreateOrderView()
|
||||
user_openid, club_id = get_payment_openid(request, current_user, club_id)
|
||||
if pay_method == 'wechat' and not user_openid:
|
||||
return Response({'code': 10, 'msg': '用户openid不存在', 'data': None})
|
||||
|
||||
jine = dingdan.Amount
|
||||
if jine is None or Decimal(str(jine)) <= 0:
|
||||
return Response({'code': 6, 'msg': '订单金额异常', 'data': None})
|
||||
|
||||
subject = getattr(settings, 'WEIXIN_PAY_DESCRIPTION', '星阙订单')
|
||||
try:
|
||||
if pay_method == 'alipay':
|
||||
pay_params = helper.generate_alipay_pay_params(
|
||||
dingdanid=dingdanid,
|
||||
jine=jine,
|
||||
subject=subject,
|
||||
club_id=club_id,
|
||||
)
|
||||
else:
|
||||
pay_params = helper.generate_wechat_pay_params(
|
||||
dingdanid=dingdanid,
|
||||
jine=jine,
|
||||
beizhu=subject,
|
||||
user_ip=helper.get_client_ip(request),
|
||||
openid=user_openid,
|
||||
club_id=club_id,
|
||||
)
|
||||
except Exception as pay_exc:
|
||||
err_text = str(pay_exc)
|
||||
# 渠道侧可能已付(同 out_trade_no),再查一次并履约,绝不静默标已付
|
||||
if 'ORDERPAID' in err_text.upper() or '已支付' in err_text:
|
||||
remote2 = confirm_remote_payment(
|
||||
dingdanid, club_id, prefer_amount=getattr(dingdan, 'Amount', None),
|
||||
)
|
||||
if remote2.get('ok'):
|
||||
PaymentVerifyView()._process_payment_success(
|
||||
dingdan,
|
||||
remote2.get('transaction_id'),
|
||||
current_user.UserUID,
|
||||
)
|
||||
dingdan.refresh_from_db()
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '订单已支付',
|
||||
'data': {
|
||||
'dingdanid': dingdanid,
|
||||
'zhuangtai': dingdan.Status,
|
||||
'already_paid': True,
|
||||
},
|
||||
})
|
||||
logger.exception('续付拉起支付失败 dingdanid=%s', dingdanid)
|
||||
return Response({'code': 11, 'msg': f'拉起支付失败: {err_text}', 'data': None})
|
||||
|
||||
if not pay_params:
|
||||
return Response({'code': 11, 'msg': '支付参数生成失败', 'data': None})
|
||||
|
||||
# 再次确认本地仍为 9(并发下回调可能已履约)
|
||||
dingdan.refresh_from_db()
|
||||
if dingdan.Status != 9:
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': '订单状态已更新',
|
||||
'data': {
|
||||
'dingdanid': dingdanid,
|
||||
'zhuangtai': dingdan.Status,
|
||||
'already_paid': dingdan.Status in (1, 7),
|
||||
},
|
||||
})
|
||||
|
||||
return Response({
|
||||
'code': 0,
|
||||
'msg': 'ok',
|
||||
'data': {
|
||||
'dingdanid': dingdanid,
|
||||
'payMethod': pay_method,
|
||||
'payParams': pay_params,
|
||||
'zhuangtai': 9,
|
||||
'already_paid': False,
|
||||
},
|
||||
})
|
||||
except Exception as e:
|
||||
logger.exception('ContinuePayView 异常')
|
||||
return Response({'code': 99, 'msg': f'系统错误: {str(e)}', 'data': None})
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user