feat: add recharge create and status APIs
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import logging
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.utils import timezone
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.exceptions import AuthenticationFailed
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
@@ -19,20 +21,30 @@ from apps.api.generation import (
|
||||
from apps.api.serializers import (
|
||||
GenerateImageRequestSerializer,
|
||||
GenerateTitleRequestSerializer,
|
||||
RechargeCreateRequestSerializer,
|
||||
RechargeStatusRequestSerializer,
|
||||
)
|
||||
from apps.billing.models import RechargeOrder
|
||||
from apps.billing.payment_gateways import (
|
||||
PaymentOrderCreateError,
|
||||
PaymentQueryUnavailableError,
|
||||
PaymentVerificationError,
|
||||
query_payment_order,
|
||||
verify_alipay_callback,
|
||||
verify_wechat_callback,
|
||||
)
|
||||
from apps.billing.pricing import NoExchangeRateError
|
||||
from apps.billing.services import (
|
||||
InvalidRechargeOrderStateError,
|
||||
RechargeAmountMismatchError,
|
||||
RechargeCallbackError,
|
||||
RechargeOrderCreateError,
|
||||
RechargeOrderNotFoundError,
|
||||
RechargePayMethodMismatchError,
|
||||
apply_recharge_payment,
|
||||
create_recharge_order,
|
||||
get_balance_snapshot,
|
||||
query_and_apply_recharge_payment,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -99,6 +111,106 @@ class BalanceView(ExternalApiView):
|
||||
)
|
||||
|
||||
|
||||
class PortalSessionApiView(APIView):
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
||||
|
||||
def _recharge_order_response(order: RechargeOrder) -> dict:
|
||||
is_expired = bool(
|
||||
order.status == RechargeOrder.Status.PENDING
|
||||
and order.expires_at is not None
|
||||
and order.expires_at <= timezone.now()
|
||||
)
|
||||
return {
|
||||
"order_no": order.order_no,
|
||||
"amount": f"{order.amount_money:.2f}",
|
||||
"currency": order.currency,
|
||||
"exchange_rate": f"{order.exchange_rate:.4f}",
|
||||
"points_granted": order.points_granted,
|
||||
"pay_method": order.pay_method,
|
||||
"status": order.status,
|
||||
"code_url": order.code_url,
|
||||
"expires_at": order.expires_at.isoformat() if order.expires_at else None,
|
||||
"paid_at": order.paid_at.isoformat() if order.paid_at else None,
|
||||
"is_expired": is_expired,
|
||||
}
|
||||
|
||||
|
||||
class RechargeCreateView(PortalSessionApiView):
|
||||
def post(self, request):
|
||||
serializer = RechargeCreateRequestSerializer(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response(
|
||||
api_error("bad_request", "参数错误"),
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
try:
|
||||
order = create_recharge_order(
|
||||
user=request.user,
|
||||
amount=serializer.validated_data["amount"],
|
||||
pay_method=serializer.validated_data["pay_method"],
|
||||
)
|
||||
except NoExchangeRateError:
|
||||
return Response(
|
||||
api_error("no_exchange_rate", "未配置当前币种汇率"),
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
except RechargeOrderCreateError:
|
||||
return Response(
|
||||
api_error("bad_request", "充值下单参数错误"),
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
except PaymentOrderCreateError:
|
||||
return Response(
|
||||
api_error("payment_order_create_failed", "支付下单失败"),
|
||||
status=status.HTTP_502_BAD_GATEWAY,
|
||||
)
|
||||
|
||||
return Response(_recharge_order_response(order), status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
class RechargeStatusView(PortalSessionApiView):
|
||||
def get(self, request):
|
||||
serializer = RechargeStatusRequestSerializer(data=request.query_params)
|
||||
if not serializer.is_valid():
|
||||
return Response(
|
||||
api_error("bad_request", "参数错误"),
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
order_no = serializer.validated_data["order_no"]
|
||||
order = RechargeOrder.objects.filter(
|
||||
user=request.user,
|
||||
order_no=order_no,
|
||||
).first()
|
||||
if order is None:
|
||||
return Response(
|
||||
api_error("order_not_found", "充值订单不存在"),
|
||||
status=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
|
||||
if order.status == RechargeOrder.Status.PENDING:
|
||||
try:
|
||||
result = query_and_apply_recharge_payment(order.order_no, query_payment_order)
|
||||
order = result.order
|
||||
except (PaymentQueryUnavailableError, PaymentOrderCreateError):
|
||||
order.refresh_from_db()
|
||||
except RechargeCallbackError as exc:
|
||||
logger.warning(
|
||||
"Rejected active recharge query result for %s: %s",
|
||||
order.order_no,
|
||||
exc.__class__.__name__,
|
||||
)
|
||||
return Response(
|
||||
api_error(exc.code, "支付查单结果与本地订单不一致"),
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
return Response(_recharge_order_response(order), status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class RechargeCallbackView(APIView):
|
||||
authentication_classes = ()
|
||||
permission_classes = ()
|
||||
|
||||
Reference in New Issue
Block a user