Skip to content

fix: 구독 상태 확인(StoreKit 2)·복원·현지 통화 가격 표시 - #18

Merged
kangddong merged 3 commits into
developfrom
fix/subscription-entitlement
Oct 4, 2026
Merged

kangddong merged 3 commits into
developfrom
fix/subscription-entitlement

Conversation

@kangddong

@kangddong kangddong commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

문제

  1. 만료·환불돼도 프리미엄이 계속 유지됨
    구독 여부를 구매 순간에 UserDefaults에 true로 저장하고, 이후 다시 확인하지 않았습니다. 주간 구독을 한 번만 결제해도 그 기기에서는 프리미엄이 영구 유지됐습니다.
  2. 복원 버튼이 동작하지 않음
    StoreObserver가 SKPaymentQueue에 등록되지 않아, 복원된 결제가 SwiftyStoreKit의 completeTransactions로만 전달됐습니다. 그곳에서는 결제를 finish만 하고 구독 상태를 바꾸지 않습니다. 재설치한 유료 사용자가 프리미엄을 되찾을 방법이 없었습니다.
  3. 구독 화면 가격이 원화 하드코딩 ("₩4,400 /월")
    노출의 90% 이상이 해외인데 모두 원화 가격을 봤고, 실제 결제는 현지 통화로 됩니다.
  4. 결제 실패·취소 시 구독 상태를 false로 덮어썼습니다.

변경

  • StoreKitManager에 StoreKit 2 기반 메서드 추가 (구매는 기존 SwiftyStoreKit 유지)
    • refreshSubscriptionStatus(): Transaction.currentEntitlements에서 검증된 트랜잭션으로 판단. 만료·환불은 제외되고, 해지했어도 결제 기간이 남아 있으면 유지됩니다
    • observeTransactionUpdates(): 앱 시작 시 Transaction.updates를 구독해 갱신·환불을 반영
    • restoreSubscription(): AppStore.sync() 후 재계산
    • displayPrices(): Product.displayPrice로 현지 통화 가격 조회
  • 호출 위치: 앱 시작(AppDelegate), 앱 활성화(sceneDidBecomeActive), 복원 버튼(SettingViewController)
  • 구독 화면에 현지 통화 가격 + 기간 표시 (/week, /주, /週 등 3개 언어)
  • 복원할 구독이 없으면 "복원할 구독이 없습니다" 알림 표시
  • 사용하지 않던 StoreObserver.swift 삭제 (프로젝트 참조 4줄 포함)
  • CLAUDE.md, ARCHITECTURE.md IAP 설명 갱신

서버 검증은 넣지 않았습니다. 유료 기능(백그라운드 알림)이 기기 안에서만 동작해서, 서버 검증이 보안상 추가로 막는 게 없습니다.

검증

  • 시뮬레이터 빌드 BUILD SUCCEEDED (Xcode 27.0), develop 머지 후 재확인. CI 통과
  • Controllers/AchaCharge.storekit(ASC 동기화)를 추가하고 scheme Run에 연결함. Xcode에서 실행하면 실제 결제 없이 테스트할 수 있음

iPhone 17 시뮬레이터(iOS 27.0, 미국 스토어 설정)에서 실제로 실행해 확인한 결과:

시나리오 결과
구독 없이 복원 ✅ "복원할 구독이 없습니다."
구독 화면 가격 ✅ $0.99 /주, $2.99 /월 (통화는 스토어, 기간 문구는 기기 언어)
월간 구매 ✅ 저장값 isSubscribed = true, 프리미엄을 다시 누르면 "이미 구독하셨습니다 !"
저장값 삭제로 흉내 낸 재설치 (저장값만 false로 지운 뒤 백그라운드 → 포그라운드) ✅ 같은 프로세스에서 복원 버튼 없이 true로 돌아옴. 실제 앱 삭제가 아님
실제 앱 삭제 → 재설치 → 복원 (iPhone 12 Pro 실기기, 샌드박스 계정, 개발 서명 빌드) ✅ 샌드박스 구독 → 앱 삭제(앱 데이터 포함) → 새로 설치 → 구매 복원 → "구매가 완료되었습니다."
실제 앱 삭제 → 재설치 → 자동 인식 (같은 조건) ✅ 복원 버튼을 누르지 않고 설정 › 프리미엄 → "이미 구독하셨습니다 !". 앱 활성화 시 currentEntitlements가 Apple 계정의 구독을 그대로 읽음
구독 있을 때 복원 (저장값 false에서 복원 버튼) ✅ "구매가 완료되었습니다.", 저장값 true
환불 → 프리미엄 해제 (Xcode Manage Transactions에서 Refund) ✅ 앱을 다시 열지 않은 상태(같은 프로세스)에서 저장값이 false로 바뀜. 프리미엄을 누르면 결제 화면이 다시 뜸
환불 후 복원 ✅ "복원할 구독이 없습니다.", 저장값 false 유지 (예전 StoreKit 1 복원은 환불된 구매도 되살렸음)

