当前位置:网站首页>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}- Get Transaction History: Get users in your app In app purchase transaction history .
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}- Get All Subscription Statuses: Get your app Status of all subscriptions of users in .
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 ofJSONData structure andBASE64URLThe code represents a digital signature or message authentication code (MAC) Content of certification .JWE( Specification documents RFC 7516):JSON Web Encryption, Based onJSONThe 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 :
- establish JWT header .
- establish JWT Payload .
- 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 |
|---|---|---|
| Encryption Algorithm, encryption algorithm | The default value is :ES256.App Store Server API All of the JWT All must use ES256 Encrypt to sign . |
| Key ID, secret key ID | Your private key ID, Value from App Store Connect, I'll talk about . |
| Token Type, Token type | The default value is :JWT |
| Issuer, The issuer | Your card issuer ID, Value from App Store Connect Key page for , I'll talk about . |
| Issued At, Release time | second , With UNIX Time ( for example :1623085200) When the token was issued |
| 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) |
| Audience, Audience | Fixed value :appstoreconnect-v1 |
| 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 . |
| 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 :
- choice “ Users and access ”, And then choose “ secret key ” Sub tab .
- stay “ Key type ” Choose “App Buy in items ”.
- single click “ Generate API Purchase project key in ”( If you've created , The click “ add to (+)” Button to add .).
- Enter the name of the key . This name is for your reference only , The name is not part of the key .
- single click “ Generate ”.
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 :
issuer ID The value is iss Value .
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 .):
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 .
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)
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 :
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.
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 :
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 :
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...."
}
]
}
]
}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 .
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. ?)
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
- Apple iOS Three steps of internal purchase :App Refund within 、 Historical order inquiry 、 Bind user anti drop doc !--- WWDC21
- Look Up Order ID | Apple Developer Documentation
- Get Transaction History | Apple Developer Documentation
- Get Refund History | Apple Developer Documentation
- Get All Subscription Statuses | Apple Developer Documentation
- Send Consumption Information | Apple Developer Documentation
- Extend a Subscription Renewal Date | Apple Developer Documentation
- Generating Tokens for API Requests | Apple Developer Documentation
- Generate and Validate Tokens | Apple Developer Documentation
- RFC 7519 - JSON Web Token (JWT)
- New fried cold rice : understand JWT The realization principle and basic use of - Throwable
- Validating "Sign in with Apple" Authorization Code - Parikshit Agnihotry
- What is a JWS and how to encode it for Apple In-App Purchases?
- Fraud in In-App Subscriptions : how to crack down on fraud from malicious users
- JWT(JSON Web) Use _wichandy Technology blog _51CTO Blog _jwt Use the tutorial
- RFC 7519 - JSON Web Token (JWT)
- Auto renew subscription - App Store - Apple Developer
- Getting only decoded payload from JWT in python - Stack Overflow
- Validate StoreKit2 in-app purchase jwsRepresentation in backend| Apple Developer Forums
- How do I convert a .cer certificate to .pem? - Server Fault
边栏推荐
- QUIC or TCP
- DAAS architecture and Implementation (I)
- Reinforcement learning series (III) -gym introduction and examples
- The difference between the use of return, break and continue in the if statement in JS
- Learning record -- memo on constructors and attributes in C
- Pyqt5 installation and use
- JS event delegation (event agent)
- Section 6: basic configuration I of spingboot
- 2D visual empowerment smart water green intensive development
- How to generate DataMatrix code in batch through TXT file
猜你喜欢

How to store, manage and view family photos in an orderly manner?

5. concept of ruler method

Soft exam information system project manager_ Contract Law_ Copyright_ Implementation Regulations - Senior Information System Project Manager of soft exam 030

6. template for integer and real number dichotomy

8. greed

Soft exam information system project manager_ Information system comprehensive testing and management - Senior Information System Project Manager of soft test 027
What is sitelock? What is the function?

C language series - Section 4 - arrays

Vulnhub DC-5
随机推荐
Integrate Tencent maps with micro build and low code
Goframe framework (RK boot): fast implementation of server-side JWT verification
Daily shift series: memory problem of primary online service
Autowired usage
The logical operators |, & &!
Deep analysis of time complexity
How to generate IATA barcode in batch through TXT file
5. concept of ruler method
Aiot application innovation competition - Smart street lamp (developed with keil)
2022-01-25: serialize and deserialize n-ary tree. Serialization means that a
Build a weather forecast applet using a widget
51. numerical arrangement
How to store, manage and view family photos in an orderly manner?
2022 opening H5 mobile page special effects
Tencent cloud server CVM system tool configuration
The difference between the use of return, break and continue in the if statement in JS
method
Docker builds MySQL master-slave
SAP mm initial transaction code MEK1 maintenance pb00 price
6 values in JS are 'false'