1. 목적
Apple App Store와 Google Play Store를 통한 인앱 결제(IAP) 연동, 결제 검증, 환불 처리에 대한 기술 정책을 정의합니다.
2. Apple App Store 연동
2-1. AppStoreTransaction 구조
Apple 거래 정보를 캐싱하여 멱등성 있는 검증을 제공합니다.
| 필드 | 설명 |
|---|
transaction_id | 거래 고유 ID (unique) |
original_transaction_id | 원본 거래 ID (갱신 추적용) |
product_id | 상품 ID |
subscription_type | 구독 유형 |
purchase_date | 구매 일시 |
expires_date | 만료 일시 |
price_amount | 결제 금액 |
price_currency | 통화 코드 |
auto_renew_status | 자동 갱신 상태 |
is_trial_period | 무료 체험 여부 |
is_in_intro_offer_period | 인트로 오퍼 기간 여부 |
ownership_type | 소유 유형: PURCHASED 또는 FAMILY_SHARED |
environment | 환경: Sandbox / Production |
app_account_token | 앱 계정 토큰 (사용자 매핑용) |
signed_transaction_jws | 서명된 거래 JWS |
raw_transaction | 원본 거래 JSON |
raw_renewal | 원본 갱신 JSON |
2-2. Apple Server Notification Webhook
- Endpoint:
POST /api/payments/apple/notifications/
- Apple Server Notifications V2를 수신합니다.
- 구독 갱신, 해지, 환불 등의 이벤트를 실시간으로 처리합니다.
2-3. iOS 클라이언트 결제 흐름
InAppPurchase 플러그인을 통해 상품 정보 조회
- StoreKit 2 기반 결제 처리 (iOS 15 미만은 StoreKit 1으로 폴백)
appAccountToken을 UUID v5로 생성하여 사용자 식별에 활용
- 결제 완료 후 서버에 검증 요청 (
transaction_id, product_id, server_verification_data)
3. Google Play Store 연동
3-1. GooglePlayReceipt 구조
Google Play v2 API 기반 구독 영수증을 관리합니다.
| 필드 | 설명 |
|---|
latest_order_id | 결제 단위 식별자 (unique, 정산 매칭) |
purchase_token | 현재 구매 토큰 |
linked_purchase_token | 토큰 교체(업그레이드/다운그레이드) 추적 |
subscription_state | 구독 상태 |
acknowledgement_state | 확인(ack) 상태 |
start_time | 구독 시작 기준일 |
expiry_time | 만료/다음 갱신 시점 |
price_amount_micros | 가격 (micros 단위, 1,000,000 = 1.00) |
currency_code | 통화 코드 |
region_code | 지역 코드 |
product_id | 상품 ID |
base_plan_id | 기본 플랜 ID |
offer_id | 오퍼 ID |
offer_tags | 프로모/인트로 오퍼 태그 (JSON) |
auto_renew_enabled | 자동 갱신 활성화 여부 |
raw_json | 원문 전체 JSON 스냅샷 (감사, 분쟁 대응) |
3-2. Google Play 구독 상태
| 상태 | 설명 |
|---|
SUBSCRIPTION_STATE_ACTIVE | 활성 |
SUBSCRIPTION_STATE_PENDING | 대기 |
SUBSCRIPTION_STATE_PAUSED | 일시정지 |
SUBSCRIPTION_STATE_IN_GRACE_PERIOD | 유예 기간 |
SUBSCRIPTION_STATE_ON_HOLD | 보류 |
SUBSCRIPTION_STATE_CANCELED | 해지 |
SUBSCRIPTION_STATE_EXPIRED | 만료 |
3-3. Google Play Webhook (RTDN)
- Endpoint:
POST /api/payments/rtdn/pubsub/
- Google Pub/Sub를 통한 Real-Time Developer Notification을 수신합니다.
- 구독 상태 변경, 결제 갱신, 환불 등의 이벤트를 실시간 처리합니다.
3-4. Google Play 관리
- Endpoint:
POST /api/payments/google-play/manage/
- 구독 확인(Acknowledge), 환불, 권한 취소 등의 관리 작업을 수행합니다.
- 미확인 구독(
ACKNOWLEDGEMENT_STATE_PENDING)은 즉시 ack 처리가 필요합니다.
3-5. Android 클라이언트 결제 흐름
InAppPurchase 플러그인을 통해 상품 정보 및 오퍼 조회
basePlanId, offerId, 가격 정보를 서버에 전달
- 결제 완료 후
purchase_token, product_id를 포함하여 서버 검증 요청
4. 결제 검증
4-1. 검증 API
- Endpoint:
POST /api/payments/verify/
- Rate Limit: 30회/분
- iOS와 Android 모두 이 단일 엔드포인트로 검증합니다.
4-2. 검증 파라미터
| 파라미터 | iOS | Android | 설명 |
|---|
platform | ios | android | 플랫폼 식별 |
transaction_id | O | O | 거래 ID |
product_id | O | O | 상품 ID |
purchase_token | - | O | Google Play 구매 토큰 |
base_plan_id | - | O (선택) | 기본 플랜 ID |
offer_id | - | O (선택) | 오퍼 ID |
server_verification_data | O (선택) | - | JWS 서명 데이터 |
receipt_data | O (선택) | - | iOS 영수증 데이터 |
app_account_token | O (선택) | O (선택) | 앱 계정 토큰 |
4-3. PaymentTransaction 구조
결제 거래 기록을 관리합니다.
| 필드 | 설명 |
|---|
type | 거래 유형: subscription / renewal / refund / cancellation |
status | 거래 상태: pending / completed / failed / refunded |
amount | 결제 금액 |
currency | 통화 코드 |
payment_method | 결제 수단 (google_play 등) |
gateway_transaction_id | 외부 거래 ID |
purchase_token | 구매 토큰 |
metadata | 추가 메타데이터 (JSON) |
5. 환불 정책
5-1. 환불 기준
본 앱의 구독 결제는 Apple App Store 또는 Google Play Store를 통해 이루어지며, 각 플랫폼의 환불 정책을 따릅니다.
5-2. iOS 사용자 (Apple App Store)
- 환불은 Apple에서만 진행 가능합니다.
- 앱 운영자는 Apple의 환불 요청을 승인하거나 개입할 수 없습니다.
- 환불 신청: Apple 환불 신청 페이지를 통해 직접 진행
- 구독 취소: 설정 > Apple ID > 구독 관리 > 앱 이름 선택 > 구독 취소
- 취소 시 다음 갱신일부터 결제되지 않으며, 현재 결제 기간까지 서비스는 유지됩니다.
5-3. Android 사용자 (Google Play Store)
| 구분 | 정책 |
|---|
| 결제 후 48시간 이내 | Google Play에서 직접 환불 가능 |
| 48시간 이후 ~ 7일 이내 | 신규 구독자에 한해 고객센터 통해 전액 환불 신청 가능 |
| 7일 이후 | 기술적 문제 발생 시 예외적으로 부분 환불 검토 가능 |
5-4. 장기 구독 환불
- 6개월/12개월 구독의 경우 잔여 기간에 대한 부분 환불 가능
- 환불금액 = 총 결제금액 - (월정액 x 이용 개월수)
- 할인 혜택은 소급 적용하여 재계산
5-5. 환불 처리 기간
- 승인: 영업일 기준 3~5일
- 실제 환불: 영업일 기준 30~60일 (플랫폼 정산 주기에 따름)
5-6. 환불 제한 사항
- 동일 계정 2회 이상 환불 제한
- 환불 후 30일간 재가입 제한
- 프로모션 코드는 환불 시 재사용 불가
- 래퍼럴 코드 혜택은 환불 시 회수
6. 환불 요청 (RefundRequest)
6-1. 데이터 구조
| 필드 | 설명 |
|---|
status | 상태: pending / approved / rejected / completed / failed |
refund_type | 환불 유형: full (전액) / partial (부분) / subscription_cancel (해지 환불) |
reason | 사유: user_request / technical_issue / billing_error / duplicate_purchase / fraud_prevention / other |
reason_description | 상세 사유 (텍스트) |
google_refund_id | Google Play 환불 ID |
google_order_id | Google Play 주문 ID |
purchase_token | 구매 토큰 |
revoke_entitlement | 접근 권한 즉시 회수 여부 (기본 true) |
initiated_by | 환불을 처리한 관리자 |
6-2. 환불 API
| Endpoint | 설명 |
|---|
POST /api/payments/refund-request/ | 환불 요청 생성 |
POST /api/payments/refund-process/ | 환불 처리 (관리자) |
7. 결제 이력
- Endpoint:
GET /api/payments/history/
- 사용자의 전체 결제 거래 이력을 조회합니다.