# Idempotency QA Checklist (연장/해지)

## 목적
- 중복 제출/동시 요청 시 **추가 결제/추가 환불/중복 처리**가 발생하지 않는지 확인한다.
- 대상 엔드포인트:
  - `shop/orderinquiryExtendPeriod.php`
  - `shop/orderinquiryrefund.php`

## 사전 준비
- 테스트 계정 1개 (포인트 충분)
- 테스트 주문 1개 (입금 또는 완료 상태)
- 브라우저 개발자도구(Network) 사용 가능
- (권장) Throttling 설정 가능 환경

---

## 시나리오 A: 정상 1회 제출
### 절차
1. 주문상세에서 연장 또는 해지 팝업 열기
2. 유효한 값 입력 후 1회 제출

### 기대 결과
- 성공 팝업 1회 노출
- DB/화면 반영 1회만 발생
  - 연장: 종료일/포인트차감/연장이력 1회
  - 해지: 환불포인트/환불합계/환불일시 1회

### 증빙
- 성공 팝업 스크린샷
- Network 요청 1건 캡처

---

## 시나리오 B: 동일 요청 재전송 (`done` 분기)
### 절차
1. 시나리오 A에서 성공한 POST 요청 선택
2. 동일 요청을 그대로 재전송 (body, cookie 유지)

### 기대 결과
- "이미 처리된 ... 요청입니다" 안내
- 금액/이력 데이터 **추가 변경 없음**

### 증빙
- 재전송 응답 스크린샷
- 재전송 전/후 주요 값 비교 캡처

---

## 시나리오 C: 처리중 중복 (`pending` 분기)
### 절차
1. 네트워크 Throttling(예: Slow 3G) 적용
2. 첫 요청 전송 후 완료 전, 같은 요청 즉시 재전송

### 기대 결과
- 두 번째 요청은 `ERR-IDEMPENDING` 또는 동등 메시지
- 첫 번째 요청만 정상 반영

### 증빙
- 동시에 날아간 2개 요청 타임라인 캡처
- 두 번째 요청 오류 메시지 캡처

---

## 시나리오 D: 키 위변조 (`ERR-IDEM` 분기)
### 절차
1. 재전송 시 `idempotency_key`를 잘못된 값(길이/형식 오류)으로 변경
2. 요청 전송

### 기대 결과
- `ERR-IDEM` 오류
- 데이터 반영 없음

### 증빙
- 수정된 요청 payload 캡처
- 오류 팝업 캡처

---

## 시나리오 E: 실패 후 재시도 (clear 동작 검증)
### 절차
1. 의도적으로 실패 케이스 유발 (예: 포인트 부족 상태 등)
2. 같은 화면 흐름에서 조건 복구 후 재시도

### 기대 결과
- 실패 시 트랜잭션 롤백
- 세션 pending 잠금이 해제되어 정상 재시도 가능

### 증빙
- 실패 응답 + 재시도 성공 응답 캡처

---

## 데이터 무결성 점검 항목
- 연장
  - `g5_shop_item_table.it_spec_to_datetime` 변경 1회
  - `g5_shop_cart_table` 연장 누적 필드 변경 1회
  - `g5_shop_order_table.od_mod_history` 로그 1회 증가
  - 포인트 차감 1회
- 해지
  - `g5_shop_cart_table.real_refund_amount` 반영 1회
  - `g5_shop_order_table.od_refund_price` 정확히 합산
  - 포인트 환불 1회

---

## 결과 기록 템플릿
| 시나리오 | 결과(PASS/FAIL) | 응답 메시지 | 데이터 변경 확인 | 증빙 링크/파일 | 비고 |
|---|---|---|---|---|---|
| A 정상 1회 |  |  |  |  |  |
| B 동일 재전송(done) |  |  |  |  |  |
| C 처리중 중복(pending) |  |  |  |  |  |
| D 키 위변조(ERR-IDEM) |  |  |  |  |  |
| E 실패 후 재시도(clear) |  |  |  |  |  |

---

