- ์น๊ฐ์ ์์ฝ์ ๊ณ ๋ฅธ ๋ค ๊ฒฐ์ ์ค๋น โ ๊ฒฐ์ ์ฐฝ โ ๊ฒฐ์ ์น์ธ ์์ผ๋ก ๊ฒฐ์ ํ๋ค. ๊ฒฐ์ ์ฌ๋ ํ ์คํ์ด๋จผ์ธ (Toss)๋ค.
- ๊ฒฐ์ ์ค๋น
- ๊ณ ๋ฅธ ์์ฝ์ด ๋ชจ๋ ์๊ธฐ ๊ฒ์ด๊ณ ์ด์ ์๋์ง, ์ด๋ฏธ ํ๋ฆฐ ์ข์๊ณผ ๊ฒน์น์ง ์๋์ง ํ์ธํ๋ค.
- ์ฃผ๋ฌธ๊ณผ ๊ฒฐ์ ๋ฅผ ๊ฒฐ์ ๋๊ธฐ ์ํ๋ก ๋ง๋ค๊ณ ์ฃผ๋ฌธ ์ฝ๋์ ๊ธ์ก์ ๋๋ ค์ค๋ค.
- ๊ฒฐ์ ์ฐฝ: ํด๋ผ์ด์ธํธ๊ฐ ์ฃผ๋ฌธ ์ฝ๋์ ๊ธ์ก์ผ๋ก Toss ๊ฒฐ์ ์ฐฝ์ ์ฐ๋ค. ์น๊ฐ์ด ๊ฒฐ์ ํ๋ฉด Toss๊ฐ
paymentKey๋ฅผ ์ค๋ค. - ๊ฒฐ์ ์น์ธ
- ํด๋ผ์ด์ธํธ๊ฐ
paymentKey, ์ฃผ๋ฌธ ์ฝ๋, ๊ธ์ก์ ๋ณด๋ด๋ฉด ์๋ฒ๊ฐ Toss์ ์น์ธ์ ์์ฒญํ๋ค. - ์น์ธ๋๋ฉด ์ฃผ๋ฌธ์ด ์๋ฃ๋๊ณ ์๋งค์ ์น์ฐจ๊ถ์ด ๋ง๋ค์ด์ง๋ค.
- ์ข์ ์ ์ ๋ฅผ ์์ฝ์์ ์๋งค๋ก ๋ฐ๊พธ๋ ์ผ์ ์ ์ ๋ค Outbox Worker๊ฐ ํ๋ค.
- ํด๋ผ์ด์ธํธ๊ฐ
- ์น์ธ ์์ฒญ์ ๊ฐ์ ๊ฒฐ์ ์ ์ฌ๋ฌ ๋ฒ ์ฌ ์ ์๋ค(์๋ก๊ณ ์นจ, ์ฌ์๋). ์นด๋๋ ํ ๋ฒ๋ง ์ฒญ๊ตฌ๋ผ์ผ ํ๊ณ ์๋งค๋ ํ ๋ฒ๋ง ๋ง๋ค์ด์ ธ์ผ ํ๋ค.
- Toss๊ฐ ์น์ธํ๋์ง ๋ชจ๋ฅด๋ ๊ฒฝ์ฐ๊ฐ ์๋ค(ํ์์์, Toss 5xx). ์ด๋๋ ์คํจ๋ก ๋จ์ ํ์ง ์๊ณ , ์น๊ฐ์ด ๋ค์ ์์ฒญํ๋ฉด Toss์ ์ํ๋ฅผ ์กฐํํด ๋ง์ถ๋ค. ๊ทธ ์ฌ์ด ์์ฝ TTL์ด ์ข์์ ๊ฐ์ ธ๊ฐ์ง ์๋๋ก ์ข์์ ๋ถ์ก์ ๋๋ค.
| ํ๊ตญ์ด | ์์ด | ์ค๋ช |
|---|---|---|
| ๊ฒฐ์ | Payment | ์ฃผ๋ฌธ ํ๋์ ๋ํ ๊ฒฐ์ . ๊ธ์ก๊ณผ ์ํ๋ฅผ ๊ฐ์ง๋ค |
| ๊ฒฐ์ ์๋ | PaymentAttempt | Toss์ ๋ณด๋ด๋ ์น์ธ ์์ฒญ ํ๋์ ๊ธฐ๋ก. ๊ฐ์ ์์ฒญ์ ์ฌ์๋๋ ๊ฐ์ ์๋๋ค |
| ์๋ ID | attemptId | ์๋๋ฅผ ๊ตฌ๋ถํ๋ ์ธ๋ถ ๋ฉฑ๋ฑ ํค. paymentKey์์ ๊ณ์ฐํ๋ค |
| paymentKey | - | Toss๊ฐ ๊ฒฐ์ ์ฐฝ์์ ๋ฐ๊ธํ ๊ฒฐ์ ์๋ณ์ |
| ์ฃผ๋ฌธ ์ฝ๋ | orderCode | Toss์ ๋๊ธฐ๋ ์ฃผ๋ฌธ ๋ฒํธ(orderId). ์ฃผ๋ฌธ์ ์ฃผ๋ฌธ ์ฝ๋์ ๊ฐ๋ค |
| ํ์ ๋์ | PaymentConfirmTarget | ํ์ ํ ๊ฒฐ์ ๋ฅผ ๊ฐ๋ฆฌํค๋ ์ธ ๊ฐ(paymentKey, ์ฃผ๋ฌธ ์ฝ๋, ๊ธ์ก). DB์์ ์ฝ๋๋ค |
| ๊ฒฐ๊ณผ ๋ถ๋ช | - | ์์ฒญ์ ๋๊ฐ์ง๋ง Toss๊ฐ ์น์ธํ๋์ง ๋ชจ๋ฅด๋ ์คํจ |
| ๋ฏธ์ ์ก | NOT_SENT | ์์ฒญ์ด Toss์ ๋๋ฌํ์ง ์์ ๊ฒ์ด ํ์ ์ธ ์คํจ |
| TX A, TX B | - | ์น์ธ ์์ ํธ๋์ญ์ ๊ณผ ์น์ธ ํ์ ํธ๋์ญ์ |
classDiagram
Payment --> Order
Payment --> Member
Payment "1" <-- "N" PaymentAttempt : paymentId
Payment "1" <-- "N" PaymentOutbox : aggregateId
Aggregate Root
- ์์ฑ:
member,order,orderCode,amount,paymentStatus,paymentKey,paymentMethod,paidAt,failedAt,cancelledAt,refundedAt,failureCode,failureMessage - ํ์
static create(member, order): ์ฃผ๋ฌธ์ ์ฝ๋์ ๊ธ์ก์ผ๋ก ๊ฒฐ์ ๋๊ธฐ ๊ฒฐ์ ๋ฅผ ๋ง๋ ๋คapprove(method, paymentKey): ๊ฒฐ์ ์๋ฃ. Toss๊ฐ ๋ฐ๊ธํpaymentKey๋ฅผ ์ ์ฅํ๋คcancel(reason),refund(),fail(code, message)
- ๊ท์น
- ๊ฒฐ์ ๋๊ธฐ์์๋ง ์น์ธ, ์ทจ์, ์คํจํ ์ ์๋ค. ๊ฒฐ์ ์๋ฃ์์๋ง ํ๋ถํ ์ ์๋ค
- ๊ฐ์ ์ฃผ๋ฌธ์ ๊ฒฐ์ ์๋ฃ๋ ๊ฒฐ์ ๊ฐ ์์ผ๋ฉด ์น์ธํ์ง ์๋๋ค
member,order๋ DB ์ธ๋ํค๋ฅผ ๊ฑธ์ง ์๋๋คcancel,refund,fail์ ๋ถ๋ฅด๋ ๊ณณ์ ์์ง ์๋ค. Toss๊ฐ ์น์ธ์ ๊ฑฐ์ ํด๋ ๊ฒฐ์ ๋ ๊ฒฐ์ ๋๊ธฐ๋ก ๋จ๊ณ ์๋๋ง ์คํจ๊ฐ ๋๋ค
Enum: PENDING ๊ฒฐ์ ๋๊ธฐ, PAID ๊ฒฐ์ ์๋ฃ, CANCELLED ๊ฒฐ์ ์ทจ์, REFUNDED ํ๋ถ ์๋ฃ, FAILED ๊ฒฐ์ ์คํจ
Entity, Payment N:1 (์ธ๋ํค ์์ด paymentId)
- ์์ฑ:
attemptId,attemptType,status,paymentKey,errorCode,errorMessage - ํ์
static startApproval(paymentId, attemptId, paymentKey): ์งํ ์ค ์น์ธ ์๋๋ฅผ ๋ง๋ ๋คmarkSucceeded(),markFailed(...),markNotSent(...),markReviewRequired(...): ์งํ ์ค์์๋ง ๋ฐ๋๋คreopen(): ๋ฏธ์ ์ก์ ๋ค์ ์งํ ์ค์ผ๋ก ๋๋๋ฆฐ๋ค
- ๊ท์น
attemptId๋sha256("apv:" + paymentKey)๋ค. ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐ์ง ์๋๋ค. ์ ๋ํฌ ์ ์ฝ์ด ์๋ค- ์๋์
paymentKey์ ๊ฒฐ์ , ์ ํ์ด ์์ฒญ๊ณผ ๋ค๋ฅด๋ฉด ๊ฐ์ ์ค๋ฅ(PAYMENT_ATTEMPT_REQUEST_MISMATCH)๋ก ๊ฑฐ์ ํ๋ค. ์ด๋ ์ชฝ์ด ๋ค๋ฅธ์ง๋ ๋ก๊ทธ์๋ง ๋จ๊ธด๋ค - ์์ฒญ๊ณผ์ ๋์กฐ๋ ์ ๊ธ ์๋ ์์ ๋จ๊ณ์์ ํ๋ค. ํ์ (TX B)์ ์ ์ฅ๋ ๊ฐ์ผ๋ก ๋น๊ตํ๋ฏ๋ก
paymentKey์ถ์ด ํญ์ ์ผ์นํ๋ค. ์์ฒญ์paymentKey๋attemptIdํ์๊ณผ ์ ๋ํฌ ์ ์ฝ์ผ๋ก ์ด๋ฏธ ์๋์ ๋ฌถ์ฌ ์๋ค processingOwner,processingLeaseUntil,nextRetryAt์ ๋ณต๊ตฌ Worker๊ฐ ์ ์ ๊ณผ ๋ฐฑ์คํ์ ์ด๋ค
Enum
| ์ํ | ๋ป | ๊ฐ์ ์๋๋ก ๋ค์ ์น์ธ ์์ฒญ์ด ์ค๋ฉด |
|---|---|---|
IN_PROGRESS |
Toss ํธ์ถ ์ ์ด๊ฑฐ๋ ๊ฒฐ๊ณผ ๋ถ๋ช | Toss์ ์ํ๋ฅผ ์กฐํํด ๋ง์ถ๋ค(PaymentApprovalReconciler) |
SUCCEEDED |
์น์ธ ํ์ | Toss ํธ์ถ ์์ด ์ด์ ๊ฒฐ๊ณผ๋ฅผ ๋๋ ค์ค๋ค |
FAILED |
Toss๊ฐ ๊ฑฐ์ | ๊ฑฐ์ ํ๋ค |
NOT_SENT |
์์ฒญ์ด Toss์ ๋๋ฌํ์ง ์์ | ๊ฐ์ ์๋๋ฅผ ๋ค์ ์งํ ์ค์ผ๋ก ์ด๊ณ ์น์ธ์ ๋ค์ ๋ณด๋ธ๋ค |
REVIEW_REQUIRED |
์น์ธ๋์ ์ ์์ด ์๋ ํ์ ํ๋ฉด ์ ๋จ | ๊ฑฐ์ ํ๊ณ ๊ณ ๊ฐ์ผํฐ๋ก ์๋ดํ๋ค |
REVIEW_REQUIRED๋ ์ข
์ฐฉ์ ์ด๋ค. ๋๊ฐ๋ ์ ์ด๊ฐ ์์ด ์๋์ผ๋ก ๋์ด์๋์ง ์๋๋ค. ์ฌ์ ๋ errorCode์ ๋จ๋๋ค.
| ์ฌ์ | ์ธ์ |
|---|---|
REVIEW_RESULT_MISMATCH |
Toss ์กฐํ ๊ฒฐ๊ณผ๊ฐ ์ ์ฅ๋ ์ฃผ๋ฌธ ์ฝ๋, ๊ธ์ก, paymentKey์ ๋ค๋ฅด๋ค |
REVIEW_PAYMENT_KEY_MISSING |
์๋์ paymentKey๊ฐ ์์ด ์กฐํ์กฐ์ฐจ ๋ชป ํ๋ค |
REVIEW_SEAT_LOST |
ํ์ ๋จ๊ณ์์ ์ข์์ด ์ด๋ฏธ ๋ค๋ฅธ ์๋งค์ ๊ฒ์ด๋ค |
REVIEW_DEPARTED |
ํ์ ๋จ๊ณ์์ ์ถ๋ฐ ์๊ฐ์ด ์ง๋ฌ๋ค |
REVIEW_RECOVERY_TIMEOUT |
๋ณต๊ตฌ Worker๊ฐ ์ต๋ ๋๊ธฐ ์๊ฐ์ ๋๊ธฐ๋๋ก ํ์ ํ์ง ๋ชปํ๋ค |
PaymentAttemptType์ APPROVAL, CANCELLATION์ด๋ค. ์ทจ์ ์๋๋ ์์ง ์ฐ์ง ์๋๋ค.
confirm์ ํธ๋์ญ์
์ ์ด์ง ์๊ณ ์ธ ๋จ๊ณ๋ฅผ ์๋๋ค.
- ์์ (
PaymentApprovalStarter,PaymentAttemptManager)- ์ฃผ๋ฌธ, ๊ฒฐ์ ์์ ์์ ๊ธ์ก(์์ฒญ = ์ฃผ๋ฌธ = ๊ฒฐ์ )์ ํ์ธํ๋ค.
- ๊ฐ์
attemptId์ ์๋๊ฐ ์์ผ๋ฉด ์ ํ๋๋ก ์ฒ๋ฆฌํ๋ค. ์งํ ์ค์ด๋ฉดPaymentApprovalReconciler๊ฐ Toss๋ฅผ ์กฐํํด ํ์ ํ๋ค. - ์์ผ๋ฉด ๊ฒฐ์ ๋๊ธฐ์ธ์ง, ์์ฝ์ด ์ด์ ์๋์ง, ๊ฐ์ ์ฃผ๋ฌธ์ ์๋ฃ๋ ๊ฒฐ์ ๊ฐ ์๋์ง ํ์ธํ๋ค.
- TX A์์ ๊ฒฐ์ ๋ฅผ ์ ๊ทธ๊ณ ๋ค์ ํ์ธํ ๋ค ์งํ ์ค ์๋๋ฅผ ์ปค๋ฐํ๋ค.
- Toss ์น์ธ ํธ์ถ: DB ํธ๋์ญ์
๋ฐ์์ ํ๋ค.
- ๊ฑฐ์ (4xx)์ด๋ฉด ์๋๋ฅผ ์คํจ๋ก ๋ฐ๊พธ๊ณ ์ข์ ๋ณดํธ๋ฅผ ํผ๋ค.
- ๋ฏธ์ ์ก์ด๋ฉด ์๋๋ฅผ ๋ฏธ์ ์ก์ผ๋ก ๋ฐ๊พผ๋ค.
- ๊ฒฐ๊ณผ ๋ถ๋ช ์ด๋ฉด ์งํ ์ค์ผ๋ก ๋๊ณ ์ข์์ ๋ถ์ก๋๋ค.
- ํ์ (
PaymentApprovalFinalizer, TX B)- ๊ฒฐ์ ๋ฅผ ์ ๊ทธ๊ณ ์๋ ์ํ๋ฅผ ๋ค์ ๋ณธ๋ค.
- Toss ์๋ต์ ๊ธ์ก,
paymentKey, ์ฃผ๋ฌธ ์ฝ๋๊ฐ DB์ ์ ์ฅ๋ ๊ฐ๊ณผ ๊ฐ์์ง ํ์ธํ๋ค. ์์ฒญ์ ์ฐ์ง ์๋๋ค. - ์ถ๋ฐ ์๊ฐ์ด ์ง๋ฌ๋์ง, ์ข์์ด ๋ค๋ฅธ ์๋งค์ ๊ฒ์ด ๋๋์ง ์ ๊ธ ์๋์์ ๋ค์ ๋ณธ๋ค.
- ์ฃผ๋ฌธ ์๋ฃ, ์๋งค ์์ฑ, ๊ฒฐ์ ์๋ฃ, ์๋ ์ฑ๊ณต, Outbox ํ ์ ์ฅ์ ํ ํธ๋์ญ์ ์ผ๋ก ์ปค๋ฐํ๋ค.
- ๊ท์น
- ํ์ ๋จ๊ณ์์ ์๋๊ฐ ์ด๋ฏธ
FAILED๋ฉด ๊ฑฐ์ ํ๋ค. Toss๋ ์น์ธํ๋๋ฐ ๊ธฐ๋ก๋ง ์คํจ์ธ ๊ฒฝ์ฐ์ด๋ฉฐ, ์ง๊ธ์ ๊ฐ์ง๋ง ํ๊ณ ๋์ด๋ฆด ๊ฒฝ๋ก๊ฐ ์๋ค - ์ถ๋ฐ์ด ์ง๋ฌ๊ฑฐ๋ ์ข์์ ๋นผ์๊ฒผ์ผ๋ฉด ํ์ ํ์ง ์๊ณ ์๋ ํ์ธ์ผ๋ก ๋จ๊ธด๋ค. ํ์ ์ ์ฌ์ ๋ง ์ค์ด ์ค๋จํ๊ณ , ์๋ ๋งํน๊ณผ ์ข์ ํด์ ๋ Payment ์ ๊ธ์ด ํ๋ฆฐ ๋ค
PaymentConfirmService๊ฐ ํ๋ค seat_booking์ ์ข์, ๊ตฌ๊ฐ ์ ๋ํฌ ์ ์ฝ์ด ์๊ณ ์๋งค ์์ฑ๋ ์ข์ ์ถฉ๋์ ๋ณด์ง ์๋๋ค. ๊ฒน์นจ์ ๊ฒฐ์ ์ค๋น์ ํ์ ๋จ๊ณ์ DB ์ฌ๊ฒ์ฆ์ผ๋ก ๋ง์ผ๋ฉฐ, ์ ์ฝ ์์ฒด๋ #280์ด๋ค
- ํ์ ๋จ๊ณ์์ ์๋๊ฐ ์ด๋ฏธ
์คํจ ์๋ต
| ๊ฒฝ์ฐ | ์๋ต | ํด๋ผ์ด์ธํธ๊ฐ ํ ์ผ |
|---|---|---|
| Toss ๊ฑฐ์ (4xx) | Toss ์ํ ์ฝ๋ ๊ทธ๋๋ก | ์คํจ ์๋ด |
| ๋ฏธ์ ์ก | 503 PAYMENT_118 |
๋ฐ๋ก ๋ค์ ์๋ํด๋ ๋๋ค |
| ๊ฒฐ๊ณผ ๋ถ๋ช , ์์ง ์ฒ๋ฆฌ ์ค | 409 PAYMENT_113 |
์ ์ ๋ค ๊ฐ์ ์์ฒญ์ผ๋ก ๋ค์ ํ์ธํ๋ค. ์๋ ์ฌ์๋ํ์ง ์๋๋ค |
| ์ด๋ฏธ ์คํจํ ์๋ | 409 PAYMENT_112 |
์๋ก ๊ฒฐ์ ํ๋ค |
| ์๋ ํ์ธ ๋์ | 409 PAYMENT_117 |
๊ณ ๊ฐ์ผํฐ |
| ํ์ ๋จ๊ณ์์ ์๋์ ์ ์ฅ๋ ๊ฒฐ์ ํค๊ฐ ์์ | 500 PAYMENT_119 |
๋ค์ ์๋ํ์ง ์๋๋ค. ์๋ฒ ์ค๋ฅ์ด๋ฉฐ ์ด์ ํ์ธ์ด ํ์ํ๋ค |
- ๋งฅ๋ฝ: Toss ํธ์ถ ์ค์ ์๋ฒ๊ฐ ์ฃฝ๊ฑฐ๋ ์๋ต์ ๋ชป ๋ฐ์ผ๋ฉด, ๋์ ๋๊ฐ๋๋ฐ ์๋ฌด ๊ธฐ๋ก๋ ์์ ์ ์๋ค. Toss ํธ์ถ์ DB ํธ๋์ญ์ ์์ ๋๋ฉด ๊ทธ ๊ธฐ๋ก๋ ํจ๊ป ๋กค๋ฐฑ๋๋ค.
- ๊ฒฐ์ : Toss๋ฅผ ๋ถ๋ฅด๊ธฐ ์ ์ ์งํ ์ค ์๋๋ฅผ ๋ณ๋ ํธ๋์ญ์
(TX A,
REQUIRES_NEW)์ผ๋ก ๋จผ์ ์ปค๋ฐํ๋ค. Toss ํธ์ถ์ ํธ๋์ญ์ ๋ฐ์์ ํ๊ณ , ์ฑ๊ณตํ๋ฉด ํ์ ์ ๋ ๋ค๋ฅธ ํธ๋์ญ์ (TX B)์ผ๋ก ์ปค๋ฐํ๋ค. - ๊ฒฐ๊ณผ: ์ด๋ค ์์ ์ ์ฅ์ ๊ฐ ๋๋ ์งํ ์ค ์๋๊ฐ ๋จ์, ๋ค์ ์์ฒญํ๋ฉด Toss ์กฐํ๋ก ๋ง์ถ ์ ์๋ค. ํ์ ์ ์ฃผ๋ฌธ, ์๋งค, ๊ฒฐ์ , ์๋, Outbox๋ฅผ ํ ๋ฒ์ ์ปค๋ฐํ๋ฏ๋ก ์ผ๋ถ๋ง ๋ฐ์๋์ง ์๋๋ค.
- ๋งฅ๋ฝ: ๊ฐ์ ๊ฒฐ์ ์ ์น์ธ ์์ฒญ์ด ๋์์ ๋๋ ๋ฐ๋ณตํด์ ์จ๋ค. DB PK๋ ์ ์ฅํด์ผ ์ ํด์ง๋ฏ๋ก, ๋ค์ ์จ ์์ฒญ์ด ์ด์ ์๋๋ฅผ ์ฐพ๋ ํค๋ก ์ธ ์ ์๋ค.
- ๊ฒฐ์ :
attemptId = sha256("apv:" + paymentKey)๋ก ๊ฐ์ ์์ฒญ์ด ํญ์ ๊ฐ์ ํค๋ฅผ ๊ฐ๊ฒ ํ๊ณ ,attempt_id์ ์ ๋ํฌ ์ ์ฝ์ ๊ฑด๋ค. - ๊ฒฐ๊ณผ: ๋์์ ์จ ๋ ์์ฒญ ์ค ํ๋๋ง ์๋๋ฅผ ๋ง๋ ๋ค. ์ง ์ชฝ์ ์ ๋ํฌ ์ถฉ๋์ ๋ฐ์ ๊ธฐ์กด ์๋์ ์ฌ์์ฒญ ํ๋ฆ์ผ๋ก ๊ฐ๋ค. ์ทจ์๋ ํ ๊ฒฐ์ ์ ์ฌ๋ฌ ๋ฒ ์ผ์ด๋ ์ ์์ด
cnl:{paymentKey}:{์๋ฒ}์ผ๋ก ๊ตฌ๋ถํ๋๋ก ์ค๋น๋ผ ์๋ค.
- ๋งฅ๋ฝ: ์ ๊ธ ์๋ ์ฌ์ ๊ฒ์ฆ๊ณผ ์ค์ ์ปค๋ฐ ์ฌ์ด์ ๋ค๋ฅธ ์์ฒญ์ด ๋ผ์ด๋ค ์ ์๋ค.
- ๊ฒฐ์ : ์์(์ ๊ธ ์์)์์ ๋นจ๋ฆฌ ๊ฑธ๋ฌ Toss ํธ์ถ์ ์๋ผ๊ณ , TX A์ TX B์์ ๊ฒฐ์ ๋ฅผ
SELECT FOR UPDATE๋ก ์ ๊ทผ ๋ค ๋ค์ ๊ฒ์ฆํ๋ค. ๋ง์ง๋ง ๋ฐฉ์ด๋attempt_id์ ๋ํฌ ์ ์ฝ์ด๋ค. - ๊ฒฐ๊ณผ: ๋์ ์์ฒญ์ด ์๋ ์น์ธ๊ณผ ํ์ ์ ํ ๋ฒ์ฉ๋ง ์ผ์ด๋๋ค. ์๋ ์ํ๋ฅผ ๋ฐ๊พธ๋ ๋ชจ๋ ๊ฒฝ๋ก๊ฐ ๊ฒฐ์ ๋ฅผ ๋จผ์ ์ ๊ทธ๊ณ ์๋๋ฅผ ์ฝ๋๋ค. ์ ๊ทธ๊ธฐ ์ ์ ์ฝ์ผ๋ฉด ๋จผ์ ์ปค๋ฐ๋ ์ฑ๊ณต์ ์คํจ๋ก ๋ฎ์ด์ธ ์ ์๋ค.
- ๋งฅ๋ฝ: Toss 5xx๋ ํ์์์์ Toss๊ฐ ์น์ธํ ๋ค ์๋ต๋ง ์คํจํ์ ์ ์๋ค. ์คํจ๋ก ์ฒ๋ฆฌํ๊ฑฐ๋ 5xx๋ฅผ ๊ทธ๋๋ก ๋ด๋ฆฌ๋ฉด, ์ฌ์๋ํ๋ ํด๋ผ์ด์ธํธ๊ฐ ์น์ธ์ ๋ค์ ๋ณด๋ด ์ด์ค ์ฒญ๊ตฌ๊ฐ ๋๋ค.
- ๊ฒฐ์ : ๊ฒฐ๊ณผ ๋ถ๋ช
์ ์๋๋ฅผ ์งํ ์ค์ผ๋ก ๋๊ณ ์ฌ์๋๋ฅผ ๋ถ๋ฅด์ง ์๋ 409๋ก ์๋ตํ๋ค. ๊ฐ์ ์์ฒญ์ด ๋ค์ ์ค๋ฉด ์น์ธ์ ๋ค์ ๋ณด๋ด์ง ์๊ณ Toss์ ์กฐํํ๋ค. Toss ํธ์ถ๋ ์กฐํ(GET)๋ง ์๋ ์ฌ์๋ํ๊ณ ์น์ธ(POST)์ ์ฌ์๋ํ์ง ์๋๋ค. Toss๋ POST์
Idempotency-Key๋ฅผ ์ง์ํ์ง๋ง(Toss ๋ฌธ์ ๊ธฐ์ค 15์ผ dedup, 2026-10 ํ์ธ) ์น์ธ์ ์์ง ํค๋๋ฅผ ๋ณด๋ด์ง ์๊ณ (#260) ์ทจ์๋ ํธ์ถ๋ง๋ค ์ ํค๋ฅผ ๋ง๋ค์ด, ์ ์ก ์ธต์์ ๋ค์ ๋ณด๋ด๋ฉด ๊ฒ์ดํธ์จ์ด๊ฐ ๋ณ๊ฐ ์์ฒญ์ผ๋ก ๋ฐ๋๋ค. ๋ฏธ๋๋ฌ, ๊ฒฐ๊ณผ ๋ถ๋ช , ๊ฑฐ์ ์ ํ์ ์TossPaymentClient๊ฐ ์ค์ด ๋ณด๋ธ ์ ์ก ๋จ๊ณ๋กPaymentGatewayException์ด ํ๊ณ , ์๋ ๊ธฐ๋ก๊ณผ HTTP ์๋ต์ด ๊ฐ์ ํ์ ์ ์ด๋ค. - ๊ฒฐ๊ณผ: ์ด์ค ์ฒญ๊ตฌ๊ฐ ์๊ธฐ์ง ์๋๋ค. ๋์ ์น๊ฐ์ด ๋ค์ ์์ฒญํ์ง ์์ผ๋ฉด ์งํ ์ค ์๋๊ฐ ๊ทธ๋๋ก ๋จ๋๋ค. ์ด๋ฅผ ๋์ฌํ๋ ๋ณต๊ตฌ Worker๊ฐ ์ค๋๋ ์๋๋ฅผ ์ ๊ธฐ์ ์ผ๋ก ๊ฐ์ ธ๊ฐ ์ฒ๋ฆฌํ๋ค.
- ๋งฅ๋ฝ: ์์ฝ TTL์ 10๋ถ์ธ๋ฐ ๊ฒฐ๊ณผ ๋ถ๋ช ์ธ ์๋๋ ๊ทธ๋ณด๋ค ์ค๋ ๋จ๋๋ค. ๊ทธ ์ฌ์ด ์ข์์ด ํ๋ ค ๋จ์ด ๊ฐ์ ธ๊ฐ๋ฉด, Toss๊ฐ ์น์ธํ ๊ฒฐ์ ๋ฅผ ํ์ ํ ์ ์๋ค. ๋ฐ๋๋ก ๊ฒฐ์ ๋ด๋ด ์ข์์ ์๊ตฌ ์ ์ ํ๋ฉด ์ดํํ ๊ฒฐ์ ๊ฐ ์ข์์ ์ดํ์ผ๊น์ง ๋ฌถ๋๋ค.
- ๊ฒฐ์ : ๊ฒฐ๊ณผ ๋ถ๋ช
(Toss 5xx, ํ์์์, ์กฐํ ์คํจ, ์กฐํ๊ฐ ์์ง ์ฒ๋ฆฌ ์ค)์ผ ๋๋ง
reservation_payment_hold.lua๋ก ์ข์ field์ ๋ง๋ฃ๋ฅผ ์์ ๊ณ , ํ์ ์คํจ์ ์๋ ํ์ธ์ผ๋ก ๋๋๋ฉดreservation_payment_release.lua๋ก ๋๋๋ฆฐ๋ค. ํธ์ถ ์ง์ ์PaymentSeatGuard๊ฐ ๋ชจ์ผ๋ฉฐ ๋ณดํธ ์คํจ๋ ๋ก๊ทธ๋ง ๋จ๊ธด๋ค. - ๊ฒฐ๊ณผ: ์ ์ ์น์ธ ๊ฒฝ๋ก๋ Redis ํธ์ถ์ด ๋์ง ์๊ณ , ๋
ธ์ถ์ด ์ฌ๋์ด ๋ด์ผ ํ๋ ์๋๋ก ํ์ ๋๋ค. ๋์ ๋ณดํธ๋ฅผ ๊ฑธ๊ณ ํด์ ๊ฐ ์ค์ง ์์ผ๋ฉด(์น๊ฐ ์ดํ, ํ๋ก์ธ์ค ์ข
๋ฃ) ์ข์์ด ๊ฐ์ฐจ ํค ๋ง๋ฃ๊น์ง ๋จ๋๋ค. ๊ณ ์
R:field ์ ๋ฆฌ๋ #274๋ค.
- ๋งฅ๋ฝ: ์งํ ์ค์ธ ์๋๋ฅผ Toss์ ์กฐํํด ๋ง์ถ๋ ํ๋จ์ ์น๊ฐ์ ์ฌ์์ฒญ๊ณผ ๋ณต๊ตฌ Worker๊ฐ ํจ๊ป ์ด๋ค. ์ด ํ๋จ์ด ์น์ธ ์์ ๋จ๊ณ์ ๋ค์ด ์์ผ๋ฉด ๋ณต๊ตฌ Worker๋ ์ฌ์ฌ์ฉํ ์ ์๋ค. ์์ ๋จ๊ณ๋ ์์ฒญ๊ณผ ํ์ ๋ฒํธ๋ฅผ ๋ฐ์ ๊ฒฐ๊ณผ๋ฅผ ์์ธ๋ก ๋๋ ค์ฃผ๋ ๊ตฌ์กฐ๋ค.
- ๊ฒฐ์ : ์กฐํ์ ํ์ ์
PaymentApprovalReconciler๋ก ๋ถ๋ฆฌํ๋ค. ์ํ๋ฅผ ๋ฐ๊พธ์ง ์๊ณReconcileDecision์ฌ์ฏ ๊ฐ์ง ์ค ํ๋๋ฅผ ๋๋ ค์ค๋ค. ์น๊ฐ ๊ฒฝ๋ก๋ ํ์ ์ ์๋ ์ํ ๊ธฐ๋ก๊ณผ HTTP ์๋ต์ผ๋ก ๋ฐ๊พธ๊ณ , ๋ณต๊ตฌ Worker๋ ์ํ ๊ธฐ๋ก์ผ๋ก๋ง ๋ฐ๊พผ๋ค. - ๊ฒฐ๊ณผ: ๋ ๊ฒฝ๋ก๊ฐ ํ๋์ ํ์ ์ ๊ณต์ ํ๋ค. ํ์ ๋ณ ๊ฑด์๋
payment.recovery.decision์งํ๋ก ๋ชจ์ธ๋ค.ReconcileDecision์ sealed์ด๋ฏ๋ก ํ์ ์ด ๋๋ฉด ๋ชจ๋ ์๋น์์์ ์ปดํ์ผ์ด ๋ฉ์ถ๊ณ , ์ ํ์ ์ด ๊ธฐ์กด ์๋ต์ผ๋ก ์กฐ์ฉํ ์ฒ๋ฆฌ๋์ง ์๋๋ค.
- ๋งฅ๋ฝ: ๋ณต๊ตฌ Worker๊ฐ ์๋ฌด๋ฆฌ ์ฌ์๋ํด๋ Toss ์๋ต์ ๋ชป ๋ฐ๋ ์๋๊ฐ ์์ ์ ์๋ค. ๋์์ด ์ฌ์๋๋ฅผ ๊ฑธ๋ฉด ๊ทธ ์๋๋ ์์ํ
IN_PROGRESS๋ก ๋จ์ ์ข์๊ณผ ์์์ ๋ถ์ก๋๋ค. - ๊ฒฐ์ :
RecoveryBackoffPolicy์ ์ต๋ ๋๊ธฐ๋ฅผ ๋๊ธฐ๋ฉด ์๋์ผ๋กFAILED๋ฅผ ์ ์ธํ์ง ์๊ณREVIEW_RECOVERY_TIMEOUT์ฌ์ ๋กREVIEW_REQUIRED์ ์ฌ๋ ค ์ฌ๋์ด Toss ์ชฝ์ ์ง์ ํ์ธํ๊ฒ ํ๋ค. - ๊ฒฐ๊ณผ: ์น์ธ๋์ ์๋ ์๋ ์๋๋ฅผ ์๋์ผ๋ก ์คํจ ์ฒ๋ฆฌํด ์ด์ค ์ฒญ๊ตฌ๋ ํ๋ถ ๋๋ฝ์ ๋ง๋ค์ง ์๋๋ค. ๋์ ์๋ ํ์ธ ๋๊ธฐ๊ฐ ๋์ด๋ ์ ์์ด
payment.attempt.review_required_rows๊ฒ์ด์ง๋ก ์ง์ผ๋ณธ๋ค.REVIEW_REQUIRED์๋ ์ฒ๋ฆฌ๋๋ค๋ ํ์์ด ์์ด ์ด ๊ฒ์ด์ง๋ ์ค์ง ์์ผ๋ฏ๋ก, ์๋ฆผ์ ์ต๊ทผ 24์๊ฐ ์์ ๋ฐ๋ ํ๋ง ์ธ๋payment.attempt.review_required_recent_rows์ ๊ฑธ๊ณ ์ ์ฒด ํ ๊ฒ์ด์ง๋ ์ ์ฒด ์ ์ฒด ๊ท๋ชจ๋ฅผ ๋ณด๋ ค๊ณ ๋๋ค. ํด์ ํ์์ #302 ๋ฒ์๋ค.
- ๋งฅ๋ฝ:
attemptId๊ฐpaymentKey์์ ๊ฒฐ์ ๋๋ฏ๋ก, ์คํจ๋ก ๊ธฐ๋กํ๋ฉด ๊ทธpaymentKey๋ก๋ ๋ค์ ์น์ธํ ์ ์๋ค. ๊ทธ๋ฐ๋ฐ ์์ฒญ์ด Toss์ ๊ฐ์ง๋ ์์๋ค๋ฉด ๋ค์ ๋ณด๋ด๋ ์ํ์ด ์๋ค. - ๊ฒฐ์ : ๋ฏธ๋๋ฌ์ด ํ์ ์ธ ์คํจ๋
NOT_SENT๋ก ๊ธฐ๋กํ๊ณ 503์ผ๋ก ์๋ตํ๋ค. ๋ค์ ์ค๋ฉด ๊ฐ์ ์๋๋ฅผ ์งํ ์ค์ผ๋ก ๋๋๋ ค ์น์ธ์ ๋ค์ ๋ณด๋ธ๋ค. - ๊ฒฐ๊ณผ: ์ผ์์ ์ธ ์ฐ๊ฒฐ ์คํจ๋ก ๊ฒฐ์ ์ฐฝ์ ์ฒ์๋ถํฐ ๋ค์ ์ด ํ์๊ฐ ์๋ค. ๋ค์ ๋ณด๋ผ ๋๋ ์์ฝ ์์กด๊ณผ ๊ฒฐ์ ์ํ๋ฅผ ์ ์น์ธ์ฒ๋ผ ๋ค์ ํ์ธํ๋ค.