Skip to content

[FEAT] 페이플 결제결과 웹훅 수신 엔드포인트 — 브라우저 리턴 유실 결제 보완 #536

Description

@minij02

✨ 기능 설명

페이플 파트너 관리자 〉 기본정보에 결제결과 수신 URL (가맹점 미수신 결과) 을 등록했습니다.

https://promptplace.kro.kr/api/prompts/purchases/payple-webhook

브라우저가 결제창에서 돌아오지 못한 결제(창 강제 종료, 네트워크 끊김, 앱 전환 실패 등)를 서버-투-서버 웹훅으로 보완해 결제결과 누락을 방지하는 것이 목적입니다. 현재 해당 경로는 미구현이라 등록만 된 상태입니다.

✨ 왜 기존 /payple-result 를 못 쓰나

src/purchases/routes/purchase.webhook.route.ts:8POST /payple-resultPCD_RST_URL(결제창 리턴 URL) 겸용입니다. 운영 .envPURCHASE_RESULT_REDIRECT_URL 이 설정돼 있어 (https://www.promptplace.kr/purchase/result) 컨트롤러가 302 redirect 를 반환합니다 (purchase.webhook.controller.ts:36).

브라우저 리턴엔 맞지만 웹훅 수신엔 200 이어야 하고, 302 를 실패로 판단해 재전송이 반복될 수 있습니다. 그래서 리다이렉트 없이 항상 200 을 주는 전용 경로를 분리합니다.

✨ 구현 중 확인된 문제 2건

1. 웹훅 페이로드의 PCD_PAY_URL 이 빈 문자열

공식 문서의 결제완료 웹훅 예시(카드/계좌 공통)에서 PCD_PAY_URL"" 이고, 대신 최종 결제요청 URL 이 PCD_PAY_COFURL 로 옵니다.

verifyPayplePaymentpayUrl 이 falsy 면 키 누락으로 던지므로 (purchases/utils/payple.ts:169), 웹훅 페이로드를 그대로 넘기면 재검증 단계에서 100% 실패합니다. PCD_PAY_COFURL 폴백이 필요합니다.

2. 재검증 호스트를 요청 본문에서 받음 (SSRF)

verifyPayplePayment${payHost}${payUrl} 로 POST 하는데 (payple.ts:175) 두 값 모두 요청 본문에서 옵니다. 지금까지는 브라우저 리턴 경로에서만 쓰여 노출이 제한적이었지만, 인증 없는 공개 웹훅 엔드포인트가 같은 함수를 타면 임의 호스트로 사내 요청을 유도할 수 있습니다. PAYPLE_CPAY_URL 도메인 allowlist 로 막습니다.

✨ 취소완료 웹훅 처리 방침

같은 URL 로 취소완료 이벤트(PCD_PAY_CODE: PAYC0000)도 들어옵니다. 다만 취소 페이로드에는 PCD_USER_DEFINE1(prompt_id/user_id)이 없고 PCD_REFUND_TOTAL 만 있어 기존 결제 처리 로직과 형태가 다릅니다.

환불은 이미 admin-refund.service.ts 워크플로(#533)가 정본이므로, 이번 PR 에서는 취소 이벤트는 로그만 남기고 무시합니다. 파트너 관리자에서 직접 취소한 건을 DB 에 자동 반영하는 건 별도 이슈로 분리합니다.

✨ 개발 목록

  • POST /api/prompts/purchases/payple-webhook 추가 — 리다이렉트 없이 항상 200
  • 수신 즉시 200 응답 후 비동기 처리 (페이플 재전송 타임아웃 방지)
  • verifyPayplePaymentPCD_PAY_URL 없으면 PCD_PAY_COFURL 폴백
  • 재검증 호스트 allowlist (PAYPLE_CPAY_URL 도메인)
  • 취소완료 이벤트는 로그만 남기고 스킵
  • 민감정보 마스킹 로그 (카드번호/계좌번호)
  • pnpm build 통과

✨ 기타 설명 / 질문

  • 멱등성은 기존 WebhookService.handlePaypleResultfindExistingPurchase 가이드 그대로 재사용합니다 (purchase.webhook.service.ts:26). /complete 와 웹훅이 동시에 도착해도 중복 구매가 생기지 않습니다.
  • 해지결과 수신 URL 칸은 비워뒀습니다. 구매 결제가 PCD_PAY_WORK: 'PAY' 단건결제라 PCD_PAYER_ID 빌링키를 저장하지 않고, schema 에 매칭할 컬럼도 없습니다. 간편/정기결제 도입 시 별도 이슈로 다룹니다.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions