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.urls import path
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from .views import CreateOrderView, WechatPayNotifyView, AlipayPayNotifyView, alipay_pay_page, \
|
from .views import CreateOrderView, WechatPayNotifyView, AlipayPayNotifyView, alipay_pay_page, \
|
||||||
PaymentFailView, PaymentVerifyView, DingdanHuoquView, DingdanXiangqingView, JiedanView, \
|
PaymentFailView, PaymentVerifyView, ContinuePayView, DingdanHuoquView, DingdanXiangqingView, JiedanView, \
|
||||||
ShangpinLeixingView, ShangjiaPaifaView, ShangjiaDingdanHuoquView, ShangjiaJiesuanView, \
|
ShangpinLeixingView, ShangjiaPaifaView, ShangjiaDingdanHuoquView, ShangjiaJiesuanView, \
|
||||||
ShangjiaDingdanXiangqingView, ShangjiaChufaShenqingView, ShangjiaTuikuanShenqingView, \
|
ShangjiaDingdanXiangqingView, ShangjiaChufaShenqingView, ShangjiaTuikuanShenqingView, \
|
||||||
ShangjiaChexiaoView, DashouDingdanHuoquView, ShangpinLeixingHuoquView, QiangdanView, \
|
ShangjiaChexiaoView, DashouDingdanHuoquView, ShangpinLeixingHuoquView, QiangdanView, \
|
||||||
@@ -17,6 +17,7 @@ from jituan.services.fubei_notify import FubeiPayNotifyView
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('xiadan', CreateOrderView.as_view(), name='下单订单生成接口'),
|
path('xiadan', CreateOrderView.as_view(), name='下单订单生成接口'),
|
||||||
|
path('jixuzhifu', ContinuePayView.as_view(), name='待付款继续支付'),
|
||||||
#path('pay-notify/', WechatPayNotifyView.as_view(), name='老板下单微信支付回调'),
|
#path('pay-notify/', WechatPayNotifyView.as_view(), name='老板下单微信支付回调'),
|
||||||
path('pay-notify/', csrf_exempt(WechatPayNotifyView.as_view()), name='老板下单微信支付回调'),
|
path('pay-notify/', csrf_exempt(WechatPayNotifyView.as_view()), name='老板下单微信支付回调'),
|
||||||
path('alipay-notify/', csrf_exempt(AlipayPayNotifyView.as_view()), name='老板下单支付宝支付回调'),
|
path('alipay-notify/', csrf_exempt(AlipayPayNotifyView.as_view()), name='老板下单支付宝支付回调'),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from .payment import (
|
|||||||
AlipayPayNotifyView,
|
AlipayPayNotifyView,
|
||||||
PaymentVerifyView,
|
PaymentVerifyView,
|
||||||
CreateOrderView,
|
CreateOrderView,
|
||||||
|
ContinuePayView,
|
||||||
alipay_pay_page,
|
alipay_pay_page,
|
||||||
)
|
)
|
||||||
from .boss_orders import (
|
from .boss_orders import (
|
||||||
@@ -68,7 +69,8 @@ from .merchant_penalty import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"PaymentFailView", "WechatPayNotifyView", "AlipayPayNotifyView", "PaymentVerifyView", "CreateOrderView",
|
"PaymentFailView", "WechatPayNotifyView", "AlipayPayNotifyView", "PaymentVerifyView", "CreateOrderView",
|
||||||
|
"ContinuePayView",
|
||||||
"alipay_pay_page",
|
"alipay_pay_page",
|
||||||
"DingdanHuoquView", "DingdanXiangqingView", "JiedanView",
|
"DingdanHuoquView", "DingdanXiangqingView", "JiedanView",
|
||||||
"DingdanXiangqingView2", "JiedanView2",
|
"DingdanXiangqingView2", "JiedanView2",
|
||||||
|
|||||||
@@ -1202,4 +1202,165 @@ class CreateOrderView(APIView):
|
|||||||
raise e
|
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