BlogRight arrowTrends & insightsRight ArrowStoreKit 2 API 의 새로운 기능과 Apple이 인앱 구매 통합 을 간소화한 방법
BlogRight arrowTrends & insightsRight ArrowStoreKit 2 API 의 새로운 기능과 Apple이 인앱 구매 통합 을 간소화한 방법

StoreKit 2 API 의 새로운 기능과 Apple이 인앱 구매 통합 을 간소화한 방법

StoreKit 2 API 의 새로운 기능과 Apple이 인앱 구매 통합 을 간소화한 방법
Listen to the episode
StoreKit 2 API 의 새로운 기능과 Apple이 인앱 구매 통합 을 간소화한 방법

Apple은최근에 열린 WWDC2021에서새로운 버전의 StoreKit2를선보였습니다.iOS에서구매를 담당하는 프레임워크입니다.인앱구매 (in-apppurchase) 및구독 (subscription)기능이있는 앱 (app)의점유율이 꾸준히 증가하고 있으며,Apple은StoreKit2를출시하여 인앱 구매를 앱에 통합하는 것을 크게간소화했습니다.오늘우리는 서버 측에서 StoreKit2로작업하는 것 즉,App Store Server API를사용하는 법을 알아볼 것입니다.

인증요청

현재API 버전에서는요청을 보내기 위해 공유 암호 (SharedSecret)가필요합니다.이것은App StoreConnect에서얻을 수 있는 고정 암호 문자열입니다.API의새 버전은 요청 인증을 위해 JSONWeb Token (JWT) 표준을사용합니다.  

키생성

가장먼저,요청을승인하는 데 사용되는 개인키를 생성합니다.App Store Connect를열고 Usersand Access 섹션으로이동한 다음 Keys탭으로이동합니다.인앱구매 키 유형을 선택합니다.새키를 다운로드합니다.또한ID가필요한데,App Store Connect API 탭에서찾을 수 있는 IssueID와동일한 페이지에서 복사할 수 있습니다.

Creating a private key to work with StoreKit 2
AppStore Server API 작업을위한 개인 키 생성

토큰생성하기

다음단계는 요청을 승인하는 데 사용할 토큰을 만드는것입니다.이과정은 문서에자세히 설명되어 있으므로,크게신경쓸 필요는 없습니다.다음은파이톤을 위한 기성품 구현의 예입니다.모든새 요청에 대해 새 토큰을 생성하는 것은 의미가없습니다.토큰을생성할 때 기한을 최대 60분으로설정하고,이기간 동안 동일한 토큰을 사용합니다.

import time, uuid
from authlib.jose import jwt

BUNDLE_ID = 'com.adapty.sample_app'
ISSUER_ID = '4336a124-f214-4d40-883b-6db275b5e4aa'
KEY_ID = 'J65UYBDA74'
PRIVATE_KEY = '''
-----BEGIN PRIVATE KEY-----
MIGTAgMGByqGSMBHkAQQgR/fR+3Lkg4...
-----END PRIVATE KEY-----
'''

issue_time = round(time.time())
expiration_time = issue_time + 60 * 60 # 1 hour expiration


header = {
 'alg': 'ES256',
 'kid': KEY_ID,
 'typ': 'JWT'
}

payload = {
 'iss': ISSUER_ID,
 'iat': issue_time,
 'exp': expiration_time,
 'aud': 'appstoreconnect-v1',
 'nonce': str(uuid.uuid4()),
 'bid': BUNDLE_ID
}

token_encoded = jwt.encode(header, payload, PRIVATE_KEY)
token_decoded = token_encoded.decode()

authorization_header = {
 'Authorization': f'Bearer {token_decoded}'
}

서명된거래

새버전의 API에서는모든 거래가 JSONWeb Signature (JWS) 표준으로반환됩니다.이것은점으로 나누어진 세 부분으로 구성된 문자열입니다.

1. Base64 헤더.

2. Base64 거래 페이로드.

3. 거래 서명.

Base64(header) + "." + Base64(payload) + "." + sign(Base64(header) + "." + Base64(payload))

거래헤더

거래가인증되었는지 확인하려면 헤더가 필요합니다.Alg 키는암호화 알고리즘을 포함하고,x5c 키는인증서 체인을 포함합니다.

