Conversation
Payple 안내사항 2번에 따라 AWS 등 클라우드에서 도메인 정보가 누락될 경우 대비해 Referer 헤더 전송. 결제 유틸(purchases/utils/payple.ts)에는 이미 적용돼 있었으나 정산 4개 유틸에 모두 누락된 상태였음. - utils/payple.ts에 buildPaypleHeaders() 헬퍼 추가 - Content-Type / Cache-Control / Referer(PAYPLE_REFERER가 있을 때만) - extra 인자로 Authorization 등 호출별 헤더 병합 가능 - 4개 파일의 모든 axios.post 호출이 buildPaypleHeaders 사용: - utils/payple.ts (실명인증 OAuth + /inquiry/real_name) - utils/payple-settlement.ts (정산내역 조회) - utils/payple-refund.ts (결제 취소) - utils/payple-billing.ts (빌링키 조회/해지) 환경변수 PAYPLE_REFERER는 .env에 이미 설정됨 (운영: https://www.promptplace.kr, dev: http://localhost:5173).
[FIX] 정산 4개 Payple 유틸에 Referer 헤더 추가 (#503)
…R B) #491 작업 분할 PR B. 현재 `verifyRealNameWithPayple`가 Payple 응답에서 account_holder_name만 추출하고 billing_tran_id는 무시 중이었음. 정산 지급대행(`/transfer/execute` 등)에 필수인 빌링키를 발급받아 저장. ### 변경 - utils/payple.ts: - PaypleVerifyResult 타입 신규 (accountHolderName + billingTranId) - verifyRealNameWithPayple 반환에 billing_tran_id 포함 (없으면 null) - utils/register-token.ts: - RegisterTokenPayload에 billingTranId 추가 (verify→register 흐름에서 전달) - services/settlement.account.service.ts: - verify 결과의 billingTranId를 register-token payload에 포함 - services/settlement.seller.service.ts: - registerIndividual / registerBusiness(신규/개인→사업자/사업자→사업자) 모두 payload.billingTranId를 repository에 전달 - repositories/settlement.repository.ts: - Upsert/Create/UpdateBusinessAccountInput에 billingTranId 추가 - SettlementAccount.billing_tran_id 컬럼에 저장 ### 확인 사항 - Payple 정산지급대행 endpoint 명세 확인 결과 /inquiry/real_name이 동일 endpoint - 응답의 billing_tran_id가 향후 /transfer/request, /transfer/execute, /account/remain 등 모든 지급이체 API에서 필요 후속 (PR C/D): - /transfer/request (이체 대기 요청 — endpoint 추정 필요) - /transfer/execute (이체 실행 — host: demohub.payple.kr, /transfer/execute) - /transfer/result, /transfer/result/date, /transfer/result/group_key - /request/result/group_key (이체대기 조회) - /account/remain (잔액 조회) - Webhook 수신 - 정산 사이클 cron (매월 15일 KST, 최소 1만원, 음수 이월)
[FEAT] verify-account 흐름에 Payple 빌링키 발급/저장 통합 (#491 PR B)
#491 작업 분할 PR C. 공식 통합 가이드(docs.payple.kr/integration/hub/payout)에서 확보한 endpoint를 utils/payple-payout.ts에 8개 함수로 묶음. 본 PR은 내부 유틸만 추가, 외부 endpoint 노출 / cron은 PR D에서. ### 신규 utils/payple-payout.ts 공통: postWithToken 헬퍼 — OAuth + buildPaypleHeaders + redactPaypleLog 일관 적용, result !== 'A0000'이면 PaypleHubFailed 502. 8개 export 함수: - requestPayoutStandby({ billingTranId, tranAmt, ... }) → group_key 발급 POST /transfer/request - executePayout({ groupKey, billingTranId, executeType: 'NOW'|'CANCEL', webhookUrl }) POST /transfer/execute - inquireStandbyByGroup(groupKey) — POST /request/result/group_key - inquireResultByCase(apiTranId) — POST /transfer/result - inquireResultByDay(bankTranDate yyyyMMdd) — POST /transfer/result/date - inquireResultByGroup(groupKey) — POST /transfer/result/group_key - fetchRemainingBalance() — POST /account/remain ### utils/payple.ts - fetchPaypleAccessToken export (정산지급대행 호출에서 재사용) ### 환경변수 기존 PAYPLE_HUB_URL (demohub.payple.kr → live에선 hub.payple.kr로 변경) / PAYPLE_CST_ID / PAYPLE_CUST_KEY 그대로 사용. 추가 env 없음. 다음 (PR D): - 정산 사이클 cron — 매월 15일 KST 09:00 - 최소 1만원 충족 시 빌링키별 requestPayoutStandby → executePayout 자동화 - 환수 차감 (Refunded 합계 - 다음 사이클 차감) - 음수 잔액 이월 처리 - Webhook 수신 (POST /api/payouts/webhook, 멱등성 + 인증)
[FEAT] Payple 정산지급대행 유틸 (이체 대기/실행/조회/잔액) 추가 (#491 PR C)
정산 사이클 cron이 생성할 이체 이력 모델. 매월 15일 KST 사이클별로
판매자별 1 row 생성. 마이그레이션 dev DB 적용 완료.
### 모델
SettlementPayout:
- user_id + cycle_start 복합 unique (한 사이클에 한 row만)
- cycle_start/end: 이번 사이클 범위 (예: 직전 15일 0시 ~ 이번 15일 0시 직전)
- amount_gross: Succeed Settlement 합계
- amount_refund: Refunded Settlement 합계
- carry_over_prev: 이전 사이클 음수 이월액 (절댓값)
- amount_net: 최종 지급액 = gross - refund - carry_over_prev (음수 가능)
- billing_tran_id: 시점 스냅샷 (SettlementAccount에서 복사)
- group_key, api_tran_id: Payple 응답 추적용
- status (PayoutStatus): Pending/Succeed/Failed/Skipped
- reason: Skipped/Failed 사유
### enum 신규
PayoutStatus { Pending, Succeed, Failed, Skipped }
- 기존 Status enum과 분리 (Skipped는 정산 전용 의미)
### User 관계
- payouts SettlementPayout[]
매월 15일 KST 09:00 cron으로 활성 판매자들의 net 금액을 산출해 Payple 빌링키로
자동 이체. Webhook 수신은 다음 커밋.
### settlement-payout.repository.ts (신규)
- findEligibleSellers — 승인 + is_active + 빌링키 보유 판매자
- getCarryOverFromLastPayout — 직전 사이클 net 음수 이월액(절댓값)
- aggregateUnsettledForCycle — raw SQL로 Succeed/Refunded 합계
- createPendingPayout — user_id+cycle_start 멱등 upsert
- updatePaypleGroupKey / markFailed / markSucceeded
- findPayoutByGroupKey — webhook 매칭용
### settlement-payout.service.ts (신규)
runPayoutCycle():
- Redis 분산 락 (CYCLE_LOCK_TTL 30분)
- 사이클 범위 계산 (직전 15일 KST 00:00 ~ 이번 15일 KST 00:00)
- 각 판매자별 processOneSeller:
- net = gross - refund - carry_over_prev
- 빌링키 미보유 → Skipped + 사유
- net < 10,000원 → Skipped + 이월 (음수면 다음 사이클 차감)
- 정상 → requestPayoutStandby → group_key 저장 → executePayout(NOW)
- Payple 호출 실패 시 markFailed
- 결과 카운트 로그 (paid/skipped_min/skipped_no_key/failed)
### settlement-payout.job.ts (신규)
- cron '0 9 15 * *', Asia/Seoul
- src/index.ts에 startSettlementPayoutJob() 부트스트랩
### 환경변수 (신규)
- PAYPLE_PAYOUT_CYCLE_ENABLED ('false'로 비활성화)
- PAYPLE_PAYOUT_WEBHOOK_URL (executePayout 요청에 첨부)
executePayout(NOW) 요청 시 첨부한 webhook_url로 Payple이 이체 결과를 전송. group_key 매칭으로 SettlementPayout을 Succeed/Failed로 마감. ### 신규 - POST /api/payouts/webhook - handlePayoutWebhook 컨트롤러: - group_key로 findPayoutByGroupKey 매칭 - result='A0000' → markSucceeded(api_tran_id, completed_at=now) - result≠A0000 → markFailed(reason) - 멱등: 이미 Pending이 아닌 row는 200 OK만 (Payple 재전송 차단) - 매칭 없는 group_key는 200 OK (false-positive 알림 방지) - redactor로 로그 마스킹 ### Swagger 명세 - /api/payouts/webhook 요청/응답 schema - 운영 전 IP allowlist/shared-secret 인증 추가 권고 명시 ### 운영 안전성 - Payple 명세에 인증 방식 미명시 → sandbox 검증 후 별도 보안 강화 권장
[FEAT] 정산 사이클 cron + 자동 지급이체 + Webhook 수신 (#491 PR D)
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.
📌 기능 설명
📌 구현 내용
📌 구현 결과
📌 논의하고 싶은 점