当前位置:网站首页>Wwdc21 - App store server API practice summary

Wwdc21 - App store server API practice summary

2022-06-23 03:03:00 37 mobile game IOS technology operation team

author :iHTCboy

About App Store The user did not notify the developer when refunding , until 2020 year 6 In May, apple provided a refund notice , But because it's not API The way , As a result, developers may not receive the refund notice . In addition, users recharge successfully but app No gold coins or services, etc , Developers generally can't judge whether users actually pay . Sum up , The apple in WWDC21 Brings a new and powerful App Store Server API, This article lets us understand the process of practice , Comprehensive understanding App Store Server API.

One 、 Preface

Hello everyone , We were in last year WWDC21 after 6 month 17 A summary article was published on the th 《 Apple iOS Three steps of internal purchase - WWDC21》. At that time, it was just sorted out according to the content of Apple's speech , At that time, many interfaces and functions were not online , For example, query the user's apple receipt according to the player's invoice order number , Query historical order interface, etc , At that time, the article did not make an in-depth analysis , And now all 2022 Years. , Apple App Store Server API Now online , therefore , Today, let's learn about , relevant API The specific use of it ~

Two 、App Store Server API

First , Let's start with a list of ,WWDC21 Apple offers those Server API, Then we are looking at how to practice these interfaces , Finally, I would like to summarize the precautions .

2.1 API brief introduction

Query the receipt of the user's order

GET https://api.storekit.itunes.apple.com/inApps/v1/lookup/{orderId}
  • Look Up Order ID: Use order ID Get the user's in app purchase item receipt information from the receipt .

Query the user's historical receipts

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

Query the user's refund for internal purchase

GET https://api.storekit.itunes.apple.com/inApps/v1/refund/lookup/{originalTransactionId}
  • Get Refund History: obtain app A list of all in app purchases that are refunded to users in .

Query user subscription item status

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

Submit anti fraud information

PUT https://api.storekit.itunes.apple.com/inApps/v1/transactions/consumption/{originalTransactionId}

Send Consumption Information: When a user requests a refund , Apple notice (CONSUMPTION_REQUEST) Developer server , Developers can find it in 12 Within hours , Provide user information ( For example, whether game gold coins have been consumed 、 How much has the user recharged 、 How much is the refund ), Finally, apple received this information , assist “ Refund decision system ” To decide whether to allow users to refund .

Extend the duration of user subscription

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

Extend a Subscription Renewal Date: Use the original transaction identifier to extend the renewal date of the user's valid subscription .( It is equivalent to adding subscription duration to users for free )

2.2 Description of interface parameters

App Store Server API It's apple that provides developers , Through the server to manage users in App Store A set of interfaces purchased in the application (REST API).

URL

Online environment URL:

https://api.storekit.itunes.apple.com/

Sandbox environment test :

https://api.storekit-sandbox.itunes.apple.com/

JWT brief introduction

Call these API need JWT(JSON Web Token) To authorize . So what is JWT Well ?

JWT It's an open standard ( Specification documents  RFC 7519), Used between parties to JSON Object security transfers information . There are two realizations , Based on the JWS The implementation uses BASE64URL Coding and digital signature The way of transmission Claims Provides integrity protection , That is to say, only the transmission is guaranteed Claims The content is not tampered with , But it will expose the plaintext . The other is based on JWE The implementation depends on the encryption and decryption algorithm 、BASE64URL Coding and Identity Authentication And other means to improve the transmission Claims The difficulty of content being cracked .

  • JWS( Specification documents  RFC 7515): JSON Web Signature, Said the use of  JSON Data structure and  BASE64URL The code represents a digital signature or message authentication code (MAC) Content of certification .
  • JWE( Specification documents  RFC 7516): JSON Web Encryption, Based on  JSON The encrypted content of the data structure .

At present, apple JWT Related content , It's all based on JWS Realization , So the following JWT Default finger JWS.

JWT(JWS) It's made up of three parts :

