feat(voucher): 회수/리컨사일 — 환불 방어 (PLD-1470/1471) - #474
Closed
ipdae wants to merge 2 commits into
Closed
Conversation
환불 감지 → 포탈 revoke를 아웃박스(voucher_grant_outbox)를 단일 조율점으로 처리. - voucher_reconcile_task(beat */5분): (A) status 기반 enroll: 환불/무효(REFUNDED_*/INVALID) receipt의 미회수 아웃박스 → REVOKE_PENDING (admin 환불 등) (B) revoke dispatch: REVOKE_PENDING → 포탈 revoke → REVOKED. 행잠금(skip_locked)·행별 격리·재시도 - track_google_refund 훅: void 감지 시 order_id→receipt→enqueue_revoke (google buyer 환불은 receipt.status 미갱신이라 이 훅이 유일 신호원). 알림 흐름과 독립·best-effort - 아웃박스가 단일 조율점: REVOKE_PENDING/REVOKED는 grant enroll(notin_)·dispatch(PENDING)가 스킵 → 환불이 grant보다 먼저 도착해도 발급 선점 차단 - revoke는 유실=미회수(환불 NCG 잔존)이므로 4xx도 드롭 않고 REVOKE_PENDING 유지+경보 - 테스트 14건(revoke 응답분류, enqueue 상태전이, 게이팅) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…470/1471) 🔴#1 훅 배치 UNIQUE 충돌 통째 롤백 → google 환불 회수 영구 유실: enqueue_revoke_for_receipt 생성 경로를 begin_nested(SAVEPOINT)로 행별 격리, 충돌(grant 선점) 시 재조회 후 REVOKE_PENDING 전이 🔴#2 크래시창에 발급 후 FAILED 찍힌 건이 enroll scope 밖 → 회수 누락: status 기반 enroll scope에 FAILED 포함(revoke는 미발급 receipt엔 멱등 no-op이라 무해) 🟡: - transient(5xx/인증/레이트리밋) 스톨은 failed에 안 잡혀 무알림 → attempts>=5 REVOKE_PENDING 백로그 집계해 경보(회수 유실 진행중 가시화) - dispatch order_by(attempts, receipt_id) → 영구 4xx 고-attempts 행이 신규 회수 starve 방지 - enqueue_revoke_by_order_id에 google 스토어 필터 + 다중매치 전건 큐잉(크로스-스토어 오회수 방지) - reconcile 엔진을 grant와 공유(프로세스당 커넥션 풀 이중생성 방지) - Apple buyer 환불 미커버 known-gap 명시(ASSN 처리기 도입 시 연결) 테스트 17건(+FAILED 전이, order_id 다중매치/무매치) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Author
|
복권식(lottery) 리팩터로 대체됨 → #475 로 통합. (이 PR의 리뷰·엔벨로프는 #475에 반영됨) |
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.
요약
환불 감지 → 포탈
revoke호출을 아웃박스(voucher_grant_outbox)를 단일 조율점으로 처리. grant 트리거(#472) 위에 스택.두 신호원 → REVOKE_PENDING
track_google_refund가 void 감지 시order_id→receipt→enqueue_revoke(google 환불은receipt.status를 갱신하지 않으므로 이 훅이 유일 신호원). 알림 흐름과 독립·best-effort.status=REFUNDED_*/INVALID): reconcile의 status 기반 enroll.reconcile beat(
*/5분)REVOKE_PENDINGREVOKE_PENDING→ 포탈 revoke →REVOKED. 행잠금(skip_locked)·행별 격리·재시도아웃박스가 단일 조율점인 이유
REVOKE_PENDING/REVOKED행은 grant enroll(notin_)·dispatch(PENDING만)가 모두 스킵 → 환불이 grant보다 먼저 도착해도 발급이 선점적으로 차단된다(아웃박스를 REVOKE_PENDING로 생성).안전
REVOKE_PENDING유지 + 경보테스트 14건
_post_revoke응답분류,enqueue_revoke_for_receipt상태전이(생성/전이/no-op), 게이팅.