## 완료 기준(Release Gate)
- A~E 전부 PASS
- 중복 제출로 인한 금전성 데이터(결제/환불) 중복 반영 0건
- 연장/해지 모두 동일 수준으로 동작

## 참고 코드 포인트
- 폼 키 주입: `skin/apms/order/basic/orderinquiryview.skin.php`
- 해지 상태전이: `shop/orderinquiryrefund.php`
- 연장 상태전이: `shop/orderinquiryExtendPeriod.php`

---

# Remote/VPN 카탈로그 분리 E2E QA Checklist

## 목적
- 주문상세에서 프로그램 카탈로그가 `program_type` 기준(`remote`/`vpn`)으로 정확히 분리 적용되는지 확인한다.
- 공식 사이트 URL 및 OS별 다운로드 URL 노출이 관리자 설정과 일치하는지 검증한다.

## 사전 준비
- 관리자 페이지: `adm/remote_program_catalog.php`
- 테스트 주문 2건 이상
  - Remote 성격 상품 1건
  - VPN 성격 상품 1건
- 프로그램별 테스트 URL(서로 구분 가능한 URL 문자열) 준비

---

## 시나리오 R1: VPN 상품은 VPN 카탈로그만 참조
### 절차
1. 관리자에서 `program_type=vpn` 항목(예: OpenVPN Connect)의 `official_url`, `download_android`, `download_ios` 저장
2. VPN 주문상세 진입 후 원격접속 카드 확인

### 기대 결과
- 공식 사이트 버튼 URL이 VPN 카탈로그 값과 일치
- 다운로드 버튼이 VPN 설정값(모바일 우선 포함)으로 노출
- remote 타입 값이 섞여 나오지 않음

### PASS 기준
- VPN 주문상세에서 VPN 타입 URL만 노출

---

## 시나리오 R2: Remote 상품은 Remote 카탈로그만 참조
### 절차
1. 관리자에서 `program_type=remote` 항목(예: RustDesk)의 `official_url`, `download_windows`, `download_macos` 저장
2. Remote/RPC 주문상세 진입 후 원격접속 카드 확인

### 기대 결과
- 공식 사이트 버튼 URL이 Remote 카탈로그 값과 일치
- PC 환경에서 Windows/macOS 버튼이 우선 노출
- VPN 타입 값이 섞여 나오지 않음

### PASS 기준
- Remote 주문상세에서 Remote 타입 URL만 노출

---

## 시나리오 R3: 유사/중복 이름 충돌 시 타입 분리 유지
### 절차
1. `remote`와 `vpn`에 유사 alias를 각각 등록(예: `wire`, `wireguard`)
2. VPN 주문/Remote 주문 각각 상세 진입

### 기대 결과
- VPN 주문은 VPN 타입 결과만 매칭
- Remote 주문은 Remote 타입 결과만 매칭
- 교차 오매칭 없음

### PASS 기준
- 주문 타입에 맞는 카탈로그 결과만 표시

---

## 시나리오 R4: DB 미매칭/비활성 시 fallback 동작
### 절차
1. 대상 항목을 비활성(`is_active=0`) 처리하거나 제거
2. 주문상세 새로고침

### 기대 결과
- DB 미매칭 시 기본 카탈로그 또는 검색 fallback으로 동작
- 버튼/레이아웃 깨짐 없이 정상 표시

### PASS 기준
- 빈/깨진 UI 없이 fallback URL 정책대로 동작

---

## 시나리오 R5: 상태(정상/만료/환불)별 보안/표시 일관성
### 절차
1. 정상 이용중, 계약만료, 중도해지 환불 주문 각각 상세 확인
2. 원격접속 카드의 상태 배지, 마스킹, URL 버튼 노출 점검

### 기대 결과
- 환불 주문: ID/PW/IP 마스킹 유지
- 만료 주문: 만료 상태/문구 정확
- 공식 사이트/다운로드 버튼은 정책대로 노출(민감정보 비노출)

### PASS 기준
- 상태별 보안 표시 + 카탈로그 버튼 동시 정상

---