{
  "kid": "AMP/DEV",
  "alg": "ES256",
  "x5c": [
    "MIIEO...",
    "MIIDK..."
  ]
}

거래페이로드

{
  "transactionId": "1000000831360853",
  "originalTransactionId": "1000000806937552",
  "webOrderLineItemId": "1000000063561721",
  "bundleId": "com.adapty.sample_app",
  "productId": "basic_subscription_1_month",
  "subscriptionGroupIdentifier": "27636320",
  "purchaseDate": 1624446341000,
  "originalPurchaseDate": 1619686337000,
  "expiresDate": 1624446641000,
  "quantity": 1,
  "type": "Auto-Renewable Subscription",
  "appAccountToken": "fd12746f-2d3a-46c8-bff8-55b75ed06aca",
  "inAppOwnershipType": "PURCHASED",
  "signedDate": 1624446484882,
  "offerType": 2,
  "offerIdentifier": "basic_subscription_1_month.pay_as_you_go.3_months"
}

Apple은거래 형식을 변경 및 확장했습니다.지금은그들과 함께 일하는 것이 더 편리하다고 생각됩니다.새형식에 대한 자세한 내용은 문서에나와있습니다.아래에서가장 중요한 변경 사항에 대해 설명하겠습니다.

  • Apple은 appAccountToken 필드를 추가했는데, 시스템의 사용자 Id가 포함되어 있습니다. 이 ID는 UUID 형식이어야 하며, 구매가 초기화될 때 모바일 앱에서 설정됩니다. 설정된 경우, 이 체인의 모든 거래 (갱신, 결제 문제 등 (billing issue))에서 반환되므로, 어떤 사용자가 구매했는지 쉽게 파악할 수 있습니다.  
  • Apple은 또한 사용된 할인이 있다면, 그에 대한 정보가 포함된 offerType 및 offerIdentifier 필드를 추가했습니다. 다음은 offerType 필드의 값입니다.
  • 1 — 출시 할인 (intro offer) (활성 또는 만료된 구독이 없는 사용자만 사용 가능)
  • 2 — 프로모션 할인 (promo offer) (현재 및 만료된 구독에만 사용 가능)
  • 3 — 할인 코드

프로모션할인 또는 할인 코드가 사용된 경우,offerIdentifier 키에사용된 할인의 ID가포함됩니다.과거에는서버 측에서 할인 사용을 추적하는 것이 불가능하여분석을 악화시켰습니다.이제분석에 할인 코드를 사용할 수 있습니다.

  • Apple은 inAppOwnershipType 필드를 추가했는데, 이는 사용자가 제품을 구입했는지 또는 가족 구독 덕분에 제품에 액세스했는지 여부를 이해하는 데 도움을 줍니다. 가능한 값:
  • PURCHASED
  • FAMILY_SHARED
  • 또 다른 새로운 필드인 type에는 거래 유형이 포함됩니다. 가능한 값:
  • 자동 갱신 구독
  • 비소모품
  • 소모품
  • 비갱신 구독
  • Cancellation_date 및 cancellation_reason 필드는 이제 revocationDate 및 revocationReason이라는 새 이름을 갖습니다. 참고로, 환불로 인한 구독 철회 (subscription revocation) 사유와 날짜가 포함되어 있으므로, 새 이름이 더 논리적으로 보입니다.
  • 모든 키는 camelCase 형식으로 반환됩니다 (모든 App Store Server API 요청과 마찬가지입니다).
  • 모든 날짜는 밀리초 단위의 Unix 타임스탬프 형식으로 표시됩니다.  

사용자구독 상태

현재사용자의 구독상태를확인하려면,GET 요청을https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/{originalTransactionId}로보냅니다.여기서{originalTransactionId}는사용자의 거래 체인 ID입니다.응답으로모든 구독 그룹에 대한 상태가 포함된 거래를 받게됩니다.  

{
  "environment": "Sandbox",
  "bundleId": "com.adapty.sample_app",
  "data": [
    {
      "subscriptionGroupIdentifier": "39636320",
      "lastTransactions": [
        {
          "originalTransactionId": "1000000819078552",
          "status": 2,
          "signedTransactionInfo": "eyJraWQiOi...",
          "signedRenewalInfo": "eyJraWQiOi..."
        }
      ]
    }
  ]
}

