sequenceDiagram
autonumber
participant U as 사용자
participant FE as 프론트엔드
participant API as Backend (AccountController)
participant SVC as BillingService
participant DB as MySQL
participant TOSS as Toss Billing API
U->>FE: 자동결제 등록 흐름 → Toss SDK
TOSS-->>FE: authKey, customerKey
FE->>API: POST /accounts/billing/issue { authKey, customerKey }
API->>SVC: issueBillingKey(...)
SVC->>TOSS: POST /v1/billing/authorizations/issue { authKey, customerKey }
TOSS-->>SVC: { billingKey, card.number, card.issuerCode }
SVC->>DB: 기존 PaymentMethod deactivate("새 빌링키 발급으로 교체")
SVC->>DB: 새 PaymentMethod.activateWithBilling(billingKey, ...)
Note over SVC: 첫 결제 즉시 charge (chargeBillingInternal)
SVC->>DB: Payment(status=PENDING, tossOrderId="BILLING_${UUID}")
SVC->>TOSS: POST /v1/billing/{billingKey} { customerKey, amount, orderId, orderName }
TOSS-->>SVC: { paymentKey, method, approvedAt }
SVC->>DB: payment.confirmTossPayment(...) → status=DONE
SVC->>DB: account.initMembershipByPayment(payment) → 새 Membership
API-->>FE: { cardName, cardNumber, membershipType, startDate, endDate }
sequenceDiagram
autonumber
participant TOSS as Toss Payments
participant API as Backend (TossWebhookController)
participant SVC as AccountService
participant DB as MySQL
Note over TOSS: 사용자가 가상계좌에 입금 완료
TOSS->>API: POST /webhooks/tosspayments { eventType: "DEPOSIT_CALLBACK", data.paymentKey }
API->>SVC: confirmVirtualAccountDeposit(paymentKey)
Note over SVC: SECURITY: Toss V2는 webhook signature 미제공 → 항상 Toss API 재조회 (authoritative source)
SVC->>TOSS: GET /v1/payments/{paymentKey}
TOSS-->>SVC: { orderId, status, method, approvedAt }
SVC->>DB: paymentRepository.findByTossOrderId(orderId)
alt 이미 DONE
SVC-->>API: 멱등 — skip
else WAITING_DEPOSIT && Toss DONE
SVC->>DB: payment.confirmTossPayment(...) → DONE
SVC->>DB: Membership 또는 TokenPack 생성
end
API-->>TOSS: 200 "OK"
Toss 웹훅 prod URL 미등록 (Issue #151 미해결 항목): cutover 시점에 Toss 콘솔에서 https://api-new.semugpt.co.kr/webhooks/tosspayments 등록 필요
Webhook signature 부재: Toss V2가 서명을 제공하지 않음. 외부 위조 요청은 paymentKey로 Toss API를 재조회해서 1차 차단하지만, 존재하지 않는 paymentKey는 무시하므로 DoS 가능성 있음 (TossWebhookController 주석 참조)
PortOne V1 잔재: PaymentStatus에 PRE_VERIFICATION, POST_VERIFICATION, ADDITIONAL_VERIFICATION 등 PortOne 시절 상태값이 남아 있음. verifyCardPaymentPre/Post/Additional 메서드도 미사용 가능성. 정리 필요
Toss live 키 미반영 (Issue #151 미해결): 현재 prod env-var는 user 제공 대기 상태. 빌링키 발급/charge가 실키로 동작하려면 prod 시크릿 주입 필요
결제 WAITING_DEPOSIT 정리 cron 부재: 가상계좌 입금 안 한 채 만료된 PENDING/WAITING_DEPOSIT payment가 누적될 수 있음 — 별도 정리 job 없음
단일 활성 결제수단 가정: paymentMethodRepository.findActivatedAndDeactivatedByAccountId(accountId)가 single-row 반환 가정. 멀티 카드 등록 시 비정상 동작 가능 — switchTierForTest가 매번 PaymentMethod를 deleteAll하는 이유
취소 시 잔여 일수 비례 환불 미구현: cancelTossPayment는 Toss에 전액 취소 요청. 멤버십 잔여 일수에 따른 부분 환불 로직 없음