Support deferred PaymentIntent outcomes - #46
Conversation
There was a problem hiding this comment.
APPROVE
개요
이 PR은 일회성 PaymentIntent에 대해 deferred outcome 설정 기능을 추가합니다.
metadata[billtap_payment_intent_outcome], billtap_outcome, deferred_outcome,
payment_intent_outcome 중 하나를 통해 생성 시점에 outcome을 저장하고, confirm 시점에
적용합니다. 또한 테스트 오케스트레이션을 위한 POST /api/payment_intents/{id}/outcome
엔드포인트를 추가합니다.
주요 변경사항
- Migration 013:
payment_intents테이블에metadata컬럼 추가 - Billing models:
PaymentIntent구조체에Metadata필드 및MetadataPaymentIntentOutcome상수 추가 - Billing service:
CreatePaymentIntent: deferred outcome 유효성 검사 추가ConfirmPaymentIntent: 저장된 deferred outcome 적용 로직SetPaymentIntentOutcome: 새로운 outcome 설정 메서드
- Storage: 모든 PaymentIntent CRUD 작업에 metadata 처리 추가
- API:
- 새로운
/api/payment_intents/{id}/outcome엔드포인트 - PaymentIntent 생성/confirm 시 deferred outcome 처리
- PaymentIntent 응답에 metadata 포함
- 새로운
검증 결과
✅ Billing State Correctness
- Migration 013
- service.go:
CreatePaymentIntent에서 outcome 유효성을 검사하고,ConfirmPaymentIntent에서
paymentIntentConfiguredOutcome를 통해 저장된 outcome을 올바르게 적용합니다. - Timeline 엔트리에 outcome 정보가 포함되어 상태 추적이 가능합니다.
✅ Webhook Reliability
- 기존 webhook 시맨틱을 변경하지 않습니다.
- Timeline 엔트리에 outcome 필드가 추가되지만, 이는 디버깅 정보를 보강하는 것입니다.
- signature, retry, duplicate, delay, out-of-order 처리에 영향이 없습니다.
✅ Safety
- 실제 카드 데이터 또는 production payment credential을 처리하지 않습니다.
- 이 기능은 오직 테스트/sandbox 환경에서의 deterministic outcome 제어용입니다.
- 새로운
/api/payment_intents/{id}/outcome엔드포인트는 docs/COMPATIBILITY.md에서
Billtap-only로 명확히 문서화되어 있습니다. paymentIntentConfiguredOutcome함수는MetadataPaymentIntentOutcome와billtap_outcome
키만 확인하여 중복을 방지합니다.
✅ Scenario/Fixture Contract
- api_test.go:
metadata를 통한 deferred decline과/api/payment_intents/{id}/outcome엔드포인트를 통한
deferred SCA 시나리오를 모두 커버하는 테스트가 추가되었습니다. - storage_test.go:
migration 테스트가 13개 migration으로 업데이트되었고, metadata persistence가 검증됩니다.
✅ UI/API Coherence
- 이 변경은 순수 backend API 변경입니다. UI/UX에 영향이 없습니다.
- stripePaymentIntent 함수에서
metadata를 응답에 포함하도록 업데이트되어 Stripe-like API와 일치합니다.
✅ Documentation
- docs/COMPATIBILITY.md:
Payment intents 행에 deferred outcome 기능이 문서화되었습니다. - specs/000-product/contracts/api.md:
새로운/api/payment_intents/{id}/outcome엔드포인트가 명세되었습니다. - CHANGELOG.md:
변경사항이 기록되었습니다.
✅ Validation
- validation.go:
confirm=true일 때 payment_method가 없지만 deferred outcome이 있는 경우를 올바르게
처리합니다 (hasPaymentIntentDeferredOutcome함수).
제안 사항 (Suggestion)
사소: outcome key 중복 처리
paymentIntentMetadata
함수는 metadata[billtap_payment_intent_outcome], billtap_outcome, deferred_outcome,
payment_intent_outcome 등 여러 키를 허용합니다. firstNonEmptyString으로 우선순위를
두지만, 사용자가 여러 키를 동시에 전달할 경우 혼란스러울 수 있습니다. 그러나 이는 이미
paymentIntentMetadata 함수에서 정규화되므로 기능적 문제는 없습니다. 향후 사용자 가이드에서
권장 키(metadata[billtap_payment_intent_outcome])를 명확히 하면 좋습니다.
결론
Blocking issue가 없습니다. Billing state correctness, webhook reliability, safety,
scenario contract, documentation 관점에서 모두 적절하게 구현되었습니다. 테스트 커버리지가
충가되었고, Stripe-like API와의 일관성이 유지됩니다.
Reviewed by Z.ai GLM via Claude Code Action
Summary
Verification