docs(charge): 충전 ID 응답 계약과 검증 결과를 기록한다

This commit is contained in:
2026-11-03 15:50:53 +09:00
parent a6a4e9069b
commit 23673b4465
2 changed files with 67 additions and 4 deletions
+10 -1
View File
@@ -4,7 +4,7 @@
| 항목 | 내용 |
|---|---|
| 문서 상태 | 구현·테스트·독립 리뷰 완료, 미배포 |
| 문서 상태 | chargeId 후속 확장 포함 구현·검증 완료 |
| 작성일 | 2026-11-03 (KST) |
| 최종 수정일 | 2026-11-03 (KST) |
| 대상 제품 | Sodalive 결제 완료 응답과 최초 유료 충전 판정 |
@@ -42,6 +42,7 @@
| PAY-003 | 확정 | `Charge.isFirstCharged: Boolean? = null`을 nullable로 저장한다. 요청에서 완료로 처음 전환될 때 회원 잠금 아래 한 번만 판정한다. 같은 회원의 다른 charge 중 다음 두 쌍만 유료 이력이다: `(ChargeStatus.CHARGE, PaymentStatus.COMPLETE)`, `(ChargeStatus.CANCEL, PaymentStatus.RETURN)`. 현재 charge는 제외하고 모든 PG/PAYVERSE/APPLE_IAP/GOOGLE_IAP를 포함한다. 결과 true는 다른 유료 이력이 없음을 뜻한다. 날짜와 ID 순서로 첫 충전을 정하지 않는다. 구현 승인 후 nullable 컬럼 추가 및 `COMMENT`를 포함한 SQL 파일을 작성하되, 과거 행은 NULL로 두고 backfill하지 않는다. `Boolean?`의 실제 MySQL 컬럼 타입 호환성은 rollout 전에 확인한다. | P1-T2, P1-T4 |
| PAY-004 | 확정 | 완료 응답에 `isIap: Boolean`을 추가한다. `GOOGLE_IAP`와 `APPLE_IAP`이면 true, `PG`와 `PAYVERSE`이면 false다. 단말 OS와 무관하게 저장된 gateway만 사용한다. `price`, `currencyCode`의 타입·값 원천은 유지한다. | P1-T3, P1-T4 |
| PAY-005 | 확정 | 소유권, 최초 판정·동시성·재시도·환불·webhook, 다섯 HTTP 응답 및 기존 추적 효과를 focused TDD와 통합 테스트로 검증한다. 모든 요구사항은 P1-GATE의 증거와 연결한다. | P1-T1~P1-T4, P1-GATE |
| PAY-006 | 확정 | 완료 응답에 `chargeId: Long`을 추가한다. 다섯 완료 API에서 실제 저장된 `Charge.id`와 일치하는 non-null JSON 정수를 반환하고 동일 charge 재조회·Payverse 웹훅 선완료 뒤에도 같은 ID를 유지한다. DB·SQL·결제 처리는 변경하지 않는다. | P2-T1, P2-GATE |
## 4. API 계약
@@ -54,6 +55,7 @@
"success": true,
"message": null,
"data": {
"chargeId": 123,
"price": 9.99,
"currencyCode": "USD",
"isFirstCharged": true,
@@ -65,6 +67,8 @@
`data.price`는 기존 `BigDecimal`의 JSON number 직렬화다. `currencyCode`는 기존 locale에서 가져오는 통화 문자열이다. `isFirstCharged`와 `isIap`은 non-null JSON boolean이다.
`data.chargeId`는 서버 충전 건의 `Long` ID이며 JSON 정수로 반환한다. 결제사 거래 ID가 아니고 신규 DB 컬럼도 필요 없다. 기존 Google API는 재요청마다 새 charge를 생성하므로 동일 purchase token의 멱등 키로 보장하지 않는다. 현재 완료 data 계약은 기존 네 필드에 chargeId를 더한 다섯 필드다.
### 완료 경로
| Method / path | 완료 결과 | 변경 여부 |
@@ -121,9 +125,12 @@ AppsFlyer는 분석에 필요한 DATA를 논의한 맥락일 뿐이다. 서버
- [x] PAY-003: PG/PAYVERSE/APPLE_IAP/GOOGLE_IAP 완료 이력, `CANCEL + RETURN`, legacy null, 현재 건 제외, `EVENT + COMPLETE`, replay, webhook-first, rollback을 검증했다. 기존 유료 이력 없는 회원의 동시 완료는 정확히 하나만 true, 이력이 있으면 둘 다 false이며 날짜/ID 순서에 의존하지 않는다. `20261103_charge_first_charged_ddl.sql`은 nullable 단일 BIT(1) 컬럼과 COMMENT만 포함하고 backfill하지 않는다. H2 및 정적 Hibernate 매핑 검증이며 운영 MySQL 적용·검증은 하지 않았다.
- [x] PAY-004: 네 gateway에 대해 `isIap` 값이 각각 false/false/true/true이고 OS에 영향받지 않는다. 기존 price 및 currency sourcing, null -> false 응답 규칙을 유지한다.
- [x] PAY-005: 기존 charge coupon test와 관리자 환불 test를 포함한 직접 영향 회귀를 통과하고 모든 변경 요구가 P1-GATE에 추적된다.
- [x] PAY-006: 실제 HTTP에서 다섯 완료 API의 chargeId가 저장된 Charge.id와 일치한다. 동일 charge 재조회·웹훅 후 verify는 같은 ID이며 기존 금액·통화·첫 결제·IAP 및 생성/webhook 응답을 보존한다.
완료 근거: 전체 339 suites / 3,112 tests, failures=0, errors=0, skipped=0. `test build ktlintCheck` exit 0 및 독립 리뷰 PASS. 실제 HTTP 56회 증거와 환경 한계는 [plan-task.md](plan-task.md)의 최종 Gate 기록에 있다. 운영 배포 승인을 뜻하지 않는다.
chargeId 후속 검증: focused 188건 및 최신 전체 341 suites / 3,128 tests, failures/errors/skipped=0, `test build ktlintCheck` exit 0, 독립 리뷰 PASS. 이전 완료 증거는 보존하며 현재 결과는 plan의 P2 기록을 따른다. Jenkins exit 137 해결이나 운영 배포 검증을 의미하지 않는다.
## 8. 미검증 항목과 Decision Log
| ID | 상태 | 항목 | 처리 또는 재개 조건 |
@@ -142,6 +149,7 @@ AppsFlyer는 분석에 필요한 DATA를 논의한 맥락일 뿐이다. 서버
| 2026-11-03 | DEC-005 | 확정 | DEC-004의 문서 전용 승인 범위는 유지한다. 구현을 별도 승인한 뒤 nullable 컬럼 추가와 `COMMENT`를 담은 SQL 파일을 작성한다. SQL 적용·배포·운영 DB 확인은 별도 운영 승인 대상이며 현재 수행하지 않는다. | PAY-003, P1-T2, OPS-001 |
| 2026-11-03 | DEC-006 | 정정, 사용자 구현 승인 | “문서대로 진행” 및 ULW 요청에 따라 P1-T1부터 구현·격리 테스트·전체 회귀·HTTP QA를 수행한다. SQL 파일 작성은 포함하며 운영 DB 적용·실결제·배포·커밋·푸시는 별도 승인 경계를 유지한다. | PAY-001~PAY-005, P1-GATE |
| 2026-11-03 | DEC-007 | 구현 세부 결정 | 동시 판정의 최신 이력 가시성 확보를 위해 여섯 완료 writer만 READ_COMMITTED로 설정하고, Hibernate MySQL dialect에 맞춰 DDL을 BIT(1)로 작성한다. 추가 temp writer는 범위 밖 관찰로 기록하며 임의 확장하지 않는다. | PAY-003, OPS-001, EXT-003 |
| 2026-11-03 | DEC-008 | 확정, 사용자 후속 구현 승인 | 완료 응답에 기존 Charge.id를 chargeId로 추가한다. PAY-001의 기존 네 필드 완료 증거는 유지하고 현재 계약은 다섯 필드로 확장한다. 별도 DB·SQL·결제·CI 설정 변경과 커밋·푸시는 제외한다. | PAY-006, P2-T1, P2-GATE |
## 9. 계획 추적
@@ -152,3 +160,4 @@ AppsFlyer는 분석에 필요한 DATA를 논의한 맥락일 뿐이다. 서버
| PAY-003 | `ChargeRepositoryTest`, `ChargeServiceIntegrationTest` 이력·동시성·재시도 assertions 및 SQL 산출물의 정적·매핑 정합성 검토 | P1-T2, P1-T4, P1-GATE |
| PAY-004 | `ChargeControllerIntegrationTest` gateway mapping 및 price/currency assertions | P1-T3, P1-T4, P1-GATE |
| PAY-005 | focused, charge/event/admin 영향 회귀 | P1-T1~P1-T4, P1-GATE |
| PAY-006 | `ChargeControllerIntegrationTest`의 저장 ID 일치·재조회·웹훅 후 verify assertions | P2-T1, P2-GATE |