import logging from django.http import HttpResponse from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from rest_framework.exceptions import AuthenticationFailed from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework import status from rest_framework.views import APIView from apps.api.authentication import ApiKeyAuthentication from apps.api.errors import api_error from apps.api.generation import ( ApiRequestError, generate_image_response, generate_title_response, ) from apps.api.serializers import ( GenerateImageRequestSerializer, GenerateTitleRequestSerializer, ) from apps.billing.payment_gateways import ( PaymentVerificationError, verify_alipay_callback, verify_wechat_callback, ) from apps.billing.services import ( InvalidRechargeOrderStateError, RechargeAmountMismatchError, RechargeCallbackError, RechargeOrderNotFoundError, RechargePayMethodMismatchError, apply_recharge_payment, get_balance_snapshot, ) logger = logging.getLogger(__name__) class ExternalApiView(APIView): authentication_classes = (ApiKeyAuthentication,) permission_classes = (IsAuthenticated,) def permission_denied(self, request, message=None, code=None): if request.authenticators and not request.successful_authenticator: raise AuthenticationFailed(api_error("unauthorized", "缺失或无效 API Key")) super().permission_denied(request, message=message, code=code) class GenerateTitleView(ExternalApiView): def post(self, request): serializer = GenerateTitleRequestSerializer(data=request.data) if not serializer.is_valid(): return Response( api_error("bad_request", "参数错误"), status=status.HTTP_400_BAD_REQUEST, ) try: data = generate_title_response( user=request.user, api_key=request.auth, request_data=serializer.validated_data, ) except ApiRequestError as exc: return Response(exc.as_response_data(), status=exc.http_status) return Response(data, status=status.HTTP_200_OK) class GenerateImageView(ExternalApiView): def post(self, request): serializer = GenerateImageRequestSerializer(data=request.data) if not serializer.is_valid(): return Response( api_error("bad_request", "参数错误"), status=status.HTTP_400_BAD_REQUEST, ) try: data = generate_image_response( user=request.user, api_key=request.auth, request=request, request_data=serializer.validated_data, ) except ApiRequestError as exc: return Response(exc.as_response_data(), status=exc.http_status) return Response(data, status=status.HTTP_200_OK) class BalanceView(ExternalApiView): def get(self, request): balance = get_balance_snapshot(request.user) return Response( { "user": request.user.get_username(), "points_balance": balance.points_balance, }, status=status.HTTP_200_OK, ) class RechargeCallbackView(APIView): authentication_classes = () permission_classes = () @staticmethod def _error_response(exc: Exception): if isinstance(exc, PaymentVerificationError): return ( api_error("signature_invalid", "支付回调验签失败"), status.HTTP_400_BAD_REQUEST, ) if isinstance(exc, RechargeAmountMismatchError): return ( api_error("amount_mismatch", "支付回调金额与本地订单金额不一致"), status.HTTP_400_BAD_REQUEST, ) if isinstance(exc, RechargeCallbackError): return api_error(exc.code, "支付回调处理失败"), status.HTTP_400_BAD_REQUEST return api_error("bad_request", "支付回调处理失败"), status.HTTP_400_BAD_REQUEST @method_decorator(csrf_exempt, name="dispatch") class WechatRechargeCallbackView(RechargeCallbackView): def post(self, request): try: payment = verify_wechat_callback(request.headers, request.body) apply_recharge_payment(payment) except ( PaymentVerificationError, RechargeAmountMismatchError, InvalidRechargeOrderStateError, RechargeOrderNotFoundError, RechargePayMethodMismatchError, ) as exc: logger.warning("Rejected WeChat recharge callback: %s", exc.__class__.__name__) data, http_status = self._error_response(exc) return Response(data, status=http_status) return Response({"code": "SUCCESS", "message": "成功"}, status=status.HTTP_200_OK) @method_decorator(csrf_exempt, name="dispatch") class AlipayRechargeCallbackView(RechargeCallbackView): def post(self, request): try: payload = request.data.dict() if hasattr(request.data, "dict") else dict(request.data) payment = verify_alipay_callback(payload) apply_recharge_payment(payment) except ( PaymentVerificationError, RechargeAmountMismatchError, InvalidRechargeOrderStateError, RechargeOrderNotFoundError, RechargePayMethodMismatchError, ) as exc: logger.warning("Rejected Alipay recharge callback: %s", exc.__class__.__name__) return HttpResponse("fail", status=status.HTTP_400_BAD_REQUEST) return HttpResponse("success", content_type="text/plain", status=status.HTTP_200_OK)