159 lines
5.7 KiB
Python
159 lines
5.7 KiB
Python
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)
|