## 결과 기록 템플릿 (Remote/VPN)
| 시나리오 | 결과(PASS/FAIL) | 주문유형 | 공식URL 확인 | 다운로드URL 확인 | 증빙 링크/파일 | 비고 |
|---|---|---|---|---|---|---|
| R1 VPN 참조 |  | VPN |  |  |  |  |
| R2 Remote 참조 |  | Remote |  |  |  |  |
| R3 이름 충돌 분리 |  | VPN/Remote |  |  |  |  |
| R4 fallback 검증 |  | VPN/Remote |  |  |  |  |
| R5 상태별 일관성 |  | 전체 |  |  |  |  |

## 완료 기준(Release Gate)
- R1~R5 전부 PASS
- 타입 혼선으로 인한 잘못된 URL 노출 0건
- 환불/만료 상태에서 민감정보 노출 0건

---

## QA 실행 운영안 (배포용)

### 1) 역할 분담
- QA 리드(1명): 전체 진행/판정/릴리스 게이트 최종 승인
- 테스터 A(1명): Idempotency 시나리오 A~E 실행
- 테스터 B(1명): Remote/VPN 시나리오 R1~R5 실행
- 개발 확인자(1명): FAIL 항목 재현 확인 및 수정 반영 여부 코멘트

### 2) 권장 타임라인 (당일 기준)
- T0~T0+20분: 데이터 준비/주문ID 확정/테스트 URL 세팅
- T0+20~T0+70분: 1차 실행 (A~E, R1~R5)
- T0+70~T0+100분: FAIL 재현/수정 확인
- T0+100~T0+120분: 재검증 및 게이트 판정

### 3) 실행 순서 (권장)
1. Idempotency A → B → C → D → E
2. Remote/VPN R1 → R2 → R3 → R4 → R5
3. FAIL 항목 우선순위
  - P0: 금전성 중복 처리, 민감정보 노출
  - P1: 타입 혼선으로 잘못된 URL 노출
  - P2: 문구/레이아웃 불일치

### 4) 주문ID/테스트 데이터 배정표 (복붙용)
| 구분 | 주문ID | 상품유형 | 프로그램명 | 상태(정상/만료/환불) | 담당자 | 실행시각 | 결과 |
|---|---|---|---|---|---|---|---|
| TC-REMOTE-01 |  | Remote |  | 정상 |  |  |  |
| TC-REMOTE-02 |  | Remote |  | 만료 |  |  |  |
| TC-VPN-01 |  | VPN |  | 정상 |  |  |  |
| TC-VPN-02 |  | VPN |  | 환불 |  |  |  |
| TC-IDEM-01 |  | 공통 |  | 정상 |  |  |  |

### 5) 관리자 카탈로그 테스트값 기록표
| program_type | program_key | official_url(테스트) | windows | macos | linux | android | ios |
|---|---|---|---|---|---|---|---|
| remote |  |  |  |  |  |  |  |
| vpn |  |  |  |  |  |  |  |

### 6) 증빙 파일명 규칙
- 스크린샷: `YYYYMMDD_{시나리오}_{주문ID}_{PASS/FAIL}.png`
- 네트워크 캡처: `YYYYMMDD_{시나리오}_{주문ID}_network.har`
- 예시: `20260303_R3_OD20260303A_FAIL.png`

### 7) FAIL 보고 템플릿
| 시나리오 | 심각도(P0/P1/P2) | 재현절차 요약 | 실제결과 | 기대결과 | 관련 주문ID | 담당(개발) | 상태 |
|---|---|---|---|---|---|---|---|
|  |  |  |  |  |  |  | Open |

### 8) 최종 Go/No-Go 체크
- [ ] A~E PASS
- [ ] R1~R5 PASS
- [ ] P0 이슈 0건
- [ ] P1 이슈 0건(또는 승인된 예외만 존재)
- [ ] 증빙 파일/링크 취합 완료
- [ ] QA 리드 최종 승인 코멘트 등록

---

## 관련 Runbook
- VPN 사용자 표시 이슈(만료/과금): `ikev2_vpn_users_ui_hotfix_runbook.md`
- Shop 동기화 헬스체크(코드 기반 대응): `ikev2_shop_sync_healthcheck_runbook.md`
