feat: 0원 체크아웃 지원#55
Open
JaeHyuckSa wants to merge 3 commits into
Open
Conversation
There was a problem hiding this comment.
Pull request overview
0원(무료) 주문을 PortOne 결제 없이 내부 완료 경로로 처리할 수 있도록 결제/주문 검증 흐름을 분리·보강하고, 유료 webhook 완료 또한 동일한 재검증(재고/한도/옵션/티켓정보) 경로를 공유하도록 정리한 PR입니다. 또한 장바구니 갱신/체크아웃 레이스를 줄이기 위해 잠금 기반 재검증을 추가하고, 0원 주문 응답/환불 불가 케이스를 API 및 스키마·테스트에 반영했습니다.
Changes:
- 무료(0원) 체크아웃 완료 서비스(
complete_free_checkout) 도입 및 주문/알림/히스토리 생성 경로 추가 - 상품/장바구니 “주문 가능” 검증에서 0원 허용(음수만 거부) 및 체크아웃 시점 잠금 기반 재검증/레이스 완화
- webhook 완료 처리 시 재검증 공유 및 실패 시 PortOne 결제 취소(가능한 경우) + 0원 주문의 환불/영수증/관리자 API 스키마 보강
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| app/shop/payment_history/services.py | 무료 체크아웃 완료 및 체크아웃 재검증(잠금/검증 payload 구성) 유틸 추가 |
| app/shop/payment_history/serializers.py | PortOne webhook validate/create에서 0원 결제 거부 및 체크아웃 재검증 공유 |
| app/shop/serializers/cart_validation/product.py | 상품 단위 합계 0원 허용(음수만 거부), 담기 시 사용자 락+재검증 및 cart row 재확인 추가 |
| app/shop/serializers/cart_validation/cart.py | 빈 cart 분리 에러(EMPTY) 및 0원 허용(음수만 거부)로 cart 검증 로직 조정 |
| app/shop/order/views/orders.py | 단건/장바구니 체크아웃에서 prepared_price=0이면 내부 무료 완료 경로로 즉시 완료 응답 |
| app/shop/order/views/carts.py | cart item 삭제 시 주문 row 잠금/PH 존재 재확인으로 체크아웃 레이스 완화 |
| app/shop/order/serializers/dto.py | 단건 주문 생성 201 응답 스키마용 DTO 추가(0원 즉시 완료 vs 유료 카트 공통 스키마) |
| app/shop/patron.py | 후원자 목록 집계에서 donation_price>0 조건 추가로 0원 주문 제외 |
| app/core/const/shop_error_messages.py | 0원 허용에 맞춰 “최소 0원” 메시지 및 webhook 실패 코드(ORDER_NOT_ORDERABLE) 추가 |
| app/shop/conftest.py | order_factory에 product_price/imp_id 옵션 추가(무료 완료/imp_id null 케이스 테스트 지원) |
| app/shop/test/product_orderable_test.py | 0원 상품 허용 및 음수 거부 케이스 테스트로 수정 |
| app/shop/test/cart_orderable_test.py | 빈 cart 에러 분리 및 0원 cart 허용/음수 거부 테스트로 수정 |
| app/shop/test/patron_api_test.py | 0원(후원 가능 설정이어도) 후원자 목록 제외 테스트 추가 |
| app/shop/test/refund_serializer_test.py | 무료 완료(imp_id=None) 주문의 환불 거부 + PortOne cancel 미호출 테스트 추가 |
| app/shop/payment_history/test/free_checkout_service_test.py | 무료 체크아웃 완료/승격/중복 처리/검증 실패 시나리오 전용 테스트 신설 |
| app/shop/payment_history/test/webhook_serializer_test.py | 0원 amount webhook 거부, 재검증 실패 시 cancel 호출, 티켓정보 사전 준비 등 테스트 보강 |
| app/shop/payment_history/test/webhook_view_test.py | 테스트 픽스처가 재검증 요구조건(티켓정보 등)과 충돌하지 않도록 설정 보정 |
| app/shop/payment_history/test/tasks_test.py | 0원 완료 알림 컨텍스트(first_paid_price=0 등) 테스트 추가 |
| app/shop/payment_history/test/concurrent_webhook_test.py | webhook 동시성 테스트에서 티켓정보 준비 추가 |
| app/shop/order/test/orders_api_test.py | 단건/카트 0원 체크아웃 즉시 완료 + PortOne/webhook 미사용 보장 테스트 추가 |
| app/shop/order/test/orders_schema_test.py | 단건 주문 생성 201 응답 스키마가 oneOf 없이 단일 ref 임을 검증하는 테스트 신설 |
| app/shop/order/test/cart_api_test.py | free checkout 경쟁 상황에서 cart add/remove 동작 및 락 전략 관련 동시성 테스트 보강 |
| app/shop/order/test/order_model_test.py | 0원 완료(imp_id=None) 주문의 환불 불가 사유/imp_id 처리 테스트 추가 |
| app/shop/order/test/opr_model_test.py | 0원 완료 OPR 환불 불가 사유 테스트 추가 |
| app/shop/order/test/soft_delete_filtering_test.py | webhook paid 전이 시 티켓정보 선행 조건을 만족하도록 테스트 보강 |
| app/admin_api/views/shop/orders.py | 관리자 환불 액션의 400 ValidationError 스키마 응답 명시 |
| app/admin_api/serializers/shop/orders.py | latest_imp_id가 null 가능해 ReadOnlyField로 직렬화 안정화 |
| app/admin_api/test/shop/products_api_test.py | 관리자 상품 생성에서 0원 가격 허용 테스트 추가 |
| app/admin_api/test/shop/orders_api_test.py | 관리자 목록/환불에서 0원 완료 주문 표시 및 환불 거부(PortOne cancel 미호출) + 스키마 테스트 추가 |
| app/admin_api/test/shop/order_notifications_api_test.py | 관리자 알림 미리보기에서 0원 완료 주문 컨텍스트 포함 테스트 추가 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
배경
가격 합계가 0원인 주문은 외부 PG 결제를 만들 수 없지만, 서비스 관점에서는 재고 차감, 주문 완료 상태, 결제 완료 알림, 관리자 주문 목록에서 일반 완료 주문처럼 처리되어야 합니다. 기존 유료 결제 경로는 PortOne 양수 금액 결제를 전제로 해서 0원 체크아웃을 별도 완료 경로로 분리할 필요가 있었습니다.
AS-IS
PaymentHistory생성 경로가 없습니다.imp_id=null케이스와 환불 불가 응답이 명확하지 않습니다.TO-BE
paid로 전환하고imp_id=null,price=0인 완료PaymentHistory를 생성합니다.latest_imp_id=null, PG 거래가 없는 환불 요청의 validation error를 반영합니다.테스트
pre-commit run --all-files --show-diff-on-failureRun lint통과Run pytest (all tests)통과Verify shop coverage is 100%통과