77from core .openapi .schemas import build_html_responses
88from django .conf import settings
99from django .db import models , transaction
10- from django .utils .decorators import method_decorator
1110from drf_spectacular .openapi import OpenApiParameter , OpenApiTypes
12- from drf_spectacular .utils import extend_schema
11+ from drf_spectacular .utils import extend_schema , extend_schema_view
1312from drf_standardized_errors .openapi_serializers import ErrorResponse403Serializer , ValidationErrorResponseSerializer
1413from rest_framework import mixins , renderers , request , response , serializers , status , viewsets
1514from rest_framework .decorators import action
2827from user .models import UserExt
2928
3029
31- @method_decorator (
32- name = "list" ,
33- decorator = extend_schema (
30+ @extend_schema_view (
31+ list = extend_schema (
3432 summary = "주문 이력 목록 조회" ,
3533 tags = [OpenAPITag .SHOP_ORDER ],
3634 responses = {
3735 status .HTTP_200_OK : OrderDto (many = True ),
3836 status .HTTP_403_FORBIDDEN : ErrorResponse403Serializer ,
3937 },
4038 ),
41- )
42- @method_decorator (
43- name = "retrieve" ,
44- decorator = extend_schema (
39+ retrieve = extend_schema (
4540 summary = "주문 이력 상세 조회" ,
4641 tags = [OpenAPITag .SHOP_ORDER ],
4742 parameters = [
@@ -145,6 +140,7 @@ def create_single_product_order(
145140 status .HTTP_403_FORBIDDEN : ErrorResponse403Serializer ,
146141 },
147142 )
143+ @transaction .atomic
148144 def create (
149145 self , request : request .Request , * args : tuple [typing .Any ], ** kwargs : dict [str , typing .Any ]
150146 ) -> response .Response :
@@ -191,7 +187,12 @@ def create(
191187 cart .name_en += f" and { len (cart_product_rels ) - 1 } more"
192188 cart .save ()
193189
194- portone_client .register_or_update_prepared_payment (merchant_id = str (cart .id ), price = cart .first_paid_price )
190+ # idempotent + forward-only — DB commit 후 비동기 호출. 실패 시 클라이언트 retry 가 자연 보상.
191+ transaction .on_commit (
192+ lambda : portone_client .register_or_update_prepared_payment (
193+ merchant_id = str (cart .id ), price = cart .first_paid_price
194+ )
195+ )
195196
196197 return response .Response (data = OrderDto (instance = cart ).data , status = status .HTTP_201_CREATED )
197198
@@ -217,7 +218,7 @@ def destroy(
217218 self , request : request .Request , * args : tuple [typing .Any ], ** kwargs : dict [str , typing .Any ]
218219 ) -> response .Response :
219220 """Order의 사용 및 환불하지 않은 상품을 refunded 상태로 변경하고, 결제 취소를 요청합니다."""
220- serializer = OrderTotalRefundSerializer (instance = self .get_object (), data = {"check_refundable_date " : True })
221+ serializer = OrderTotalRefundSerializer (instance = self .get_object (), data = {}, context = { "check_totp " : False })
221222 serializer .is_valid (raise_exception = True )
222223 serializer .refund ()
223224 return response .Response (status = status .HTTP_204_NO_CONTENT )
@@ -338,7 +339,7 @@ def destroy(
338339 self , request : request .Request , * args : tuple [typing .Any ], ** kwargs : dict [str , typing .Any ]
339340 ) -> response .Response :
340341 """부분 환불을 진행합니다."""
341- serializer = OrderProductRefundSerializer (instance = self .get_object (), data = {"check_refundable_date " : True })
342+ serializer = OrderProductRefundSerializer (instance = self .get_object (), data = {}, context = { "check_totp " : False })
342343 serializer .is_valid (raise_exception = True )
343344 serializer .refund ()
344345 return response .Response (status = status .HTTP_204_NO_CONTENT )
0 commit comments