You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PATCH /api/admin/refunds/:refundId/reject — 거절, body { reason: string }
GET /api/admin/refunds — 전체 이력 (status 필터)
전 엔드포인트 authenticateJwt + isAdmin
Swagger 문서화
6. Payple 취소 실패 시 처리 (3개월 경과 건)
카드사 취소 가능 기간을 넘긴 건은 Payple 취소 API가 거절합니다. 승인 자체를 롤백하면 시스템상 영영 환불 불가로 남으므로, 승인 상태로 멈추고 오프라인 처리로 넘깁니다.
승인 처리 흐름
status: APPROVED + reviewed_by/reviewed_at 먼저 기록
Payple 취소 호출
성공 → COMPLETED + refunded_at + Payment/Settlement Refunded 전이
실패 → APPROVED 유지 + payple_fail_code 기록, 응답은 200이되 payple_cancel_failed: true 로 관리자에게 명시
APPROVED 상태(= 취소 실패 대기)를 관리자 목록에서 별도로 필터링 가능하게
수동 송금 완료 후 COMPLETED로 전이시키는 엔드포인트: PATCH /:refundId/complete-manual
7. 결제 화면 환불정책 동의 기록
PurchaseRequestDTO에 refund_policy_agreed: boolean 추가 — true가 아니면 400 RefundPolicyNotAgreed
Purchase.refund_policy_agreed_at DateTime? 컬럼 추가
PCD_USER_DEFINE1에 agreed_at 포함 → purchase.complete에서 Purchase 생성 시 기록
Swagger에 문구 원문 명시:
디지털콘텐츠 특성상 열람(제공 개시) 후에는 단순 변심 환불이 불가합니다
8. 검증
7일 경계 계산 자체 검증 (src/refunds/utils/refund-policy.test.ts 또는 self-check)
pnpm build / pnpm tsc --noEmit
기존 자동 환불 경로 회귀 확인 (열람 전 7일 이내 → 즉시 환불이 그대로 동작)
✨ API 변경 요약 (프론트 동기화)
Method
Path
비고
GET
/api/prompts/downloads
응답에 refundable, refund_deadline, manual_refund_available 추가
GET
/api/prompts/purchases/{id}/refund-eligibility
응답에 refund_deadline 추가, 7일 판정 기준 변경
POST
/api/prompts/purchases/{id}/refund
기존 유지 (열람 전 7일 이내 즉시 환불)
POST
/api/prompts/purchases/{id}/refund-request
신규 — 열람 후 수동 환불 신청
POST
/api/prompts/purchases/request
refund_policy_agreed: true필수화 (breaking)
GET
/api/admin/refunds/pending
신규
GET
/api/admin/refunds/{refundId}
신규
PATCH
/api/admin/refunds/{refundId}/approve
신규
PATCH
/api/admin/refunds/{refundId}/reject
신규
PATCH
/api/admin/refunds/{refundId}/complete-manual
신규
⚠️refund_policy_agreed 필수화는 breaking change이므로 프론트 배포와 동시에 나가야 합니다.
✨ 기타 설명 / 질문
Payple 문서 참고 범위: 공유된 해외카드 운영 API 문서는 service_id / pay_id / totalAmount + Bearer 토큰 체계로, 현재 사용 중인 국내카드 취소 API(PCD_PAYCANCEL_FLAG=Y + PCD_REFUND_KEY)와 파라미터 체계가 다릅니다. 현재 구현(payple-refund.ts)은 국내카드 기준이 맞으므로 그대로 유지합니다. 해외카드 결제를 붙이게 되면 취소 경로가 별도로 필요합니다.
Refund.purchase_id가 @unique 이므로 거절된 건의 재신청은 불가합니다. 재신청을 허용하려면 별도 RefundRequest 테이블이 필요한데, 우선 이번 범위에서는 제외합니다. 거절 후 재신청은 문의(Inquiry) 경로로 안내.
3개월 기준일: 구매일(Purchase.created_at) 기준입니다. 열람일 기준이 맞다면 알려주세요.
부실 판정은 전적으로 담당자 재량입니다. 자동 판정(본문 길이 등)은 넣지 않았습니다 — 오탐 시 판매자 피해가 크고, 정책 문구도 "담당자 수동 확인"으로 되어 있습니다.
✨ 기능 설명
환불 정책이 확정됨에 따라, 기존 환불 기능(#485, #497, #518)과 정책 사이의 차이를 메우고 열람 후 수동 환불(최장 3개월) 워크플로를 추가합니다.
확정 정책
A. 자동 환불 — 열람 전, 7일 이내
"디지털콘텐츠 특성상 열람(제공 개시) 후에는 단순 변심 환불이 불가합니다"문구 + 동의 체크박스B. 수동 환불 — 열람 후, 담당자 확인, 최장 3개월
단순 변심은 불가. 아래 사유에 한해 담당자가 수동 확인 후 환불:
현재 develop 상태
PCD_PAYCANCEL_FLAG=Y)src/settlements/utils/payple-refund.tsRefund모델,Purchase.downloaded_atprisma/schema.prismasrc/refunds/routes/refund.route.tsRefunded전이refund.service.ts:135-144purchase_id/is_refundedrefund.service.ts:10,50— 168시간 절대값refundable부재Refund에 상태값 없음, 관리자 API 없음✨ 개발 목록
1. 7일 기준을 KST 날짜 기준으로 정정
현재
refund.service.ts:50은Date.now() - created_at >= 168시간으로 판정합니다. 23일 15시 구매 시 30일 15시에 마감되어, "30일까지 가능"이라는 정책과 어긋납니다.REFUND_WINDOW_MS상수 제거, KST 날짜 기준 마감 계산으로 교체구매일(KST) + 8일 00:00 KST(= D+7일 24:00까지)remaining_seconds를 새 마감 기준으로 재계산refund_deadline(ISO8601) 추가 — FE가 "N일 남음"을 직접 계산할 수 있도록2. 환불 가능 여부 판정 로직 공용화 + 목록 노출
정책 판정이
refund.service.ts에만 있어, 목록 화면에서 버튼 활성화를 판단할 방법이 없습니다. FE가 항목마다refund-eligibility를 호출하면 N+1이 됩니다.checkEligibility의 판정 로직을 순수 함수로 분리 (src/refunds/utils/refund-policy.ts){ user_id, created_at, downloaded_at, is_free, payment.status, refund }{ eligible, reason, refund_deadline, remaining_seconds }refund.service.ts가 이 함수를 쓰도록 리팩터링 (동작 변화 없음)PromptDownloadRepository.getDownloadedPromptsByUserselect 보강created_at,downloaded_at,is_free,payment: { select: { status: true } }추가DownloadedPromptResponseDTO에 필드 추가refundable: boolean— 자동 환불(열람 전 7일 이내) 신청 버튼 활성화 여부refund_deadline: string | null— 자동 환불 마감 시각manual_refund_available: boolean— 열람 후 3개월 이내 → 수동 환불 신청 가능prompt.download.route.tsSwagger 응답 스키마 갱신3.
Refund모델에 상태 추가schema.prisma수정COMPLETED기본값으로 백필refunded_at은 실제 환불 완료 시점 의미로 유지 (REQUESTED단계에서는 미확정)4. 수동 환불 신청 API (사용자)
POST /api/prompts/purchases/:purchaseId/refund-requestpayment.status === 'Succeed'/ 환불 이력 없음 / 열람함(downloaded_at !== null) / 구매 후 3개월 이내{ reason: string }(필수, 10자 이상 500자 이하)Refund레코드를status: REQUESTED,initiator: 'USER'로 생성POST .../refund)로 안내 (400)NotificationType확장 필요 시 별도 논의)5. 관리자 환불 관리 API
admin-seller.route.ts의pending 목록 → 상세 → 승인 → 거절구조를 그대로 따릅니다. 신규 라우터src/refunds/routes/admin-refund.route.ts,/api/admin/refunds에 마운트.GET /api/admin/refunds/pending—status: REQUESTED목록 (페이지네이션)GET /api/admin/refunds/:refundId— 상세 (구매자, 프롬프트 본문, 상세페이지 설명, 신청 사유, 열람 시점)PATCH /api/admin/refunds/:refundId/approve— 승인 → Payple 취소 호출PATCH /api/admin/refunds/:refundId/reject— 거절, body{ reason: string }GET /api/admin/refunds— 전체 이력 (status 필터)authenticateJwt+isAdmin6. Payple 취소 실패 시 처리 (3개월 경과 건)
카드사 취소 가능 기간을 넘긴 건은 Payple 취소 API가 거절합니다. 승인 자체를 롤백하면 시스템상 영영 환불 불가로 남으므로, 승인 상태로 멈추고 오프라인 처리로 넘깁니다.
status: APPROVED+reviewed_by/reviewed_at먼저 기록COMPLETED+refunded_at+ Payment/SettlementRefunded전이APPROVED유지 +payple_fail_code기록, 응답은 200이되payple_cancel_failed: true로 관리자에게 명시APPROVED상태(= 취소 실패 대기)를 관리자 목록에서 별도로 필터링 가능하게COMPLETED로 전이시키는 엔드포인트:PATCH /:refundId/complete-manual7. 결제 화면 환불정책 동의 기록
PurchaseRequestDTO에refund_policy_agreed: boolean추가 —true가 아니면 400RefundPolicyNotAgreedPurchase.refund_policy_agreed_at DateTime?컬럼 추가PCD_USER_DEFINE1에agreed_at포함 →purchase.complete에서Purchase생성 시 기록8. 검증
src/refunds/utils/refund-policy.test.ts또는 self-check)pnpm build/pnpm tsc --noEmit✨ API 변경 요약 (프론트 동기화)
GET/api/prompts/downloadsrefundable,refund_deadline,manual_refund_available추가GET/api/prompts/purchases/{id}/refund-eligibilityrefund_deadline추가, 7일 판정 기준 변경POST/api/prompts/purchases/{id}/refundPOST/api/prompts/purchases/{id}/refund-requestPOST/api/prompts/purchases/requestrefund_policy_agreed: true필수화 (breaking)GET/api/admin/refunds/pendingGET/api/admin/refunds/{refundId}PATCH/api/admin/refunds/{refundId}/approvePATCH/api/admin/refunds/{refundId}/rejectPATCH/api/admin/refunds/{refundId}/complete-manualrefund_policy_agreed필수화는 breaking change이므로 프론트 배포와 동시에 나가야 합니다.✨ 기타 설명 / 질문
service_id/pay_id/totalAmount+ Bearer 토큰 체계로, 현재 사용 중인 국내카드 취소 API(PCD_PAYCANCEL_FLAG=Y+PCD_REFUND_KEY)와 파라미터 체계가 다릅니다. 현재 구현(payple-refund.ts)은 국내카드 기준이 맞으므로 그대로 유지합니다. 해외카드 결제를 붙이게 되면 취소 경로가 별도로 필요합니다.Refund.purchase_id가@unique이므로 거절된 건의 재신청은 불가합니다. 재신청을 허용하려면 별도RefundRequest테이블이 필요한데, 우선 이번 범위에서는 제외합니다. 거절 후 재신청은 문의(Inquiry) 경로로 안내.Purchase.created_at) 기준입니다. 열람일 기준이 맞다면 알려주세요.