상태키는 현재 구독 상태를 표시하며,이를기반으로 사용자에게 앱의 유료 기능에 대한 접근권한을 제공할지 여부를 결정할 수 있습니다.가능한값:

  • 1 — 구독이 활성 상태이며, 사용자는 유료 기능에 접근할 수 있어야 합니다.
  • 2 — 구독이 만료되었습니다. 사용자는 유료 기능에 접근할 수 없어야 합니다.  
  • 3 — 구독 상태가 결제 재시도 (Billing Retry)입니다. 즉, 사용자가 구독을 취소하지 않았지만 결제에 문제가 있음을 의미합니다. Apple은 60일 동안 카드 청구를 시도합니다. 사용자는 유료 기능에 접근할 수 없어야 합니다.  
  • 4  — 구독 상태는 유예 기간 (Grace Period)이며, 이는 사용자가 구독을 취소하지 않았지만 결제 문제가 발생했음을 의미합니다. App Store Connect에서 유예 기간이 설정되어 있으므로 사용자는 유료 기능에 접근할 수 있어야 합니다.    
  • 5 — 환불로 인해 구독이 취소되었습니다. 사용자는 유료 기능에 접근할 수 없어야 합니다.. 

SignedTransactionInfo키는체인의 마지막 거래에 대한 정보를 포함합니다.위에서형식에 대한 세부 정보를 찾을 수 있습니다.

Start for free

Integrate in-app subscriptions in your Android app in 30 minutes with all side cases

Start for free

구독갱신 (subscriptionrenewal)에대한 정보  

SignedRenewalInfo키에는구독갱신에대한 정보가 포함되어 있습니다.

{
  "expirationIntent": 1,
  "originalTransactionId": "1000000819078552",
  "autoRenewProductId": "basic_subscription_1_month",
  "productId": "basic_subscription_1_month",
  "autoRenewStatus": 0,
  "isInBillingRetryPeriod": false,
  "signedDate": 1624520884048
}

이정보를 통해 다음 지불 기간 동안 구독이 어떻게 되는지이해할 수 있습니다.예를들어,사용자가자동 갱신을 취소한 것을 보게 되면 다른 구독 플랜으로전환하도록 제안하거나 할인을 제공할 수 있습니다.서버알림 (servernotifications)을통해 이러한 이벤트 (events)를추적하는 것이 편리합니다.이에대해서는 곧 알려 드리겠습니다.

사용자의거래 내역

사용자의거래내역을얻으려면,GET 요청을https://api.storekit.itunes.apple.com/inApps/v1/history/{originalTransactionId}로보냅니다.여기서{originalTransactionId}는사용자의 거래 체인 ID입니다.응답으로시간별로 정렬된 거래 어레이를 얻습니다.    

{
	"revision": "1625872984000_1000000212854038",
	"bundleId": "com.adapty.sample_app",
	"environment": "Sandbox",
	"hasMore": true,
	"signedTransactions": [
		"eyJraWQiOiJ...",
		"joiRVMyNeyX...",
		"5MnkvOTlOZl...",
		...
	]
}

요청에는20개이하의 거래만 포함할 수 있습니다.사용자가더 많이 갖고 있다면,hasMore 플래그의값은 true가될 것입니다.다음거래 페이지가 필요하면 수정 GET매개변수가포함된 요청을 다시 보내십시오.동일한키의 값이 포함됩니다.

서버거래 알림

서버알림은 신규 구매,갱신,결제문제 등에 대한 정보를 얻는 데 도움이 됩니다.이를통해 보다 정확한 분석을 구축하고,구독자상태 관리를 간소화할 수 있습니다.  

기존서버 알림 (V1)은대부분의 문제를 해결할 수 있지만 가끔 불편합니다.주로사용자의 한 동작에 대해 여러 알림을 받는 상황에관한 것입니다.예를들면,이제사용자가 구독을 업그레이드할 때 Apple은두 가지 알림을 보냅니다.DID_CHANGE_RENEWAL_STATUS 및INTERACTIVE_RENEWAL.현재는이 케이스를 처리하려면,어떻게든상태를 저장하고 두 번째 알림이 전송되었는지 확인해야합니다.새로운버전의 서버 알림 (V2)에서는사용자의 하나의 작업에 대해 하나의 알림만 있습니다.이것은훨씬 더 편리합니다.  

