Skip to content

[FEAT] 환불 → 정산 지급이체 차감 자동화 (Payple 정산지급대행 연동) #491

@minij02

Description

@minij02

✨ 기능 설명

#485에서 환불 시 Settlement.status='Refunded'로 마킹은 되지만, 이미 지급된 정산금 회수 또는 다음 정산에서 차감하는 실제 회계 처리가 없음. Payple 정산지급대행 명세(파트너 인증 → 계좌인증 → 빌링키 이체 대기 → 실행 → Webhook)를 활용해 정산 사이클 자동화 + 환불 차감 정책 구현.

✨ 결정 필요 사항 (기획/회계 확정 필요)

정산 정책

  1. 정산 사이클 주기
    • A. 주간 (예: 매주 월요일) — 빠른 정산
    • B. 월간 (예: 매월 15일) — 회계 단순 ← 권장 (Payple 정산 D+N 보통)
    • C. 실시간 (Succeed 시 즉시) — Payple rate-limit/수수료 부담
  2. 정산 최소 금액 — 예: 1만원 이상부터 지급 (그 이하는 다음 사이클로 이월)
  3. 환불 차감 정책 — 잔액 음수 처리
    • A. 다음 사이클로 이월 (계속 차감)
    • B. 즉시 청구 (관리자 알림 + 사용자에게 청구서)
    • C. 무시 (음수 잔액 그대로 유지)
  4. 테스트 환경 이체 금액 — 명세상 sandbox는 tran_amt=1000 고정. 운영 전환 시 검증 필요

인프라

  1. 빌링키 발급 시점 — 현재 verify-account 흐름은 Payple 실명인증만 호출. 정산지급대행 명세 2의 계좌인증은 billing_tran_id 반환 — 이걸 SettlementAccount에 저장해야 지급이체 가능
    • 본 이슈에서 verify-account 흐름을 정산지급대행 인증으로 전환? 또는 별도 endpoint?
  2. Webhook 인증 — Payple Webhook 인증 방식 명세 확인 필요 (IP allowlist / 서명)

✨ 핵심 흐름 (구상)

[정산 사이클 cron — 매월 15일 KST 09:00]
  for each 판매자:
    1. 미정산 금액 계산
       = SUM(Settlement.status=Succeed 직전 사이클 이후)
       - SUM(Settlement.status=Refunded 직전 사이클 이후)
       - 이월 음수 잔액
    2. 최소 금액 미만이면 SKIP (다음 사이클 이월)
    3. billing_tran_id 미보유 시 SKIP + ERROR (계좌 재등록 안내)
    4. Payple 이체 대기 (3) → group_key 받음
    5. Payple 이체 실행 (4, NOW)
    6. SettlementPayout row 생성 (status=Pending)
  Webhook 수신 시:
    1. SettlementPayout 상태 업데이트 (Succeed/Failed)
    2. 알림 (선택)

✨ 개발 목록

Prisma 스키마

  • SettlementAccount.billing_tran_id String? 추가 (계좌인증 응답 빌링키 저장)
  • SettlementPayout 모델 신규 — 정산 지급이체 이력
    user_id / amount_gross(Succeed합) / amount_refund(Refunded합) / amount_net /
    cycle_start / cycle_end / payple_group_key / payple_api_tran_id /
    status(Pending|Succeed|Failed) / requested_at / completed_at
    
  • 마이그레이션

Payple 정산지급대행 유틸 (utils/payple-payout.ts 신규)

  • fetchPayoutAuth() — 파트너 인증 (code 10자리 영문+숫자, access_token 60초)
    • 기존 verify(실명인증)와 같은 인증인지 확인 필요 — 응답 필드 같음
    • 응답 access_token Redis 30초 캐시 (60초 만료보다 짧게)
  • fetchPayoutAccountVerification({ bankCode, accountNumber, accountHolderInfoType, accountHolderInfo }) — 명세 2. 계좌인증, 빌링키(billing_tran_id) 반환
  • requestPayoutDeposit({ billingTranId, tranAmt }) — 명세 3. 이체 대기 요청, group_key 반환
  • executePayout({ groupKey, billingTranId, executeType: 'NOW' }) — 명세 4. 이체 실행
  • redactor 적용 (billing_tran_id, access_token, api_tran_id 등)

빌링키 발급 통합

  • 현재 verify-account 흐름이 실명인증만 호출 — 정산지급대행 계좌인증과 통합 검토
    • 옵션 A: verify-account 통합 (한 번에 빌링키까지 발급) — 인증 흐름 단순화
    • 옵션 B: 별도 endpoint (POST /settlements/issue-billing-key) — 분리 유지
  • SettlementAccount.billing_tran_id에 저장 + 응답 마스킹

Webhook 수신

  • POST /api/payouts/webhook 신규 (Payple 이체 결과)
  • IP allowlist + (가능하면) 서명 검증
  • SettlementPayout.status 업데이트, api_tran_id 저장
  • 멱등성 (같은 webhook 중복 수신 안전)

정산 사이클 cron

  • src/settlements/jobs/settlement-payout.job.ts 신규
  • cron 표현식 (월간 / 주간 결정 후)
  • Redis 분산 락
  • 판매자별 미정산 금액 계산 + 환불 차감
  • 빌링키 보유 + 최소 금액 충족 시 Payple 호출
  • 결과를 SettlementPayout row로 기록

환수 회계 (환불 차감)

알림 / 리포트

  • 정산 완료 / 실패 / 환수 차감 알림 (선택, 별도 이슈로 분리 가능)

환경변수

  • PAYPLE_PAYOUT_AUTH_PATH (필요 시)
  • PAYPLE_PAYOUT_WEBHOOK_URL (등록용)
  • PAYPLE_PAYOUT_MIN_AMOUNT (최소 정산 금액)
  • PAYPLE_PAYOUT_CYCLE_CRON (사이클 cron 표현식)

검증

  • pnpm build / pnpm tsc --noEmit
  • sandbox e2e: 빌링키 발급 → 이체 대기 → 실행 → Webhook 수신
  • 환불 차감 동작 (환불 후 다음 사이클에서 정산금 차감 검증)
  • 잔액 음수 정책 동작

✨ 별도 이슈로 분리 가능 (작업 분할)

범위가 크므로 단계별로 분리:

  1. 빌링키 발급 인프라 (verify 통합 + Prisma)
  2. Payple 지급이체 유틸 (utils/payple-payout.ts)
  3. Webhook 수신 (/payouts/webhook + 멱등성)
  4. 정산 사이클 cron (자동 지급 + 환불 차감)

각 단계별로 PR 분할 권장.

✨ 기타 / 질문

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions