What's new in the StoreKit 2 API and how it made it easier to integrate in-app purchases

At the recently held WWDC 2021, a new version of StoreKit 2 was presented. It is a framework that is responsible for making purchases in iOS. The share of apps with in-app purchases and subscriptions is growing steadily, and with the release of StoreKit 2, Apple has made it much easier to integrate purchases into the app. Today we will look at working with StoreKit 2 from the server side, that is, using the App Store Server API.





Authenticating Requests

In the current version of the API, a Shared Secret is required to send a request. This is a secret fixed string that can be obtained from the App Store Connect. The new version of the API uses the JSON Web Token (JWT) standard to authenticate requests.





Key generation

First of all, you need to create a private key with which the requests will be signed. This is done in the App Store Connect under Users and Access. There you need to go to the Keys tab and select the In-App Purchase key type. After creation, the key must be downloaded. You will also need its ID, which can be copied on the same page, and the Issuer ID, which is located on the App Store Connect API tab.





Creating a private key for working with the App Store Server API
Creating a private key for working with the App Store Server API

Token creation

, . , , Python. , . 60 .





JWT- App Store Server API
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}'
}
      
      



(signed transactions)

API JSON Web 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..."
	]
}
      
      



Apple , , . , .





  • appAccountToken



    , . UUID . , (, .), , .





  • offerType



    offerIdentifier



    , , . offerType



    :





    • 1



      — ( )





    • 2



      — ( )





    • 3







  • ( 2



    3



    ), offerIdentifier



    . , . .





  • inAppOwnershipType



    , , . :





    • PURCHASED







    • FAMILY_SHARED







  • type



    , . :





    • Auto-Renewable Subscription







    • Non-Consumable







    • Consumable







    • Non-Renewing Subscription







  • cancellation_date



    cancellation_reason



    revocationDate



    revocationReason



    . , , .





  • camelCase ( App Store Server API).





  • Unix timestamp .





{
	"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"
}
      
      



GET https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/{originalTransactionId}



, {originalTransactionId}



— . .





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



status



, , , . :





  • 1



    — , .





  • 2



    — , .





  • 3



    — Billing Retry, , . Apple 60 . .





  • 4



    — Grace Period, , . App Store Connect Grace Period, .





  • 5



    — , .





signedTransactionInfo



, .





signedRenewalInfo



.





, . , , , . , .





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



, GET https://api.storekit.itunes.apple.com/inApps/v1/history/{originalTransactionId}



, {originalTransactionId}



— . , .





20 , , hasMore



true



. , GET- revision



, .





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



, , . , .





(V1) , . , . , Apple : DID_CHANGE_RENEWAL_STATUS



INTERACTIVE_RENEWAL



, , - , , . (V2), , .





OFFER_REDEEMED



, EXPIRED



GRACE_PERIOD_EXPIRED



, . SUBSCRIBED



PRICE_INCREASE



— .





JWS.





Types of notifications

, , , .





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



Sandbox

, , URL Sandbox : https://api.storekit-sandbox.itunes.apple.com



.





, , URL Production Sandbox . Sandbox V2, Production V1.





App Store Connect :





  • Sandbox , .





  • Sandbox .





  • Sandbox . , , 1 , 5 , .





Apple . , :





  • ;





  • ;





  • ;





  • Sandbox .





API , receipt originalTransactionId. , .





, — . Adapty :





  • .





  • , .





  • / .





  • .





  • .





  • Open source SDK .





, .








All Articles