base64(header) + '.' + base64(payload) + '.' + sign( Base64(header) + "." + Base64(payload) )
  • header: The main statement is JWT The signature algorithm of ;
  • payload: It mainly carries various declarations and transmits plaintext data ;
  • signture: Who owns this part JWT go by the name of JWS, That is, signed JWS.

JWT Content example of :

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidGVhbSI6IjM35omL5ri45oqA5pyv6L-Q6JCl5Zui6ZifIiwiYXV0aG9yIjoiaUhUQ2JveSIsImlhdCI6MTUxNjIzOTAyMn0.dL5U_t_DcfLTY9WolmbU-j81jrZqs1HhHqYKM6HSxVgWCGUAKLzwVrnLuuMCnSRnrW9vmGKNqNvrzG8cEwxvAg

The details of the JWT Content , It's over here , You can automatically search to learn more .

assemble JWT

Know the basic JWT knowledge , We can start work . To generate a signature JWT There are three steps :

  1. establish JWT header .
  2. establish JWT Payload .
  3. stay JWT Sign on .

JWT header Example :

{
    "alg": "ES256",
    "kid": "2X9R4HXF34",
    "typ": "JWT"
}

JWT payload Example :

{
  "iss": "57246542-96fe-1a63e053-0824d011072a",
  "iat": 1623085200,
  "exp": 1623086400,
  "aud": "appstoreconnect-v1",
  "nonce": "6edffe66-b482-11eb-8529-0242ac130003",
  "bid": "com.example.testbundleid2021"
}

These are the field specifications required by apple , So different JWT The characters and contents are the same , therefore , Let's take a look at Apple's definition of these fields :

Field

Field description

Field value description

alg

Encryption Algorithm, encryption algorithm

The default value is :ES256.App Store Server API All of the JWT All must use ES256 Encrypt to sign .

kid

Key ID, secret key ID

Your private key ID, Value from App Store Connect, I'll talk about .

typ

Token Type, Token type

The default value is :JWT

iss

Issuer, The issuer

Your card issuer ID, Value from App Store Connect Key page for , I'll talk about .

iat

Issued At, Release time

second , With UNIX Time ( for example :1623085200) When the token was issued

exp

Expiration Time, Due time

second , The expiration time of the token , With UNIX Time in units . stay iat In more than 60 The token expired in minutes is invalid ( for example :1623086400)

aud

Audience, Audience

Fixed value :appstoreconnect-v1

nonce

Unique Identifier, Unique identifier

Any number that you create and use only once ( for example : "6edffe66-b482-11eb-8529-0242ac130003"). It can be understood as UUID value .

bid

Bundle ID, suit ID

Your app Suit ID( for example :“com.example.testbundleid2021”)

among kid and iss Values from App Store Connect Background creation and acquisition .

Generate the key ID(kid)