두번째 버전의 서버 알림에는 새로운 이벤트인OFFER_REDEEMED,EXPIRED 및GRACE_PERIOD_EXPIRED가있습니다.구독자상태를 훨씬 쉽게 관리할 수 있습니다.SUBSCRIBED 및PRICE_INCREASE이벤트는첫 번째 버전에서 개선된 이벤트입니다.

Transaction notifications in StoreKit 2 feature new events

알림유형

알림에는이제 유형이 있으므로,사용자의모든 작업에 대한 알림 하나만으로 발생한 상황을이해할 수 있습니다.

Notifications in StoreKit 2 have types

알림유형

{
  "notificationType": "SUBSCRIBED",
  "subtype": "INITIAL_BUY",
  "version": 2,
  "data": {
    "environment": "Sandbox",
    "bundleId": "com.adapty.sample_app",
    "appAppleId": 739104078,
    "bundleVersion": 1,
    "signedTransactionInfo": "eyJraWQiOi...",
    "signedRenewalInfo": "eyJraWQiOi..."
  }
}

서버알림에는 앞에서 설명한 JWS형식의거래 및 갱신에 대한 정보가 포함됩니다.

샌드박스(Sandbox)환경에서작업하기

구매를테스트하려면 샌드박스 환경의 URL인https://api.storekit-sandbox.itunes.apple.com을사용해야 합니다.

새버전의 서버 알림은 아직 테스트할 수 없습니다.사용가능하게 되면 프로덕션 및 샌드박스 알림에 대해 다른URL을지정할 수 있습니다.샌드박스의경우 V2를선택하고 테스트용 프로덕션의 경우 V1을선택할 수 있습니다.

또한App StoreConnect는이제 다음을 허용합니다.

  • 샌드박스 사용자의 구매 내역을 지웁니다. 더 이상 새 계정을 만들 필요가 없다는 뜻입니다.  
  • 샌드박스 사용자의 스토어 국가를 변경합니다.
  • 샌드박스 구독 갱신 기간을 변경합니다. 예를 들면, 5분이 아닌 1시간 동안 지속되는 월간 구매를 할 수 있습니다. 

결론

Apple은서버 측에서 인앱 구매 및 구독 작업을 크게 개선했습니다.제가볼 때 가장 유용한 새 기능은 다음과 같습니다.

  • 본격적인 프로모션 할인 및 할인 코드 지원,  
  • 더 간단하고 유용한 서버 알림,  
  • 간단한 API 호출로 현재 구독 상태를 알 수 있는 기회,
  • 사용자의 샌드박스 구매 내역 삭제.

새API로전환하는 것은 어렵지 않습니다.모든영수증의 originalTransactionId만있으면 됩니다.이미가지고 있을 가능성이 높죠.  

어쨌든구독을 모바일 앱에 통합할 때 가장 어려운 부분은분석 시스템을 구축하고 경제적인 문제를 최적화하는것입니다.Adapty는이러한 문제를 잘 해결할 수 있습니다.

  • 내장된 분석을 통해 주요 앱의 수치를 빠르게 파악할 수 있습니다.
  • 코호트 분석 (cohort analysis)은 수익에 문제가 있는지 이해하는 데 도움이 됩니다.
  • A/B 테스트 (A/B testing)는 앱의 수익성을 높입니다.  
  • 외부 시스템과의 통합을 통해 어트리뷰션 (attribution) 및 제품 분석 서비스에 거래를 보낼 수 있습니다.  
  • 프로모션 캠페인은 고객 손실을 줄입니다.  
  • 오픈 소스 SDK를 사용하면 몇 시간 내에 구독을 앱에 통합할 수 있습니다.

이러한기능에 대해 자세히 알아보셔서,앱에더 빨리 구독을 구현하고 더 빨리 앱에서 수익을창출하세요.

Further reading

What's wrong with my in-app subscription on iOS?
What's wrong with my in-app subscription on iOS?
November 18, 2019
8 min read
Ultimate guide on Apple Subscription Offers. Part 2
Ultimate guide on Apple Subscription Offers. Part 2
March 17, 2021
8 min read
How to get paid subscribers for your app: The top reasons mobile app users go from freemium to premium
How to get paid subscribers for your app: The top reasons mobile app users go from freemium to premium
September 15, 2022
15 min read