참고: Xcode 테스트 환경에서의 재설치 시도 (실기기 샌드박스 테스트로 대체)

  • 1차 (Xcode 테스트 환경): 주간 구매 → 저장값이 true가 아니라 false로 나옴 → 앱 삭제 → 재빌드 → false, 복원 시 "복원할 구독이 없습니다". 주간 구매가 환불 직후에 이뤄져서 실제로 활성 상태였는지 불확실함. 이어진 월간 구매 창이 업그레이드가 아니라 "Starting today"로 표시됨
  • 2차: 월간 구매 → true (백그라운드 → 포그라운드 후에도 유지) → 앱 삭제 → 재빌드 → false. 그런데 복원 시 Xcode 가상 로그인이 아닌 실제 Apple 계정 로그인 창이 떠서, 이 실행은 StoreKit 테스트 환경이 아니었음 → 결과 무효 (로그인은 취소, 계정 정보 입력 안 함)
  • Xcode 로컬 테스트 결제는 Apple 계정에 저장되지 않아서, 앱 삭제 후에도 남는지는 Xcode 고유 동작에 달려 있음. 실제 출시 환경과 같은 조건은 TestFlight 또는 샌드박스 계정에서 확인해야 함
  • 함께 확인할 것: 1차의 "구매 직후 false"가 버그(구매 성공 후 재판정이 덮어쓰는 경합)인지. 2차 구매에서는 재현되지 않음 (구독 후 약 2.4초에 true, 이후 유지)

범위 밖

  • 구매까지 StoreKit 2로 옮기는 것(SwiftyStoreKit 제거)은 하지 않았습니다. 구매는 지금도 동작하고, 구독 상태 판단과 분리돼 있어 급하지 않습니다
  • 연간 가격 인하, 무료 체험은 ASC 설정 작업이라 이 PR에 넣지 않았습니다

🤖 Generated with Claude Code

- 구독 여부를 구매 시 저장한 값 대신 Transaction.currentEntitlements로 판단
  (앱 활성화 시, Transaction.updates 수신 시 갱신). 만료·환불 시 프리미엄 해제
- 복원 버튼을 AppStore.sync() 기반으로 교체. 기존 StoreObserver는 결제 큐에
  등록되지 않아 복원이 동작하지 않았으므로 삭제
- 구독 화면 가격을 하드코딩된 원화 대신 Product.displayPrice(현지 통화)로 표시
- 결제 실패·취소 시 구독 상태를 false로 덮어쓰던 코드 제거
- CLAUDE.md, ARCHITECTURE.md IAP 설명 갱신

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kangddong
kangddong changed the base branch from chore/1.1.0-release-prep to develop October 3, 2026 20:03
kangddong and others added 2 commits October 4, 2026 05:06
#16(충전 알림 기준값·Live Activity), #17(테스터 UI) 반영.
- SettingViewController: 충전 알림 기준값 기능 유지, StoreObserver 복원 delegate는 삭제
- CLAUDE.md·ARCHITECTURE.md: develop의 새 구조 유지, IAP 결정 #7을 StoreKit 2 판정 기준으로 갱신

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Controllers/AchaCharge.storekit: ASC와 동기화된 StoreKit 설정 (구독 3종)
- Controllers scheme Run에 StoreKit Configuration 연결. Xcode에서 실행할 때
  실제 결제 없이 구매·복원·환불을 테스트할 수 있음 (Archive·Release에는 영향 없음)
- project.pbxproj: 파일 참조 추가, Xcode가 기존 항목 순서를 재정렬함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kangddong
kangddong merged commit cfe75af into develop Oct 4, 2026
1 check passed
@kangddong kangddong mentioned this pull request Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant