1074 lines
48 KiB
Python
1074 lines
48 KiB
Python
"""users.views.pay - auto-generated by split script."""
|
||
import os
|
||
import sys
|
||
import json
|
||
import re
|
||
import time
|
||
import uuid
|
||
import random
|
||
import string
|
||
import requests
|
||
import hashlib
|
||
import traceback
|
||
import decimal
|
||
import jwt
|
||
import ipaddress
|
||
import xmltodict
|
||
from datetime import datetime, timedelta
|
||
from decimal import Decimal
|
||
import defusedxml.ElementTree as ET
|
||
|
||
from django.conf import settings
|
||
from django.db import models, transaction, IntegrityError, DatabaseError, connection
|
||
from django.db.models import Q, F, Sum, Count, Case, When, IntegerField, Prefetch
|
||
from django.core.cache import cache
|
||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||
from django.core.exceptions import ObjectDoesNotExist
|
||
from django.shortcuts import get_object_or_404
|
||
from django.utils import timezone
|
||
from django.http import HttpResponse
|
||
from django.views import View
|
||
from django.views.decorators.csrf import csrf_exempt
|
||
from django.contrib.auth.hashers import make_password
|
||
|
||
from rest_framework.views import APIView
|
||
from rest_framework.response import Response
|
||
from rest_framework import permissions, status
|
||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
||
from rest_framework.throttling import AnonRateThrottle
|
||
from rest_framework_simplejwt.tokens import RefreshToken
|
||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||
|
||
from gvsdsdk.fluent import db, func, FQ
|
||
|
||
from utils.oss_utils import validate_image, upload_to_oss, delete_from_oss
|
||
from utils.money import yuan_to_fen
|
||
from utils.fadan_utils import check_fadan_qiangdan_eligible
|
||
from utils.invitationcode_utils import CreateInvitationCode, VerifyInvitationCode
|
||
from users.fadan_fenhong_utils import process_fadan_fenhong
|
||
|
||
from orders.utils import (
|
||
update_daily_payout,
|
||
settle_shangjia_order_guanshi_fenhong
|
||
)
|
||
from backend.utils import (
|
||
update_dashou_daily_by_action, update_guanshi_daily_by_action,
|
||
update_shangjia_daily, update_zuzhang_daily_by_action,
|
||
verify_kefu_permission
|
||
)
|
||
from rank.utils import get_tag_fee, create_shenhe_jilu, validate_shenheguan
|
||
|
||
from ..models import (
|
||
UserBoss, UserDashou, UserShangjia, UserGuanshi,
|
||
UserZuzhang, UserKefu, AdminProfile, OfficialAccountUser,
|
||
TixianAutoRecord, TixianShenheJilu, Tixianjilu, Xiugaijilu,
|
||
RankingRecord
|
||
)
|
||
from users.business_models import User
|
||
from ..tixian_shenhe_services import (
|
||
load_audit_meta_map, refund_balance, sync_audit_and_jilu_status,
|
||
mark_transfer_success, release_collect_quota_for_record,
|
||
close_audit_wechat_failed, query_wechat_transfer_bill,
|
||
check_shijidaozhang_within_limit,
|
||
check_dashou_trial_blocks_commission_withdraw,
|
||
WX_STATE_FAIL, WX_STATE_SUCCESS, WX_STATE_WAIT, WX_STATE_CANCELING,
|
||
)
|
||
from ..tixian_shenhe_views import process_audit_collect
|
||
|
||
from orders.models import (
|
||
Order, PlatformOrderExt, MerchantOrderExt, CommissionRate,
|
||
PenaltyRecord, PenaltyEvidenceImage, RefundRecord, PlayerDeliveryImage,
|
||
Penalty, PenaltyAppealImage
|
||
)
|
||
from products.models import (
|
||
ShangpinLeixing, Huiyuan, Huiyuangoumai, Gsfenhong, Czjilu
|
||
)
|
||
from config.models import Qunpeizhi
|
||
from backend.models import MerchantDailyStats
|
||
from rank.models import (
|
||
KaohePayTemp, Chenghao, ShenheJilu,
|
||
KaoheCishuFeiyong, YonghuChenghao
|
||
)
|
||
from users.models import UserShenheguan
|
||
from products.utils import update_shangpin_daily_stat
|
||
from shop.utils import update_dianpu_daily_stat
|
||
from rank.utils import create_shenhe_jilu_from_temp
|
||
|
||
import logging
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class FaKuanPayView(APIView):
|
||
"""
|
||
罚款微信支付下单接口(完全照抄 YajinGoumai)
|
||
路径:POST /yonghu/fkjn
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
fadan_id = request.data.get('fadan_id')
|
||
if not fadan_id:
|
||
return Response({'code': 400, 'message': '参数不完整'}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
try:
|
||
fadan = Penalty.query.get(id=fadan_id, PenalizedUserID=request.user.UserUID)
|
||
except Penalty.DoesNotExist:
|
||
return Response({'code': 404, 'message': '罚单不存在或不属于您'}, status=status.HTTP_404_NOT_FOUND)
|
||
|
||
if fadan.Status not in [1, 3]:
|
||
return Response({'code': 400, 'message': '罚单当前状态不可支付'}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
jine = float(fadan.FineAmount)
|
||
|
||
timestamp_ms = int(time.time() * 1000)
|
||
timestamp_ns = int(time.time_ns() // 1000) % 1000000
|
||
random_num = random.randint(0, 99)
|
||
dingdanid = f"FK{timestamp_ms:011d}{timestamp_ns:06d}{random_num:02d}"
|
||
|
||
from jituan.services.club_penalty import penalty_cz_club_id
|
||
from jituan.services.club_user import get_payment_openid
|
||
from products.czjilu_types import CZJILU_LEIXING_FAKUAN, penalty_cz_shuoming
|
||
|
||
pay_openid, _ = get_payment_openid(request)
|
||
if not pay_openid:
|
||
return Response({
|
||
'code': 400,
|
||
'message': '用户openid不存在,无法发起微信支付',
|
||
}, status=status.HTTP_400_BAD_REQUEST)
|
||
|
||
chongzhi_jilu = Czjilu.query.create(
|
||
dingdan_id=dingdanid,
|
||
zhuangtai=9,
|
||
yonghuid=request.user.UserUID,
|
||
jine=jine,
|
||
leixing=CZJILU_LEIXING_FAKUAN,
|
||
shuoming=penalty_cz_shuoming(fadan.id),
|
||
club_id=penalty_cz_club_id(fadan),
|
||
)
|
||
|
||
try:
|
||
pay_params = self.generate_wechat_pay_params(
|
||
dingdanid=dingdanid,
|
||
jine=jine,
|
||
openid=pay_openid,
|
||
pay_type='fakuan'
|
||
)
|
||
except Exception as e:
|
||
chongzhi_jilu.delete()
|
||
logger.error(f"生成微信支付参数失败: {str(e)}")
|
||
return Response({
|
||
'code': 500,
|
||
'message': f'支付参数生成失败: {str(e)}'
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
return Response({
|
||
'code': 200,
|
||
'message': '支付参数生成成功',
|
||
'payParams': pay_params,
|
||
'dingdanid': dingdanid
|
||
}, status=status.HTTP_200_OK)
|
||
|
||
except Exception as e:
|
||
logger.error(f"罚款支付下单接口异常: {str(e)}", exc_info=True)
|
||
return Response({
|
||
'code': 500,
|
||
'message': '服务器内部错误,请稍后重试'
|
||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||
|
||
def generate_wechat_pay_params(self, dingdanid, jine, openid, pay_type):
|
||
from jituan.services.club_write import resolve_club_id_for_write
|
||
from jituan.services.wechat_pay import get_wechat_v2_config
|
||
pay_cfg = get_wechat_v2_config(resolve_club_id_for_write(self.request, self.request.user))
|
||
APPID = pay_cfg['appid']
|
||
MCHID = pay_cfg['mch_id']
|
||
KEY = pay_cfg['key']
|
||
|
||
if pay_type == 'fakuan':
|
||
PAY_DESCRIPTION = settings.FAKUAN_PAY_DESCRIPTION
|
||
NOTIFY_URL = settings.FAKUAN_PAY_NOTIFY_URL # 回调地址在这里
|
||
else:
|
||
raise Exception('未知的支付类型')
|
||
|
||
nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
|
||
total_fee = yuan_to_fen(jine)
|
||
trade_type = 'JSAPI'
|
||
user_ip = self.get_client_ip()
|
||
|
||
# 第一次签名
|
||
sign_data = {
|
||
'appid': APPID,
|
||
'mch_id': MCHID,
|
||
'nonce_str': nonce_str,
|
||
'body': PAY_DESCRIPTION,
|
||
'out_trade_no': dingdanid,
|
||
'total_fee': total_fee,
|
||
'spbill_create_ip': user_ip,
|
||
'notify_url': NOTIFY_URL, # 关键:把回调地址传入
|
||
'trade_type': trade_type,
|
||
'openid': openid
|
||
}
|
||
|
||
sorted_sign_data = sorted(sign_data.items(), key=lambda x: x[0])
|
||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_sign_data if value])
|
||
sign_string += f'&key={KEY}'
|
||
sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||
|
||
# 构建 XML,这里必须包含 notify_url
|
||
xml_data = f"""<xml>
|
||
<appid>{APPID}</appid>
|
||
<body>{PAY_DESCRIPTION}</body>
|
||
<mch_id>{MCHID}</mch_id>
|
||
<nonce_str>{nonce_str}</nonce_str>
|
||
<notify_url>{NOTIFY_URL}</notify_url>
|
||
<openid>{openid}</openid>
|
||
<out_trade_no>{dingdanid}</out_trade_no>
|
||
<spbill_create_ip>{user_ip}</spbill_create_ip>
|
||
<total_fee>{total_fee}</total_fee>
|
||
<trade_type>{trade_type}</trade_type>
|
||
<sign>{sign}</sign>
|
||
</xml>"""
|
||
|
||
response = requests.post(
|
||
'https://api.mch.weixin.qq.com/pay/unifiedorder',
|
||
data=xml_data.encode('utf-8'),
|
||
headers={'Content-Type': 'application/xml'},
|
||
timeout=10
|
||
)
|
||
|
||
logger.info(f"罚款微信支付响应: {response.text}")
|
||
|
||
root = ET.fromstring(response.content)
|
||
|
||
return_code_node = root.find('return_code')
|
||
if return_code_node is None or return_code_node.text is None:
|
||
logger.error(f"微信返回缺少return_code,原始: {response.text}")
|
||
raise Exception('微信支付接口异常,缺少return_code')
|
||
return_code = return_code_node.text
|
||
|
||
result_code_node = root.find('result_code')
|
||
result_code = result_code_node.text if result_code_node is not None else None
|
||
|
||
if return_code == 'SUCCESS' and result_code == 'SUCCESS':
|
||
prepay_id_node = root.find('prepay_id')
|
||
if prepay_id_node is None or prepay_id_node.text is None:
|
||
logger.error(f"统一下单成功但无prepay_id,原始: {response.text}")
|
||
raise Exception('微信支付接口异常,缺少prepay_id')
|
||
prepay_id = prepay_id_node.text
|
||
timestamp = str(int(time.time()))
|
||
nonce_str = ''.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': nonce_str,
|
||
'package': package,
|
||
'signType': sign_type
|
||
}
|
||
|
||
sorted_pay_sign_data = sorted(pay_sign_data.items(), key=lambda x: x[0])
|
||
pay_sign_string = '&'.join([f"{key}={value}" for key, value in sorted_pay_sign_data])
|
||
pay_sign_string += f'&key={KEY}'
|
||
pay_sign = hashlib.md5(pay_sign_string.encode('utf-8')).hexdigest().upper()
|
||
|
||
return {
|
||
'appId': APPID,
|
||
'timeStamp': timestamp,
|
||
'nonceStr': nonce_str,
|
||
'package': package,
|
||
'signType': sign_type,
|
||
'paySign': pay_sign
|
||
}
|
||
else:
|
||
err_code = root.find('err_code')
|
||
err_code_des = root.find('err_code_des')
|
||
return_msg = root.find('return_msg')
|
||
error_details = f"return_code: {return_code}"
|
||
if result_code:
|
||
error_details += f", result_code: {result_code}"
|
||
if return_msg is not None and return_msg.text:
|
||
error_details += f", return_msg: {return_msg.text}"
|
||
if err_code is not None and err_code.text:
|
||
error_details += f", err_code: {err_code.text}"
|
||
if err_code_des is not None and err_code_des.text:
|
||
error_details += f", err_code_des: {err_code_des.text}"
|
||
raise Exception(f"微信支付下单失败: {error_details}")
|
||
|
||
def get_client_ip(self):
|
||
request = self.request
|
||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||
if x_forwarded_for:
|
||
ip = x_forwarded_for.split(',')[0]
|
||
else:
|
||
ip = request.META.get('REMOTE_ADDR')
|
||
return ip if ip else '127.0.0.1'
|
||
|
||
|
||
class FaKuanHuitiaoView(View):
|
||
|
||
"""
|
||
罚款支付回调接口(严格借鉴 YajinHuitiao 接口,参数一个不少)
|
||
路径:POST /yonghu/fk_huitiao
|
||
"""
|
||
def post(self, request):
|
||
try:
|
||
# 1. 解析微信回调XML数据
|
||
request_body = request.body
|
||
logger.info(f"收到微信支付回调,原始数据: {request_body.decode('utf-8')}")
|
||
|
||
root = ET.fromstring(request_body)
|
||
|
||
# 获取关键字段
|
||
return_code = root.find('return_code').text
|
||
result_code = root.find('result_code').text
|
||
out_trade_no = root.find('out_trade_no').text # 订单ID
|
||
transaction_id = root.find('transaction_id').text # 微信交易号
|
||
total_fee = int(root.find('total_fee').text) # 支付金额(分)
|
||
|
||
logger.info(f"回调数据解析: 订单{out_trade_no}, 状态{return_code}/{result_code}, 金额{total_fee}分")
|
||
|
||
# 2. 验证基本返回状态
|
||
if return_code != 'SUCCESS' or result_code != 'SUCCESS':
|
||
logger.warning(f"微信支付回调状态异常: {return_code}/{result_code}")
|
||
return self.wechat_response('FAIL', '支付失败')
|
||
|
||
# 3. 验证签名(确保是微信发来的)
|
||
from jituan.services.wechat_pay import club_id_from_czjilu, verify_wechat_v2_signature
|
||
notify_club = club_id_from_czjilu(out_trade_no)
|
||
if not verify_wechat_v2_signature(request_body, notify_club):
|
||
logger.error(f"签名验证失败: {out_trade_no}")
|
||
return self.wechat_response('FAIL', '签名验证失败')
|
||
|
||
# 4. 查询订单
|
||
try:
|
||
order = Czjilu.query.get(dingdan_id=out_trade_no)
|
||
logger.info(f"找到订单: {order.dingdan_id}, 状态: {order.zhuangtai}, 金额: {order.jine}")
|
||
except Czjilu.DoesNotExist:
|
||
logger.error(f"订单不存在: {out_trade_no}")
|
||
return self.wechat_response('FAIL', '订单不存在')
|
||
|
||
from products.czjilu_types import is_penalty_czjilu
|
||
if not is_penalty_czjilu(order):
|
||
logger.error(f"非罚款支付订单误入罚款回调: {out_trade_no}, leixing={order.leixing}")
|
||
return self.wechat_response('FAIL', '订单类型错误')
|
||
|
||
# 5. 检查订单状态是否为未支付(9)
|
||
if order.zhuangtai != 9:
|
||
logger.warning(f"订单状态不是未支付: {order.dingdan_id}, 当前状态: {order.zhuangtai}")
|
||
try:
|
||
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
|
||
repair_wechat_income_if_missing(
|
||
Decimal(str(order.jine)),
|
||
getattr(order, 'club_id', None),
|
||
biz_ref=f'cz:{out_trade_no}',
|
||
)
|
||
except Exception as exc:
|
||
logger.error('罚款回调补记收支失败 %s: %s', out_trade_no, exc, exc_info=True)
|
||
return self.wechat_response('SUCCESS', 'OK')
|
||
|
||
# 6. 验证金额(微信返回的是分,需要转换)
|
||
wechat_amount_yuan = total_fee / 100 # 转换为元
|
||
order_amount_yuan = float(order.jine) # 订单金额(元)
|
||
|
||
# 允许1分钱的误差(浮点数比较)
|
||
if abs(wechat_amount_yuan - order_amount_yuan) > 0.01:
|
||
logger.error(f"金额不一致: 订单金额{order_amount_yuan}元, 支付金额{wechat_amount_yuan}元")
|
||
return self.wechat_response('FAIL', '金额不一致')
|
||
|
||
# 7. 更新订单状态为已支付(3)
|
||
order.zhuangtai = 3
|
||
order.save(update_fields=['zhuangtai'])
|
||
logger.info(f"订单状态更新成功: {order.dingdan_id} -> 3")
|
||
|
||
# 8. 更新罚单状态为已缴纳(2)
|
||
try:
|
||
fadan = None
|
||
m = re.search(r'#fadan:(\d+)', order.shuoming or '')
|
||
if m:
|
||
fadan = Penalty.query.filter(
|
||
id=int(m.group(1)),
|
||
PenalizedUserID=order.yonghuid,
|
||
Status__in=[1, 3],
|
||
).first()
|
||
if not fadan:
|
||
fadan = Penalty.query.filter(
|
||
PenalizedUserID=order.yonghuid,
|
||
FineAmount=order.jine,
|
||
Status__in=[1, 3],
|
||
).order_by('-CreateTime').first()
|
||
if fadan:
|
||
fadan.Status = 2 # 已缴纳
|
||
fadan.save(update_fields=['Status'])
|
||
logger.info(f"罚单 {fadan.id} 已自动标记为已缴纳")
|
||
|
||
success, msg = process_fadan_fenhong(fadan.id)
|
||
if not success:
|
||
logger.error(f"罚单 {fadan.id} 分红处理失败: {msg}")
|
||
else:
|
||
logger.info(f"罚单 {fadan.id} 分红处理完成: {msg}")
|
||
|
||
except Exception as e:
|
||
logger.error(f"更新罚单状态失败: {str(e)}", exc_info=True)
|
||
|
||
# 9. 更新收支记录表
|
||
try:
|
||
jine_decimal = Decimal(str(order.jine)) if not isinstance(order.jine, Decimal) else order.jine
|
||
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
|
||
repair_wechat_income_if_missing(
|
||
jine_decimal,
|
||
getattr(order, 'club_id', None),
|
||
biz_ref=f'cz:{order.dingdan_id}',
|
||
)
|
||
|
||
logger.info(f"收支记录更新成功: 订单{order.dingdan_id}, 金额{jine_decimal}元")
|
||
|
||
except Exception as e:
|
||
# 收支记录更新失败不影响主流程,只记录日志
|
||
logger.error(f"更新收支记录失败: {str(e)}", exc_info=True)
|
||
|
||
# 10. 返回成功响应给微信
|
||
logger.info(f"罚款支付回调处理完成: {out_trade_no}")
|
||
return self.wechat_response('SUCCESS', 'OK')
|
||
|
||
except ET.ParseError as e:
|
||
logger.error(f"XML解析失败: {str(e)}")
|
||
return self.wechat_response('FAIL', '数据格式错误')
|
||
except Exception as e:
|
||
logger.error(f"罚款支付回调处理异常: {str(e)}", exc_info=True)
|
||
# 返回FAIL让微信重试
|
||
return self.wechat_response('FAIL', '处理异常')
|
||
|
||
def verify_signature(self, xml_data):
|
||
"""验证微信支付签名(完全照搬 YajinHuitiao 的 verify_signature)"""
|
||
try:
|
||
root = ET.fromstring(xml_data)
|
||
sign = root.find('sign').text
|
||
|
||
if not sign:
|
||
logger.error("签名字段不存在")
|
||
return False
|
||
|
||
# 获取所有参数并排除sign
|
||
params = {}
|
||
for child in root:
|
||
if child.tag != 'sign' and child.text is not None:
|
||
params[child.tag] = child.text
|
||
|
||
# 按照参数名ASCII码从小到大排序
|
||
sorted_params = sorted(params.items(), key=lambda x: x[0])
|
||
|
||
# 拼接字符串
|
||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
|
||
sign_string += f'&key={settings.WEIXIN_SHANGHUMIYAO}'
|
||
|
||
# 计算签名
|
||
calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||
|
||
is_valid = sign == calculated_sign
|
||
if not is_valid:
|
||
logger.error(f"签名验证失败: 计算签名{calculated_sign}, 收到签名{sign}")
|
||
|
||
return is_valid
|
||
|
||
except Exception as e:
|
||
logger.error(f"验证签名异常: {str(e)}")
|
||
return False
|
||
|
||
def wechat_response(self, return_code, return_msg):
|
||
"""返回微信支付回调响应(完全照搬 YajinHuitiao 的 wechat_response)"""
|
||
xml_response = f"""<xml>
|
||
<return_code><![CDATA[{return_code}]]></return_code>
|
||
<return_msg><![CDATA[{return_msg}]]></return_msg>
|
||
</xml>"""
|
||
|
||
logger.info(f"返回微信响应: {return_code} - {return_msg}")
|
||
return HttpResponse(xml_response, content_type='application/xml')
|
||
|
||
|
||
class FaKuanPayPollView(APIView):
|
||
"""罚款支付轮询接口(借鉴 YajinLunxun)"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
dingdanid = request.data.get('dingdanid')
|
||
if not dingdanid:
|
||
return Response({'code': 400, 'message': '订单ID不能为空'})
|
||
|
||
order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.UserUID)
|
||
if order.zhuangtai == 3:
|
||
return Response({'code': 200, 'message': '支付成功'})
|
||
else:
|
||
return Response({'code': 400, 'message': '订单尚未支付'})
|
||
except Czjilu.DoesNotExist:
|
||
return Response({'code': 400, 'message': '订单不存在'})
|
||
except Exception as e:
|
||
logger.error(f"轮询异常: {str(e)}")
|
||
return Response({'code': 500, 'message': '服务器内部错误'})
|
||
|
||
|
||
class FaKuanPayFailView(APIView):
|
||
"""罚款支付失败处理接口(借鉴 YajinShibai)"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
dingdanid = request.data.get('dingdanid')
|
||
if not dingdanid:
|
||
return Response({'code': 400, 'message': '订单ID不能为空'})
|
||
|
||
order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.UserUID)
|
||
if order.zhuangtai == 9: # 未支付
|
||
order.delete()
|
||
logger.info(f"用户{request.user.UserUID}删除未支付订单: {dingdanid}")
|
||
return Response({'code': 200, 'message': '订单已删除'})
|
||
else:
|
||
return Response({'code': 400, 'message': '订单状态不是未支付,无法删除'})
|
||
except Czjilu.DoesNotExist:
|
||
return Response({'code': 400, 'message': '订单不存在'})
|
||
except Exception as e:
|
||
logger.error(f"支付失败处理异常: {str(e)}")
|
||
return Response({'code': 500, 'message': '服务器内部错误'})
|
||
|
||
|
||
class KaohePayView(APIView):
|
||
"""
|
||
考核支付下单接口
|
||
路径:POST /yonghu/khzf
|
||
参数:
|
||
tag_id: 标签ID
|
||
game_nick: 游戏昵称
|
||
remark: 备注
|
||
reapply: 是否再次申请 (true/false)
|
||
jilu_id: 再次申请时的原记录ID(reapply=true时必传)
|
||
shenheguan_id: 指定考核官ID(可选)
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
user = request.user
|
||
tag_id = request.data.get('tag_id')
|
||
game_nick = request.data.get('game_nick', '').strip()
|
||
remark = request.data.get('remark', '').strip()
|
||
reapply = request.data.get('reapply', False)
|
||
jilu_id = request.data.get('jilu_id') if reapply else None
|
||
shenheguan_id = request.data.get('shenheguan_id', '').strip()
|
||
|
||
logger.info(f"用户 {user.UserUID} 发起考核支付申请,标签ID: {tag_id}, 再次申请: {reapply}")
|
||
|
||
if not tag_id or not game_nick:
|
||
logger.warning(f"用户 {user.UserUID} 参数不完整: tag_id={tag_id}, game_nick={game_nick}")
|
||
return Response({'code': 400, 'message': '标签ID和游戏昵称不能为空'})
|
||
|
||
# 1. 检查是否有未支付的考核订单
|
||
"""unpaid_order = Czjilu.query.filter(
|
||
UserUID=user.UserUID,
|
||
leixing=5,
|
||
zhuangtai=9
|
||
).exists()
|
||
if unpaid_order:
|
||
logger.warning(f"用户 {user.UserUID} 存在未支付的考核订单")
|
||
return Response({'code': 400, 'message': '您有未支付的考核订单,请先完成支付或取消'})"""
|
||
|
||
# 2. 验证标签
|
||
try:
|
||
chenghao = Chenghao.query.get(id=tag_id)
|
||
logger.info(f"标签验证通过: {chenghao.mingcheng}")
|
||
except Chenghao.DoesNotExist:
|
||
logger.error(f"标签不存在: tag_id={tag_id}")
|
||
return Response({'code': 404, 'message': '标签不存在'})
|
||
|
||
# 3. 检查用户是否已拥有该标签
|
||
if YonghuChenghao.query.filter(yonghu=user, chenghao=chenghao).exists():
|
||
logger.warning(f"用户 {user.UserUID} 已拥有标签 {chenghao.mingcheng}")
|
||
return Response({'code': 400, 'message': '您已拥有该标签,无需重复考核'})
|
||
|
||
# 4. 检查是否有未完成的申请
|
||
if ShenheJilu.query.filter(
|
||
shenqingren_id=user.UserUID,
|
||
chenghao=chenghao,
|
||
zhuangtai__in=[0, 3] # 审核中、待审核
|
||
).exists():
|
||
logger.warning(f"用户 {user.UserUID} 有未完成的考核申请")
|
||
return Response({'code': 400, 'message': '您有未完成的考核申请,请等待结果'})
|
||
|
||
# 5. 计算考核次数和费用
|
||
if reapply and jilu_id:
|
||
last = ShenheJilu.query.filter(
|
||
jilu_id=jilu_id,
|
||
shenqingren_id=user.UserUID,
|
||
zhuangtai=2
|
||
).first()
|
||
if not last:
|
||
logger.warning(f"用户 {user.UserUID} 再次申请失败:原记录不存在或状态不是未通过,jilu_id={jilu_id}")
|
||
return Response({'code': 400, 'message': '原记录不存在或状态不是未通过'})
|
||
new_cishu = last.cishu + 1
|
||
logger.info(f"再次申请,原记录ID: {jilu_id},新次数: {new_cishu}")
|
||
else:
|
||
max_cishu = ShenheJilu.query.filter(
|
||
shenqingren_id=user.UserUID,
|
||
chenghao=chenghao,
|
||
zhuangtai__in=[1, 2]
|
||
).aggregate(max=models.Max('cishu'))['max'] or 0
|
||
new_cishu = max_cishu + 1
|
||
logger.info(f"首次申请,计算次数: {new_cishu}")
|
||
|
||
# 6. 获取本次费用
|
||
fee = get_tag_fee(chenghao, new_cishu)
|
||
logger.info(f"本次考核费用: {fee}元,次数: {new_cishu}")
|
||
|
||
if fee <= 0:
|
||
logger.warning(f"费用为0,应使用免费申请接口,tag_id={tag_id}, cishu={new_cishu}")
|
||
return Response({'code': 400, 'message': '本次费用为0,请直接使用免费申请接口'})
|
||
|
||
# 7. 生成订单ID
|
||
timestamp_ms = int(time.time() * 1000)
|
||
timestamp_ns = int(time.time_ns() // 1000) % 1000000
|
||
random_num = random.randint(0, 99)
|
||
dingdanid = f"KH{timestamp_ms:011d}{timestamp_ns:06d}{random_num:02d}"
|
||
logger.info(f"生成订单ID: {dingdanid}")
|
||
|
||
# 8. 创建支付订单(Czjilu)
|
||
from jituan.services.club_write import resolve_club_id_for_write
|
||
order = Czjilu.query.create(
|
||
dingdan_id=dingdanid,
|
||
zhuangtai=9, # 未支付
|
||
yonghuid=user.UserUID,
|
||
jine=float(fee),
|
||
leixing=5, # 5-考核支付
|
||
shuoming=settings.KAOHE_PAY_DESCRIPTION,
|
||
club_id=resolve_club_id_for_write(request, user),
|
||
)
|
||
logger.info(f"创建支付订单成功: {dingdanid}")
|
||
|
||
# 9. 保存考核支付临时上下文(用于回调时取业务参数)
|
||
try:
|
||
KaohePayTemp.query.create(
|
||
dingdan_id=dingdanid,
|
||
yonghuid=user.UserUID,
|
||
tag_id=tag_id,
|
||
game_nick=game_nick,
|
||
remark=remark,
|
||
reapply=reapply,
|
||
jilu_id=jilu_id if reapply else None,
|
||
shenheguan_id=shenheguan_id,
|
||
cishu=new_cishu,
|
||
fee=fee,
|
||
)
|
||
logger.info(f"保存临时上下文成功: {dingdanid}")
|
||
except Exception as e:
|
||
logger.error(f"保存临时上下文失败: {str(e)}", exc_info=True)
|
||
# 临时表保存失败应该回滚订单
|
||
order.delete()
|
||
return Response({'code': 500, 'message': '系统异常,请稍后重试'})
|
||
|
||
# 10. 构造 attach 参数(只传订单ID,确保不超127字节)
|
||
attach_str = json.dumps({'oid': dingdanid}, ensure_ascii=False)
|
||
logger.info(f"attach内容: {attach_str},长度: {len(attach_str.encode('utf-8'))}字节")
|
||
|
||
# 11. 生成微信支付参数
|
||
try:
|
||
pay_params = self.generate_wechat_pay_params(
|
||
dingdanid=dingdanid,
|
||
jine=float(fee),
|
||
openid=user.OpenID,
|
||
attach=attach_str,
|
||
pay_type='kaohe'
|
||
)
|
||
logger.info(f"微信支付参数生成成功: {dingdanid}")
|
||
except Exception as e:
|
||
order.delete() # 下单失败,删除订单记录
|
||
# 同时删除临时表记录
|
||
try:
|
||
KaohePayTemp.query.filter(dingdan_id=dingdanid).delete()
|
||
except Exception as del_e:
|
||
logger.error(f"删除临时表记录失败: {str(del_e)}")
|
||
logger.error(f"生成微信支付参数失败: {str(e)}", exc_info=True)
|
||
return Response({'code': 500, 'message': f'支付参数生成失败: {str(e)}'})
|
||
|
||
return Response({
|
||
'code': 200,
|
||
'message': '支付参数生成成功',
|
||
'payParams': pay_params,
|
||
'dingdanid': dingdanid
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.error(f"考核支付下单异常: {str(e)}", exc_info=True)
|
||
return Response({'code': 500, 'message': '服务器内部错误'})
|
||
|
||
def generate_wechat_pay_params(self, dingdanid, jine, openid, attach, pay_type):
|
||
"""生成微信JSAPI支付参数"""
|
||
from jituan.services.club_write import resolve_club_id_for_write
|
||
from jituan.services.wechat_pay import get_wechat_v2_config
|
||
pay_cfg = get_wechat_v2_config(resolve_club_id_for_write(self.request, self.request.user))
|
||
APPID = pay_cfg['appid']
|
||
MCHID = pay_cfg['mch_id']
|
||
KEY = pay_cfg['key']
|
||
|
||
if pay_type == 'kaohe':
|
||
PAY_DESCRIPTION = settings.KAOHE_PAY_DESCRIPTION
|
||
NOTIFY_URL = settings.KAOHE_PAY_NOTIFY_URL
|
||
else:
|
||
raise Exception('未知支付类型')
|
||
|
||
nonce_str = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
|
||
total_fee = yuan_to_fen(jine)
|
||
trade_type = 'JSAPI'
|
||
user_ip = self.get_client_ip()
|
||
|
||
logger.info(f"微信支付下单参数: out_trade_no={dingdanid}, total_fee={total_fee}, openid={openid}, ip={user_ip}")
|
||
|
||
# 第一次签名(统一下单)
|
||
sign_data = {
|
||
'appid': APPID,
|
||
'mch_id': MCHID,
|
||
'nonce_str': nonce_str,
|
||
'body': PAY_DESCRIPTION,
|
||
'out_trade_no': dingdanid,
|
||
'total_fee': total_fee,
|
||
'spbill_create_ip': user_ip,
|
||
'notify_url': NOTIFY_URL,
|
||
'trade_type': trade_type,
|
||
'openid': openid,
|
||
'attach': attach,
|
||
}
|
||
|
||
sorted_sign_data = sorted(sign_data.items(), key=lambda x: x[0])
|
||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_sign_data if value])
|
||
sign_string += f'&key={KEY}'
|
||
sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||
|
||
# 构建XML请求体
|
||
xml_data = f"""<xml>
|
||
<appid>{APPID}</appid>
|
||
<body>{PAY_DESCRIPTION}</body>
|
||
<mch_id>{MCHID}</mch_id>
|
||
<nonce_str>{nonce_str}</nonce_str>
|
||
<notify_url>{NOTIFY_URL}</notify_url>
|
||
<openid>{openid}</openid>
|
||
<out_trade_no>{dingdanid}</out_trade_no>
|
||
<spbill_create_ip>{user_ip}</spbill_create_ip>
|
||
<total_fee>{total_fee}</total_fee>
|
||
<trade_type>{trade_type}</trade_type>
|
||
<attach><![CDATA[{attach}]]></attach>
|
||
<sign>{sign}</sign>
|
||
</xml>"""
|
||
|
||
logger.info(f"微信支付请求XML: {xml_data[:500]}...") # 只打印前500字符
|
||
|
||
response = requests.post(
|
||
'https://api.mch.weixin.qq.com/pay/unifiedorder',
|
||
data=xml_data.encode('utf-8'),
|
||
headers={'Content-Type': 'application/xml'},
|
||
timeout=10
|
||
)
|
||
|
||
logger.info(f"微信支付响应: {response.text}")
|
||
|
||
root = ET.fromstring(response.content)
|
||
|
||
# 安全获取return_code
|
||
return_code_node = root.find('return_code')
|
||
if return_code_node is None or return_code_node.text is None:
|
||
logger.error(f"微信返回缺少return_code节点,原始内容: {response.text}")
|
||
raise Exception('微信支付接口异常,缺少return_code')
|
||
return_code = return_code_node.text
|
||
|
||
# 安全获取result_code
|
||
result_code_node = root.find('result_code')
|
||
if result_code_node is None or result_code_node.text is None:
|
||
logger.error(f"微信返回缺少result_code节点,原始内容: {response.text}")
|
||
raise Exception('微信支付接口异常,缺少result_code')
|
||
result_code = result_code_node.text
|
||
|
||
if return_code == 'SUCCESS' and result_code == 'SUCCESS':
|
||
prepay_id_node = root.find('prepay_id')
|
||
if prepay_id_node is None or prepay_id_node.text is None:
|
||
logger.error(f"统一下单成功但未返回prepay_id,原始内容: {response.text}")
|
||
raise Exception('微信支付接口异常,缺少prepay_id')
|
||
prepay_id = prepay_id_node.text
|
||
|
||
timestamp = str(int(time.time()))
|
||
nonce_str = ''.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': nonce_str,
|
||
'package': package,
|
||
'signType': sign_type,
|
||
}
|
||
sorted_pay_sign_data = sorted(pay_sign_data.items(), key=lambda x: x[0])
|
||
pay_sign_string = '&'.join([f"{key}={value}" for key, value in sorted_pay_sign_data])
|
||
pay_sign_string += f'&key={KEY}'
|
||
pay_sign = hashlib.md5(pay_sign_string.encode('utf-8')).hexdigest().upper()
|
||
|
||
logger.info(f"微信支付预下单成功,prepay_id={prepay_id}")
|
||
|
||
return {
|
||
'appId': APPID,
|
||
'timeStamp': timestamp,
|
||
'nonceStr': nonce_str,
|
||
'package': package,
|
||
'signType': sign_type,
|
||
'paySign': pay_sign,
|
||
}
|
||
else:
|
||
# 安全获取错误信息
|
||
err_code_node = root.find('err_code')
|
||
err_code = err_code_node.text if err_code_node is not None else '未知错误码'
|
||
err_code_des_node = root.find('err_code_des')
|
||
err_code_des = err_code_des_node.text if err_code_des_node is not None else '未知错误描述'
|
||
error_details = f"return_code: {return_code}, result_code: {result_code}, err_code: {err_code}, err_code_des: {err_code_des}"
|
||
logger.error(f"微信支付下单失败: {error_details}")
|
||
raise Exception(f"微信支付下单失败: {error_details}")
|
||
|
||
def get_client_ip(self):
|
||
"""获取客户端IP"""
|
||
request = self.request
|
||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||
if x_forwarded_for:
|
||
ip = x_forwarded_for.split(',')[0]
|
||
else:
|
||
ip = request.META.get('REMOTE_ADDR', '127.0.0.1')
|
||
return ip if ip else '127.0.0.1'
|
||
|
||
|
||
class KaohePayCallbackView(View):
|
||
"""
|
||
考核支付回调接口
|
||
路径:/yonghu/kh_huitiao
|
||
"""
|
||
|
||
def post(self, request):
|
||
try:
|
||
request_body = request.body
|
||
logger.info(f"收到考核支付回调: {request_body.decode('utf-8')}")
|
||
|
||
root = ET.fromstring(request_body)
|
||
|
||
# 安全获取return_code
|
||
return_code_node = root.find('return_code')
|
||
if return_code_node is None or return_code_node.text is None:
|
||
logger.error(f"微信回调XML缺少return_code节点,原始内容: {request_body.decode('utf-8')}")
|
||
return self.wechat_response('FAIL', '数据格式错误')
|
||
return_code = return_code_node.text
|
||
|
||
# 安全获取result_code
|
||
result_code_node = root.find('result_code')
|
||
if result_code_node is None or result_code_node.text is None:
|
||
logger.error(f"微信回调XML缺少result_code节点,原始内容: {request_body.decode('utf-8')}")
|
||
return self.wechat_response('FAIL', '数据格式错误')
|
||
result_code = result_code_node.text
|
||
|
||
# 安全获取out_trade_no
|
||
out_trade_no_node = root.find('out_trade_no')
|
||
if out_trade_no_node is None or out_trade_no_node.text is None:
|
||
logger.error(f"微信回调XML缺少out_trade_no节点,原始内容: {request_body.decode('utf-8')}")
|
||
return self.wechat_response('FAIL', '数据格式错误')
|
||
out_trade_no = out_trade_no_node.text
|
||
|
||
# 安全获取transaction_id
|
||
transaction_id_node = root.find('transaction_id')
|
||
if transaction_id_node is None or transaction_id_node.text is None:
|
||
logger.error(f"微信回调XML缺少transaction_id节点,原始内容: {request_body.decode('utf-8')}")
|
||
return self.wechat_response('FAIL', '数据格式错误')
|
||
transaction_id = transaction_id_node.text
|
||
|
||
# 安全获取total_fee
|
||
total_fee_node = root.find('total_fee')
|
||
if total_fee_node is None or total_fee_node.text is None:
|
||
logger.error(f"微信回调XML缺少total_fee节点,原始内容: {request_body.decode('utf-8')}")
|
||
return self.wechat_response('FAIL', '数据格式错误')
|
||
total_fee = int(total_fee_node.text)
|
||
|
||
logger.info(
|
||
f"回调参数解析成功: out_trade_no={out_trade_no}, transaction_id={transaction_id}, total_fee={total_fee}")
|
||
|
||
if return_code != 'SUCCESS' or result_code != 'SUCCESS':
|
||
logger.warning(f"支付状态异常: return_code={return_code}, result_code={result_code}")
|
||
return self.wechat_response('FAIL', '支付失败')
|
||
|
||
# 验证签名
|
||
from jituan.services.wechat_pay import club_id_from_czjilu, verify_wechat_v2_signature
|
||
notify_club = club_id_from_czjilu(out_trade_no)
|
||
if not verify_wechat_v2_signature(request_body, notify_club):
|
||
logger.error(f"签名验证失败,订单号: {out_trade_no}")
|
||
return self.wechat_response('FAIL', '签名验证失败')
|
||
logger.info(f"签名验证成功,订单号: {out_trade_no}")
|
||
|
||
# 查找订单
|
||
try:
|
||
order = Czjilu.query.get(dingdan_id=out_trade_no, leixing=5)
|
||
logger.info(f"找到订单: {out_trade_no}, 当前状态: {order.zhuangtai}")
|
||
except Czjilu.DoesNotExist:
|
||
logger.error(f"考核订单不存在: {out_trade_no}")
|
||
return self.wechat_response('FAIL', '订单不存在')
|
||
|
||
if order.zhuangtai != 9:
|
||
logger.warning(f"订单状态非未支付: {order.zhuangtai}, 订单号: {out_trade_no}")
|
||
if order.zhuangtai == 3:
|
||
logger.info(f"订单已支付,补试收支记账: {out_trade_no}")
|
||
try:
|
||
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
|
||
repair_wechat_income_if_missing(
|
||
Decimal(str(order.jine)),
|
||
getattr(order, 'club_id', None),
|
||
biz_ref=f'cz:{out_trade_no}',
|
||
)
|
||
except Exception as exc:
|
||
logger.error('考核回调补记收支失败 %s: %s', out_trade_no, exc, exc_info=True)
|
||
return self.wechat_response('SUCCESS', 'OK')
|
||
return self.wechat_response('FAIL', '订单状态异常')
|
||
|
||
# 验证金额
|
||
wechat_yuan = total_fee / 100
|
||
if abs(wechat_yuan - float(order.jine)) > 0.01:
|
||
logger.error(f"金额不一致: 订单金额={order.jine}, 回调金额={wechat_yuan}, 订单号: {out_trade_no}")
|
||
return self.wechat_response('FAIL', '金额不一致')
|
||
logger.info(f"金额验证通过: {wechat_yuan}元")
|
||
|
||
# 更新订单状态为已支付
|
||
order.zhuangtai = 3
|
||
order.save(update_fields=['zhuangtai'])
|
||
logger.info(f"订单状态更新为已支付: {out_trade_no}")
|
||
|
||
# 从临时表获取业务参数并创建审核记录
|
||
try:
|
||
temp = KaohePayTemp.query.get(dingdan_id=out_trade_no, yonghuid=order.yonghuid)
|
||
logger.info(f"找到临时表记录: {out_trade_no}, 用户ID: {order.yonghuid}")
|
||
|
||
user = User.query.get(UserUID=order.yonghuid)
|
||
logger.info(f"获取用户信息成功: {user.UserUID}")
|
||
|
||
success, msg, record = create_shenhe_jilu_from_temp(user, temp)
|
||
if success:
|
||
logger.info(f"创建审核记录成功: jilu_id={record.jilu_id}, 订单号: {out_trade_no}")
|
||
else:
|
||
logger.error(f"创建审核记录失败: {msg}, 订单号: {out_trade_no}")
|
||
# 订单已支付但记录创建失败,需要标记订单异常,人工介入
|
||
order.zhuangtai = 10 # 10表示支付成功但业务处理失败
|
||
order.save(update_fields=['zhuangtai'])
|
||
logger.error(f"订单状态标记为异常(10): {out_trade_no}")
|
||
|
||
except KaohePayTemp.DoesNotExist:
|
||
logger.error(f"临时表无记录,订单号: {out_trade_no}, 用户ID: {order.yonghuid}")
|
||
# 尝试从attach解析(兼容旧数据)
|
||
attach_element = root.find('attach')
|
||
if attach_element is not None and attach_element.text:
|
||
try:
|
||
attach_data = json.loads(attach_element.text)
|
||
logger.info(f"从attach解析数据成功: {attach_data}")
|
||
# 这里可以保留旧逻辑,但建议只记录日志
|
||
except Exception as e:
|
||
logger.error(f"解析attach失败: {str(e)}")
|
||
except Exception as e:
|
||
logger.error(f"处理临时表或创建审核记录异常: {str(e)}", exc_info=True)
|
||
# 标记订单异常
|
||
order.zhuangtai = 10
|
||
order.save(update_fields=['zhuangtai'])
|
||
logger.error(f"订单状态标记为异常(10): {out_trade_no}")
|
||
|
||
# 更新收支记录
|
||
try:
|
||
jine_decimal = Decimal(str(order.jine))
|
||
from jituan.services.szjilu_accounting import repair_wechat_income_if_missing
|
||
repair_wechat_income_if_missing(
|
||
jine_decimal,
|
||
getattr(order, 'club_id', None),
|
||
biz_ref=f'cz:{order.dingdan_id}',
|
||
)
|
||
logger.info(f"更新收支记录成功: {jine_decimal}元")
|
||
except Exception as e:
|
||
logger.error(f"更新收支记录失败: {str(e)}", exc_info=True)
|
||
|
||
logger.info(f"考核支付回调处理完成,订单号: {out_trade_no}")
|
||
return self.wechat_response('SUCCESS', 'OK')
|
||
|
||
except ET.ParseError as e:
|
||
logger.error(f"XML解析失败: {str(e)}, 原始数据: {request.body.decode('utf-8')}")
|
||
return self.wechat_response('FAIL', '数据格式错误')
|
||
except Exception as e:
|
||
logger.error(f"考核支付回调异常: {str(e)}", exc_info=True)
|
||
return self.wechat_response('FAIL', '处理异常')
|
||
|
||
def verify_signature(self, xml_data):
|
||
"""验证微信支付签名"""
|
||
try:
|
||
root = ET.fromstring(xml_data)
|
||
sign_node = root.find('sign')
|
||
if sign_node is None or sign_node.text is None:
|
||
logger.error("微信回调XML缺少sign节点")
|
||
return False
|
||
sign = sign_node.text
|
||
|
||
params = {}
|
||
for child in root:
|
||
if child.tag != 'sign' and child.text is not None:
|
||
params[child.tag] = child.text
|
||
|
||
sorted_params = sorted(params.items(), key=lambda x: x[0])
|
||
sign_string = '&'.join([f"{key}={value}" for key, value in sorted_params if value])
|
||
sign_string += f'&key={settings.WEIXIN_SHANGHUMIYAO}'
|
||
calculated_sign = hashlib.md5(sign_string.encode('utf-8')).hexdigest().upper()
|
||
|
||
result = sign == calculated_sign
|
||
if not result:
|
||
logger.error(f"签名验证失败: 原签名={sign}, 计算签名={calculated_sign}")
|
||
return result
|
||
except Exception as e:
|
||
logger.error(f"签名验证异常: {str(e)}", exc_info=True)
|
||
return False
|
||
|
||
def wechat_response(self, return_code, return_msg):
|
||
"""构造微信回调响应XML"""
|
||
xml = f"""<xml>
|
||
<return_code><![CDATA[{return_code}]]></return_code>
|
||
<return_msg><![CDATA[{return_msg}]]></return_msg>
|
||
</xml>"""
|
||
logger.info(f"返回微信响应: {xml}")
|
||
return HttpResponse(xml, content_type='application/xml')
|
||
|
||
|
||
class KaohePayPollView(APIView):
|
||
"""考核支付轮询"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
dingdanid = request.data.get('dingdanid')
|
||
if not dingdanid:
|
||
return Response({'code': 400, 'message': '订单ID不能为空'})
|
||
try:
|
||
order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.UserUID, leixing=5)
|
||
if order.zhuangtai == 3:
|
||
return Response({'code': 200, 'message': '支付成功'})
|
||
return Response({'code': 400, 'message': '订单尚未支付'})
|
||
except Czjilu.DoesNotExist:
|
||
return Response({'code': 400, 'message': '订单不存在'})
|
||
|
||
|
||
class KaohePayFailView(APIView):
|
||
"""考核支付失败处理"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
dingdanid = request.data.get('dingdanid')
|
||
if not dingdanid:
|
||
return Response({'code': 400, 'message': '订单ID不能为空'})
|
||
try:
|
||
order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.UserUID, leixing=5)
|
||
if order.zhuangtai == 9:
|
||
order.delete()
|
||
return Response({'code': 200, 'message': '订单已删除'})
|
||
return Response({'code': 400, 'message': '订单状态无法删除'})
|
||
except Czjilu.DoesNotExist:
|
||
return Response({'code': 400, 'message': '订单不存在'})
|