Payment ‐ 세부기능 - nhnacademy-aiot3-4iren/4iren-wiki GitHub Wiki

Payment (구독 결제)

repo: 4iren-payment-api


0. 무엇을 판매하는가

  • 팀/워크스페이스의 Owner 등급을 월간(MONTHLY)/연간(YEARLY) 구독 상품으로 판매한다.
  • 등급 승격은 빌링키 등록 자체가 아니라 최초 결제가 성공한 시점에 이루어진다.
  • 승격/강등은 RabbitMQ 이벤트로 account-api에 전달된다. account-api가 소유한 UserRole.OWNER와 core-api의 팀별 TeamRole.OWNER는 서로 다른 개념이다.

1. 요금제 조회

  • GET /api/payment/plans — 판매 중인 요금제와 가격 조회 (역할 검증 없음)

2. 구독 관리

  • GET /api/payment/subscriptions — 현재 구독 상태 조회 (OWNER)
  • DELETE /api/payment/subscriptions — 구독 취소 (OWNER)
    • 취소해도 즉시 강등되지 않고 현재 결제 주기 종료일(currentPeriodEnd)까지 OWNER 등급이 유지된다.

3. 결제 이력

  • GET /api/payment/payments — 결제 이력 조회 (OWNER)

4. 빌링키(자동결제) 등록

기능 Toss Pay Kakao Pay 권한
등록 시작 POST /api/payment/billing-keys/toss/registrations POST /api/payment/billing-keys/kakao/registrations NORMAL
결제수단 변경 POST .../toss/registrations/change POST .../kakao/registrations/change OWNER
콜백 POST .../toss/callback GET .../kakao/callback 검증 없음
  • 콜백은 PG사가 호출하므로 역할 검증을 적용하지 않는다.
  • 결제수단 변경 시 새 빌링키는 PENDING 상태로 저장되었다가 다음 청구 시점에 교체된다.
  • 등록 진행 상태는 Redis에 TTL 캐시로 보관한다.
  • 빌링키 자격증명은 컨버터로 암호화하여 저장한다.

5. PG(결제대행사) 연동 현황

PG사 상태
Toss Pay 구현 완료 (등록 콜백에서 REMOVED 처리 포함)
Kakao Pay 구현 완료
Naver Pay 미구현 — Provider.NAVER_PAY enum 값만 존재하며, 선택 시 게이트웨이 조회에서 예외가 발생한다

6. 자동결제 및 배치

스케줄러 주기 역할
구독 정기 결제 매일 01:00 갱신 대상 구독 청구 (가상 스레드, 동시 실행 수 제한)
취소 구독 만료 처리 매일 01:30 주기 종료된 취소 구독을 만료 처리하고 강등 이벤트 발행
미완료 결제 복구 10분마다 READY 상태로 멈춘 결제 복구
  • 다중 인스턴스 중복 실행은 Redis 기반 ShedLock으로 방지한다.
  • 결제 실패 시 즉시 강등하지 않고 PAST_DUE 상태로 두며, 최대 3회까지 재청구(dunning)한다. 재시도 횟수와 크론 주기는 코드 기본값이며 config-repo에서 별도로 재정의하지 않는다.

7. 등급 이벤트

  • 승격: 최초 결제 성공 시 OWNER 승격 이벤트 발행 (갱신 결제 시에는 재발행하지 않음)
  • 강등: 구독 만료(EXPIRED) 또는 취소 후 주기 종료 시 NORMAL 강등 이벤트 발행

8. 도메인 엔티티 및 상태

  • Subscriptions — ACTIVE / PAST_DUE / EXPIRED / CANCELED
  • Payments — READY / DONE / FAILED
  • BillingKeys — ACTIVE / PENDING / DELETED
  • PlanPrices — 가격 변경 후에도 기존 구독자는 가입 시점 가격을 유지한다.
  • 물리 삭제를 사용하지 않고 모두 상태 전이로 관리한다. (빌링키 삭제도 DELETED 전이)

9. 알림 및 실패 로깅

  • 결제·구독 관련 메일 5종을 이벤트 기반으로 발송한다.
  • 메일 발송 실패와 역할 변경 이벤트 발행 실패는 Elasticsearch에 별도 로그로 적재한다.
  • RabbitMQ publisher confirms/returns를 사용해 발행 실패를 감지한다.

10. 외부 연동

  • Eureka 서비스 디스커버리 기반 Feign 클라이언트로 4IREN-ACCOUNT, 4IREN-CORE 호출. 두 클라이언트 모두 fallback을 두지만 예외를 던지는 fail-closed 방식이며, 기능을 축소해 계속 동작하지는 않는다.
  • Config Server에서 4iren-payment.properties(-dev/-prod) 프로파일별 설정 로드
  • MySQL 사용, Flyway로 마이그레이션 관리 — 이미 적용된 마이그레이션 파일은 수정하지 않는 것이 원칙. ⚠️ dev 프로파일은 로컬 전용 스키마(payment_db)를 쓰지만, prod는 다른 서비스들과 동일한 공유 스키마(aiot03_team4)를 사용한다 — Database-per-Service 원칙이 prod에서는 지켜지지 않고 있다(4iren-config-repo/4iren-payment-prod.properties 확인, 2026-09-09).

11. 현재 제공하지 않는 것

  • 환불 API
  • PG 결제 웹훅 수신 (콜백은 빌링키 등록 흐름에만 존재)
  • 명시적 멱등성 키 — Toss는 등록 대기 캐시 소비와 활성 키 존재 여부로, Kakao는 404 응답으로 중복을 방어한다.