To generate a key , You must be in App Store Connect Has administrator role or account holder role in . Sign in App Store Connect And complete the following steps :

  1. choice “ Users and access ”, And then choose “ secret key ” Sub tab .
  2. stay “ Key type ” Choose “App Buy in items ”.
  3. single click “ Generate API Purchase project key in ”( If you've created , The click “ add to (+)” Button to add .).
  4. Enter the name of the key . This name is for your reference only , The name is not part of the key .
  5. single click “ Generate ”.
AppStoreServerAPI-01.jpg
AppStoreServerAPI-02.jpg

Generated key , There is a column called “ secret key ID” Namely kid Value , When the mouse moves to the text, it will show Copy key ID, Click the button to copy kid value .

Generate Issuer(iss)

Empathy ,iss Value generation , similar :

AppStoreServerAPI-03.jpg

issuer ID The value is iss Value .

AppStoreServerAPI-04.jpg

Generate and sign JWT

After getting the parameters here , You need to sign , Then you need a signed key file .

Download and save key file

App Store Connect The key file , It was generated just now kid when , On the right side of the list download App Purchase project key in Button ( Only if you have not downloaded the private key , The download link will be displayed .):

AppStoreServerAPI-05.jpg

This private key can only be downloaded once !

in addition Apple Do not keep a copy of the private key , Keep your private key in a safe place .

Be careful : Keep your private key in a safe place . Don't share keys , Do not store the key in the code repository , Do not put the key in the client code . If you suspect that the private key has been stolen , Please immediately App Store Connect Revoking the key in . For more information , see also revoke API secret key .

AppStoreServerAPI-06.jpg

API The key has two parts : Apple keeps the public key and the private key you downloaded . The developer uses the private key to authorize API stay App Store To access data in the .

It should be noted that ,App Store Server API The key is App Store Server API Unique , Cannot be used for other Apple service ( such as Sign in with Apple Service or App Store Connet API Service etc. .).

by API Request to generate token

Final ,JWT Header and payload Example :

{
    "alg": "ES256",
    "kid": "2X9R4HXF34",
    "typ": "JWT"
}

{
    "iss": "57246542-96fe-1a63-e053-0824d011072a",
    "iat": 1623085200,
    "exp": 1623086400,
    "aud": "appstoreconnect-v1",
    "nonce": "6edffe66-b482-11eb-8529-0242ac130003",
    "bid": "com.apple.test"
}

With the above parameters and key file , We can press JWT Specification requirements to generate token value . Let's say Python3 Code to generate token, Other language types .

First , Terminal execution command , install ptyhon Dependency Library :

pip3 install PyJWT

We make use of Python Of PyJWT Library to generate JWT token. Sample code :

import jwt
import time

#  Read the contents of the key file certificate 
f = open("/Users/37/Downloads/SubscriptionKey_2X9R4HXF34.p8")
key_data = f.read()
f.close()

# JWT Header
header = {
	"alg": "ES256",
	"kid": "2X9R4HXF34",
	"typ": "JWT"
}

# JWT Payload
payload = {
	"iss": "57246542-96fe-1a63-e053-0824d011072a",
	"aud": "appstoreconnect-v1",
	"iat": int(time.time()),
	"exp": int(time.time()) + 60 * 60, # 60 minutes timestamp
	"nonce": "6edffe66-b482-11eb-8529-0242ac130003",
	"bid": "com.apple.test"
}

# JWT token
token = jwt.encode(headers=header, payload=payload, key=key_data, algorithm="ES256")

print("JWT Token:", token)

Output example :

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjJYOVI0SFhGMzQifQ.eyJpc3MiOiI1NzI0NjU0Mi05NmZlLTFhNjMtZTA1My0wODI0ZDAxMTA3MmEiLCJhdWQiOiJhcHBzdG9yZWNvbm5lY3QtdjEiLCJpYXQiOjE2NDMwMTU1OTQsImV4cCI6MTY0MzAxOTE5NCwibm9uY2UiOiI2ZWRmZmU2Ni1iNDgyLTExZWItODUyOS0wMjQyYWMxMzAwMDMiLCJiaWQiOiJjb20uYXBwbGUudGVzdCJ9.muBKcbT3AnK3WAivbtIr64d2Gu7bVhGL3AhiYnDjb7D3qslHNnASE2EUUuN24jOLsSnLBWkBdwDutl5UU87paw

Through this script, you can generate token To request App Store Server API 了 . Of course, you can encapsulate the above code into a method , Pass in kid and iss Equal parameter , Then return token, It's over here .

2.3 Interface explanation

Said so much , Finally back to the following !!!

How to ask for App Store Server API ? Apple gives an example :

curl -v -H 'Authorization: Bearer [signed token]' 
"https://api.storekit.itunes.apple.com/inApps/v1/subscriptions/{original_transaction_id}"

Also is to use JWT Generated token, Put it in App Store Server API Request linked header part ,key by Authorization,value by Bearer [signed token].

Next , We go through Python Of requests To request App Store Server API. You can also use other tools to simulate , Such as online tools or Postman etc. .

Terminal execution command , install ptyhon Dependency Library :

pip3 install requests
import requests
import json

# JWT Token
token = "xxxxx"

#  Request links and parameters 
url = "https://api.storekit.itunes.apple.com/inApps/v1/lookup/" + "MK5TTTVWJH"
header = {
	"Authorization": f"Bearer {token}"
}
#  Requests and responses 
rs = requests.get(url, headers=header)
data = json.loads(rs.text)

print(data)

Example query results :

{
    'status': 0, 
    'signedTransactions': [
            'eyJhbGciOiJFUz.............',                       
            'eyJ0eXAiOiJKV1.............',               
            'eyJ0eXAiJhbGci.............'
    ]
}

Next , The following example of a request is not repeated , It mainly explains the function of the interface and the returned data format , Precautions, etc .

Query the receipt of the user's order (Look Up Order ID)

AppStoreServerAPI-07.jpg
GET https://api.storekit.itunes.apple.com/inApps/v1/lookup/{orderId}

Now this Look Up Order ID The interface is only available in the online environment , Sandbox environment is not supported . because , This interface is used after the user purchases the project , Upon receipt of Apple's invoice , There is a column called order number Order ID, In the past, it was impossible to get the transaction order number from apple with the developer transactionId Make mapping associations , And now , You can query through this interface !

The data format of the response :

AppStoreServerAPI-08.jpg

What this interface does , When a user complains ( Recharging fails to reach the account ) when , Let players provide orders ID, Then query the corresponding status of the order through this interface , If there are unused receipts (transactionId) when , It can provide users with reissue or service support .( Because we can find transactionId, This indicates that the player's recharge order is valid ! As for whether to consume , The server is required to check whether there is any unused receipt .)

status=0, Indicates a valid order number :

{
    'status': 0, 
    'signedTransactions': [
            'eyJhbGciOiJFUz.............',                       
            'eyJ0eXAiOiJKV1.............',               
            'eyJ0eXAiJhbGci.............'
    ]
}

If you have any attention , Will see signedTransactions There are many transaction Transaction receipt , Why is that ? Actually , Here is a Order ID It can correspond to multiple purchased items , Let's say the user 1 In minutes , Also bought 2 A project , those , When Apple sends invoices to users , Will merge this 2 One order is one order , There is only one order number Order ID.

AppStoreServerAPI-09.jpg

therefore , Developers need to pay attention to ,Order ID For a purchase order , It's not the only one . Verify the user's Order ID when , You have to go through all the signedTransactions, Find items that may not have been consumed .

Every JWT decode after , Sample format :

header:

{
    "alg":"ES256",
    "x5c":[
            "MIIEMDC....",
            "MIIDFjC....",
            "MIICQzC...."
            ]
}

payload

{
    "transactionId": "20000964758895",
    "originalTransactionId": "20000964758895",
    "bundleId": "com.apple.test",
    "productId": "com.apple.iap.60",
    "purchaseDate": 1640409900000,
    "originalPurchaseDate": 1640409900000,
    "quantity": 1,
    "type": "Consumable",
    "inAppOwnershipType": "PURCHASED",
    "signedDate": 1642995907240
}

This is the data format of a consumable item .

Last , About parsing JWT Content , I won't go into details here , The following is a unified explanation .

Query the user's historical receipts (Get Transaction History)

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

according to WWDC21 Video introduction , Interface can get users in your app In app purchase transaction history . But in practice , It is found that the consumable items are not found , Review the interface documentation Get Transaction History, Found new update instructions :

The transaction history return result only supports the following cases :

  • Auto renew subscription
  • Non renewal subscription
  • Non expendable in app purchases
  • Consumable in app purchases : If the transaction is refunded 、 Revocation or app Transaction processing has not been completed .

The data format of the response :

AppStoreServerAPI-10.jpg

It should be noted that , In the returned result , No, status Field .

{
    "revision": "1642993906000_1000000954832195",
    "bundleId": "com.apple.test",
    "appAppleId": 925021570,
    "environment": "Production",
    "hasMore": false,
    "signedTransactions": [
        "eyJhbGciOi...",
        "eyJhbGciOi..."
    ]
}

Default signedTransactions Back up to 20 strip , At present, developers can't control this number . exceed 20 Stripe time , The data has a field hasMore by ture, Indicates that there are updated historical orders , here , The developer needs to add the requested query field revision, The corresponding value corresponds to the data returned from the previous request revision Field contents .

for instance , Request more data :/inApps/v1/history/foriginalTransactionId}&revision=8a170756-e913-42fc-8629-76051f9e1134.

Every JWT decode after , Sample format :

payload

{
    "transactionId": "1000000954804912",
    "originalTransactionId": "1000000954804912",
    "webOrderLineItemId": "1000000071590544",
    "bundleId": "com.apple.test",
    "productId": "com.apple.iap.month",
    "subscriptionGroupIdentifier": "20919269",
    "purchaseDate": 1642990548000,
    "originalPurchaseDate": 1642990550000,
    "expiresDate": 1642990848000,
    "quantity": 1,
    "type": "Auto-Renewable Subscription",
    "inAppOwnershipType": "PURCHASED",
    "signedDate": 1643024941850
}

This is the data format of an auto renewal subscription item .

Query the user's refund for internal purchase (Get Refund History)

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

Purchased through any of the users originalTransactionId Can pass Get Refund History Find all the refund records and orders of this user .

The data format of the response :

AppStoreServerAPI-11.jpg

The response contains signedTransactions And App Store Server One or more of the notices REFUND notice (Notification) May be the same . therefore , Use this API Check for any refund notices you may have missed , For example, during server downtime .

But we need to pay attention , Include only App Store Approved refunds : Consumptive 、 Non consumptive 、 Automatic renewal subscription and non renewal subscription . If the user does not receive any App Store Approved refunds , An empty... Is returned on success signedTransactions Array .

{
    "signedTransactions": []
}

Because in a sandbox environment , The operation was rejected by Apple , Online application failed , therefore , There is no format for the returned data .( The refund format will be supplemented later .)

Query user subscription item status (Get All Subscription Statuses)

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

Subscription item status query API Get All Subscription Statuses, Get your app Status of all subscriptions of users in .

The data format of the response :

{
    "environment": "Sandbox",
    "bundleId": "securitynote",
    "data": [
        {
            "subscriptionGroupIdentifier": "20919269",
            "lastTransactions": [
                {
                    "status": 2,
                    "originalTransactionId": "1000000954804912",
                    "signedTransactionInfo": "eyJhbGciOiJFUz....",
                    "signedRenewalInfo": "eyJhbGciOiJFUzI1Ni...."
                }
            ]
        }
    ]
}
AppStoreServerAPI-12.jpg

lastTransactions Is the last subscription status of each subscription item ,status type :

  • 1: It works
  • 2: Be overdue
  • 3: Account deduction retry
  • 4: Account grace period ( This is the developer setting , For example, when the due fee deduction fails , How long can users be delayed .)
  • 5: Has been revoked .

signedTransactionInfo Format example :

{
    "transactionId": "1000000955217725",
    "originalTransactionId": "1000000954804912",
    "webOrderLineItemId": "1000000071615442",
    "bundleId": "com.apple.test",
    "productId": "com.apple.iap.month",
    "subscriptionGroupIdentifier": "20919269",
    "purchaseDate": 1643023487000,
    "originalPurchaseDate": 1642990550000,
    "expiresDate": 1643023787000,
    "quantity": 1,
    "type": "Auto-Renewable Subscription",
    "inAppOwnershipType": "PURCHASED",
    "signedDate": 1643028928116
}

signedRenewalInfo Format example :

{
    "expirationIntent": 1,
    "originalTransactionId": "1000000954804912",
    "autoRenewProductId": "com.apple.iap.month",
    "productId": "com.apple.iap.month",
    "autoRenewStatus": 0,
    "isInBillingRetryPeriod": false,
    "signedDate": 1643028928116
}

Submit anti fraud information (Send Consumption Information)

PUT https://api.storekit.itunes.apple.com/inApps/v1/transactions/consumption/{originalTransactionId}

This interface is used to submit anti fraud information to apple , You can check the document for details Send Consumption Information .

When a user requests a refund , Apple notice (CONSUMPTION_REQUEST) Developer server , Developers can find it in 12 Within hours , Provide user information ( For example, whether game gold coins have been consumed 、 How much has the user recharged 、 How much is the refund ), Finally, apple received this information , assist “ Refund decision system ” To decide whether to allow users to refund . For details, please see our previous Article content Learn more about .

The user submits a refund application , The apple system will 48 Update the processing results in the problem report within hours . therefore , After the developer receives the user's refund notice , Yes 12 Hours to decide whether to provide anti fraud information to Apple .

AppStoreServerAPI-13.jpg

parameters Field description , See the document for details :Send Consumption Information

Requested Response Codes by 202 It means that Apple has received a message .

Extend the duration of user subscription (Extend a Subscription Renewal Date)

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

Developers have... A year 2 One chance to add... Each time to subscribers 90 Days free compensation . That is, there are automatic subscription types App, Developers can take the initiative to compensate users on the server ( Free extension ) User's order time , At most 90 God . See the document for details Extend a Subscription Renewal Date.

The following types of subscriptions Do not conform to the Conditions for renewal date extension :

  • Subscription during the free discount period
  • Inactive subscription in billing retry status
  • Has expired , Subscription in grace period
  • in the past 365 Two subscriptions with extended renewal dates have been received within days

in addition , Apple has a hint : When App Store When calculating the developer's commission percentage , The extension period is not included in the one-year paid service .

Simply speaking , One year after the user subscribes to the project , Developers can get 85% Net income . And developers give users free extended time , Not included in the time of the year !( Get it. ?)

AppStoreServerAPI-14.jpg

parameters Field description , See the document for details :ExtendRenewalDateRequest | Apple Developer Documentation

Requested Response Codes by 200 It means the request is successful .

2.4 Question answer

Authorization: Bearer signed token

Whenever a user accesses a protected route or resource , Users can use the hosted (bearer) Mode send JWT, Usually in Authorization In the head , The content format is as follows :

Authorization: Bearer [signed token]

And then , The server will take out token The content in , To return the corresponding content . We need to pay attention to , This token Not necessarily stored in cookie in , If there is cookie In the words of , I need to set to http-only, prevent XSS. in addition , You can see , If you are like Authorization: Bearer Sent in token, Then cross domain resource sharing (CORS) It won't be a problem , Because it doesn't use cookie.

therefore ,JWT The main purpose of is to transfer declarations in a secure way between the server and the client . Main application scenarios :

  • authentication Authentication
  • to grant authorization Authorization
  • associated recognition
  • Client session ( Stateless session )

Error Codes

If token When invalid or invalid , Return content :

Unauthenticated

Request ID: 7F5DBZ7VDX677TOPBAOEUXWSCY.0.0

If requested originalTransactionId non-existent , Will report a mistake 4040005(OriginalTransactionIdNotFoundError):

{
    "errorCode": 4040005,
    "errorMessage": "Original transaction id not found."
}

Other error codes :

Object

errorCode

errorMessage

GeneralBadRequestError

4000000

Bad request.

InvalidAppIdentifierError

4000002

Invalid request app identifier.

InvalidRequestRevisionError

4000005

Invalid request revision.

InvalidOriginalTransactionIdError

4000008

Invalid original transaction id.

InvalidExtendByDaysError

4000009

Invalid extend by days value.

InvalidExtendReasonCodeError

4000010

Invalid extend reason code.

InvalidRequestIdentifierError

4000011

Invalid request identifier.

SubscriptionExtensionIneligibleError

4030004

Forbidden - subscription state ineligible for extension.

SubscriptionMaxExtensionError

4030005

Forbidden - subscription has reached maximum extension count.

AccountNotFoundError

4040001

Account not found.

AccountNotFoundRetryableError

4040002

Account not found. Please try again.

AppNotFoundError

4040003

App not found.

AppNotFoundRetryableError

4040004

App not found. Please try again.

OriginalTransactionIdNotFoundError

4040005

Original transaction id not found.

OriginalTransactionIdNotFoundRetryableError

4040006

Original transaction id not found. Please try again.

GeneralInternalError

5000000

An unknown error occurred.

GeneralInternalRetryableError

5000001

An unknown error occurred. Please try again.

Detailed error code description , See documentation :Error Codes.

Query the receipt of the user's order (Look Up Order ID)

GET https://api.storekit.itunes.apple.com/inApps/v1/lookup/{orderId}

Through this interface , Can I check all orders ? Or just use StoreKit2 Only created orders can be queried ?

answer : At present, the author has found many 2020 Project order number purchased in , Can pass. API Query to . therefore , This interface does not limit the purchase period of the order .( At least 2020 Years later, you can find , If abnormal , Welcome to the comment area to exchange ideas .)

JWT Signature verification

towards App Store Server API Every request made , You need to take it with you JSON Web Token(JWT) Token to authorize . Apple suggests that there is no need for every API Request to generate a new token . In order to learn from App Store Server API Get better performance , Please reuse the existing signing token , Each token has 60 Minutes effective time .

If you just want to get JWT The payload of Payload Parameters , Can directly base64 Decode Payload Just parameters , But if you need to verify the signature , You must use Signture, Header.

It can be used Python Of PyJWT Library come decode:

import jwt

token = "exxxxxx" # Need to decode  token

data = jwt.decode(token, options={"verify_signature": False})

Apple's advice is : Various open source libraries can be used , Create and sign JWT token . of JWT For more information , see also JWT.io.

from PyJWT The document can be seen ,JWT The contents to be verified include :

  • verify_signature: verification JWT Encrypted signature
  • verify_aud: match audience
  • verify_iss: match issuer
  • verify_exp: Is it overdue
  • verify_iat: Integer or not
  • verify_nbf: Whether it is the past time (nbf Express :Not Before Abbreviation , Express JWT Token It is invalid before this time . That is, the effective time .)

therefore , We can understand , verification JWT There are so many contents . The most important thing is to verify verify_signature, When verifying the signature , Use a public key or key to decrypt Sign, and base64UrlEncode(header) + "." + base64UrlEncode(payload) When the content is exactly the same , Indicates that the verification passed .

The verification process , Example :

import jwt

public_key = "xxxx" # Contents of public key certificate 

data = jwt.decode(token, key=public_key, algorithms=["ES256"])

So here comes the question , Where can I get Apple's public key ? Found clues through the Apple Developer Forum :

Simply speaking ,JWS Of x5c The header field contains a certificate chain (x509), The first certificate contains the for verifying JWS Signed public key . Obtained from signedTransactions in , Take a token The format of decoding is as follows :

{
    "alg": "ES256",
    "x5c": [
        "MIIEMDCCA7.....",
        "MIIDFjCCApy.....",
        "MIICQzCCAc......"
    ]
}

Certificates can be obtained from apple Apple PKI Page download .

x5c The last certificate in the certificate chain , Corresponding to Apple's certificate Apple Root CA - G3 Root, But we need to .cer convert to .pem Format , command :

openssl x509 -inform der -in AppleRootCA-G3.cer -out AppleRootCA-G3.pem

annotation : X.509: Is a certification standard , It mainly defines what should be included in the certificate . For details, please refer to RFC5280,SSL This is the certificate standard used . alike X.509 certificate , There may be different encoding formats , At present, there are two coding formats :DER:Distinguished Encoding Rules, It's in binary format , Do not read . PEM:Privacy Enhanced Mail, Open to see the text format , With ”—–BEGIN…” start ,”—–END…” ending , The content is BASE64 code .

AppleRootCA-G3.pem Content , and x5c The contents of the last certificate in the certificate chain are the same , as follows :

MIICQzCCAcmgAwIBAgIILcX8iNLFS5UwCgYIKoZIzj0EAwMwZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcNMTQwNDMwMTgxOTA2WhcNMzkwNDMwMTgxOTA2WjBnMRswGQYDVQQDDBJBcHBsZSBSb290IENBIC0gRzMxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABJjpLz1AcqTtkyJygRMc3RCV8cWjTnHcFBbZDuWmBSp3ZHtfTjjTuxxEtX/1H7YyYl3J6YRbTzBPEVoA/VhYDKX1DyxNB0cTddqXl5dvMVztK517IDvYuVTZXpmkOlEKMaNCMEAwHQYDVR0OBBYEFLuw3qFYM4iapIqZ3r6966/ayySrMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMQCD6cHEFl4aXTQY2e3v9GwOAEZLuN+yRhHFD/3meoyhpmvOwgPUnPWTxnS4at+qIxUCMG1mihDK1A3UT82NQz60imOlM27jbdoXt2QfyFMm+YhidDkLF1vLUagM6BgD56KyKA==

therefore , Specific verification , Reference resources Validate StoreKit2 The answer given in :

def good_signature?(jws_token)
  raw = File.read "/Users/steve1/downloads/AppleRootCA-G3.cer"
  apple_root_cert = OpenSSL::X509::Certificate.new(raw)

  parts = jws_token.split(".")
  decoded_parts = parts.map { |part| Base64.decode64(part) }
  header = JSON.parse(decoded_parts[0])

  cert_chain =  header["x5c"].map { |part| OpenSSL::X509::Certificate.new(Base64.decode64(part))}
  return false unless cert_chain.last == apple_root_cert

  for n in 0..(cert_chain.count - 2)
    return false unless cert_chain[n].verify(cert_chain[n+1].public_key)
  end

  begin
    decoded_token = JWT.decode(jws_token, cert_chain[0].public_key, true, { algorithms: ['ES256'] })
    !decoded_token.nil?
  rescue JWT::JWKError
    false
  rescue JWT::DecodeError
    false
  end
end

The above code is used Ruby Written , The logic to implement verification is , Use what Apple offers AppleRootCA-G3.cer Certificate content verification JWT x5c The last certificate in the certificate chain , And then use it x509 Certificate chain specification , Verify each remaining certificate chain , Last use x5c The public key of the first certificate in the certificate chain , To verify JWT.

Sign in with Apple

except App Store Server API, also Sign in with Apple、App Store Connet API Etc , Is to use JWT To pass on . Specific requirements and fields may be related to App Store Server API inequality . such as Sign in with Apple Of JWT Unwanted typ,sub And bid Same meaning , It's all about Bundle ID,app The package name . So these specifications are the same , When these details are different , Developers need to step on the pit before they know , It shows the importance of specifications .

{
    "alg": "ES256",
    "kid": "ABC123DEFG"
}
{
    "iss": "DEF123GHIJ",
    "iat": 1637179036,
    "exp": 1693298100,
    "aud": "https://appleid.apple.com",
    "sub": "com.apple.test"
}

3、 ... and 、 summary

Xiaobian began to want to explain all subscription types in detail , But the writing process found that things are very complicated , Because subscription projects are complex , There are many fields and functions . Limited to the length of the article , And apple docs already have detailed field descriptions , So this article mainly explains App Store Server API The overall process and precautions . If there are errors or problems , Welcome to the comment area for correction and exchange ~

secondly ,App Store Server API The significance of the new interface is very significant ! In the past, a large number of black ash products were produced through internal purchase , Take advantage of the loopholes in various links of Apple's internal purchase , Through the exchange rate difference 、 Zombie account 、 Malicious refund, etc , A workshop forming an industrial chain 、 Gang crime . The last year , Apple has provided a refund notice for internal purchases , This year, a query interface is provided , There are also relevant customer service interfaces , Although they are all late responses , But to a certain extent , It is an important blow to the fight against black and evil !

Last , From Apple's open interface and Philosophy , Apple focuses on the user experience , I hope that the opener can better serve users ! therefore ,2022 year , I hope to share with you Learn and share interesting technologies , Polish excellent product experience and service ! Work hard together ~

Four 、 Reference

原网站

版权声明
本文为[37 mobile game IOS technology operation team]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/01/202201251406278352.html