Discover the App Store Server Library and learn how you can take advantage of resources and configurations for your apps. We'll show you how to set up the library, call the App Store Server API, verify App Store Server Notifications, and use app receipts. Explore insights and best practices for using App Store Server API endpoints, verifying App Store signed data, and migrating away from verifyReceipt.
♪ ♪ Dave: Hi, I'm Dave Wendland, a developer advocate for our App Store Commerce team. My colleague Alex and I will discuss the new App Store Server Library and how its set of functions will enable your server to utilize an array of capabilities from generating a JWT for the App Store Server API to migrating away from the verifyReceipt endpoint for purchase validation.
When we look back, the App Store launched in 2008 when apps were either free or paid. Soon, we added in-app purchases, and ever since then, the developer community has grown in size and complexity across the globe. The App Store continues to roll out updates for developers and customers to support the global and dynamic mobile app ecosystem.
In 2021, we released the next era of StoreKit tools with the revamped StoreKit Framework, App Store Server API, and App Store Server Notifications v2. With more updates in 2022 and again this year at WWDC 2023. These tools provide transactions and status in a signed JWS format. And they are designed to provide developers robust information, client side and server side.
This set of APIs inspired the App Store Server Library. We are proud to share that this library provides a set of functions, which will make it easier for our developer community to adopt and integrate the latest APIs available today and in the future. The library beta launch supports four languages: Swift, Java, Node, and Python, giving you the flexibility to choose the language that best supports your backend and expertise.
The App Store server library for each language is available on GitHub, and we look forward to your feedback and contributions. I've broken down what the library has to offer into four key capabilities. The first and most robust capability is with the App Store Server API. By streamlining the JWT creation, you can use any of the dozen different endpoints the App Store Server API offers.
Next is a core capability to verify JWS signed data, so you can ensure your transactions and server notifications have been generated and signed by Apple.
Next is the extract receipt transaction utility. This simple tool extracts a transaction identifier from an app receipt. Doing this can alleviate your need to use the verifyReceipt endpoint and enable you to migrate to the App Store Server API for your purchase validation and additional capabilities. This provides a clear path to support your current and legacy app versions.
Lastly, is the utility to generate subscription promotional offer signatures. This utility does the heavy lifting of signing and generating your offers using your in-app purchase private key. If you are not familiar with the subscription promotional offers, learn more in our session titled "Subscription Offers Best Practices." Now lets deep dive into three of those core library functions. App Store Server API, signed data verification, and moving to the App Store Server API. Let's get started with the App Store Server API.
The foundation to the Server API is the Get Transaction History endpoint. By simply using a transaction ID, this API provides a customer's complete in-app purchase transaction history. And it has even more capabilities beyond this endpoint. This API has a dozen endpoints, all of which require a form of authentication, a JSON Web Token. Generating your JWT is a critical step, and if you aren't familiar with this process, that is where the library comes in. Over to Alex to demonstrate getting the library setup for use with the App Store Server API.
Alex: Hello, I'm Alex Baker, an App Store Server Engineer. I'll demonstrate how to get started with the App Store Server Library and how you can use it to call the App Store Server API. This demo will walk through collecting the necessary pieces of information to configure the App Store Server Library, then show an example of creating an API client and calling the API. I am starting in App Store Connect to get information I'll need to use the App Store Server API with the library. Go to the Users and Access module...
Then the Keys tab, then the In-App Purchase option.
There are a few pieces of useful information here. First, the issuer ID. Next, I am going to generate a new private key. I'll give it a name, then click Generate. Generating the key provides two pieces of information: The key ID and the option to download the private key. Downloading is only possible once. Switching to the Apple Public Key infrastructure website, focus on the Apple Root Certificates section in the upper left. Download the root certificates.
Here is a simple Java project using the Gradle build system. First, add a dependency on the App Store Server Library.
Moving to the ExampleApp class, here are the pieces of information I obtained earlier, the issuerId, the keyId, and the private key. Additionally, store the bundleId of the app-- in this demonstration, I am using sandbox-- and store the appropriate enum value.
Using these pieces of information, instantiate an AppStoreServerAPIClient. With this client, call the Request a Test Notification endpoint, which requests the App Store server send a notification with type TEST to the URL you configured in App Store Connect. Last, print the testNotificationToken. Running this, we will see the testNotificationToken printed, and, as expected, we see this token.
This demonstrated how to use the App Store Server library to simplify using the App Store Server API and the information you need from App Store Connect. Now back to Dave. Dave: Thank you, Alex. That demo really illustrates how quickly this library gets you set up and generating a JWT for use with the Server API. This library will have a meaningful impact in reducing your implementation timelines when adopting our APIs.
While using the library is helpful and simple, there is nothing more critical than storing your in-app purchase private key securely. And if you ever think your key has been compromised, generate a new key in App Store Connect anytime. As you begin your development, we do recommend starting with sandbox and TestFlight transactions. And lastly, be sure to check regularly for updated Apple root certificate authorities. Now lets discuss why signed data verification is a foundational action to your business with in-app purchases. First let's discuss what signed data contains and why it is important. StoreKit Signed data means it was generated and signed by the App Store in a JSON Web Signature format and contains data about the app purchases, in-app purchases, customer events, and customer subscription status. The two most common signed data payloads are the JWS Transaction and JWS Renewal Info. Then the appTransaction contains details on the app version originally purchased and the version currently installed on device. And then we have the App Store Server Notifications V2, the notification itself is signed data and additionally may contain a JWS Transaction and JWS Renewal Info.
And as a reminder, you will only find this JWS signed data in StoreKit 2 on iOS 15 and later and in the App Store Server API and App Store Server Notifications v2.
It's recommended that you verify the JWS signed data after any of the following events: When delivering or unlocking content on device or when your server has received signed data, whether that be from your own app, another server, or App Store server notifications. And lastly, when you receive a response from the App Store server API. Here's Alex to demo how to verify JWS signed data and how the library handles this for you.
Alex: In this section, I'll show how to verify signed data from the App Store. I'll describe the verification process you would need to perform. Then, we'll walk through how the App Store Server Library's SignedDataVerifier class can perform this process for you. I would like to stress that using tools like the App Store Server Library is highly advisable when you are not familiar with the RFCs and protocols behind the operations I am about to describe. Here is some signed data from the App Store. It looks like there is a lot going on here. Color coding reveals there are three sections. Each section is separated by a period and is Base64 URL encoded. The first and largest section is the header. Once decoded, the header is a JSON structure with fields defined by the JWS specification. In this case, our header only has two fields: First, the algorithm, which is always ES256. Next a field called x5c. This is an array of certificates that are used to calculate the expected public key that signed the JWS. Let's review the certificate chain construction process. The first certificate in the array is the leaf certificate. This certificate's public key signed the JWS. To verify this certificate is from Apple, construct a chain of trust back to a known trusted source, in this case, an Apple root certificate authority.
The next certificate in the array is the Apple Worldwide Developer Relations intermediate certificate authority. Think of this as a more specialized version of the Apple Root Certificate Authority focused on developers. The last certificate in the chain is an Apple root certificate authority so that we understand which Apple authority originated this chain. Reminder, it is important to verify the certificate exactly matches a root certificate we previously obtained from Apple's Public Key Infrastructure website.
The first step is to verify that each certificate is signed by the previous certificate in the chain. Then perform additional verification steps, like making sure each certificate has valid dates, is properly formatted, etc. Next, validate that these certificates are from Apple and that their purpose is to sign App Store data, as opposed to an unrelated use case, which would not be valid to sign App Store data.
To verify the leaf certificate, confirm its purpose by checking the presence of the object identifier, or OID, for Mac App Store Receipt Signing. For the intermediate certificate, check that the intermediate authority OID for Apple Worldwide Developer Relations. Last, as stated before, make sure the root certificate authority is one of the certificates you stored as an Apple Root Certificate Authority. Now let's actually decode a leaf certificate and observe how to check these values.
Here is the X.509 v3 extensions section of a certificate as produced by the OpenSSL x509 command.
At the bottom is the OID listed on the previous slide, indicating the certificate's purpose is App Store receipt signing. However, there are some additional fields that are important to check.
Here we see the authority information access section, which provides information about the issuer and, importantly, provides information for checking if the certificate was revoked. Using the Online Certificate Status Protocol, or OCSP. check if a certificate has been revoked before proceeding with the verification process. The process and cryptographic procedures for doing so are defined in RFC 6960.
After verifying the certificate chain, check the JWS is signed by the leaf certificate's public key. Take the leaf certificate from before, extract the public key of the leaf certificate, take the key and original JWS, and pass them to a JWS signature verification function. The verification function checks that the data is signed by the public key and decodes the payload. The process is almost complete, but there is one additional verification step.
Here is a decoded App Store Server TEST Notification. The previous steps verified the data came from the App Store. However, also check that the notification is targeted at your correct application and environment.
Check the appAppleId and bundleId to confirm the notification is targeted for your correct application. Check the environment matches the expected environment.
Just like the other steps in the verification process, the App Store Server library also checks these when it performs the verification.
That completes the process to verify signed data from the App Store. Next, I'll expand my project from earlier to verify signed data using the SignedDataVerifier class included in the App Store Server Library. The SignedDataVerifier class performs the verification steps previously covered.
In this demo, I'll get the test notification that I requested earlier, and then validate and decode the signed notification. There is a small delay between requesting the test notification and the notification being received on my server. Therefore, add a five-second delay. Next, call the Get Test Notification Status endpoint using the testNotificationToken obtained earlier. Last, print the first few characters of the notification to confirm success. The Get Test Notification Status endpoint returns the result of the send attempt, as well as the notification payload. The beginning of that payload is what we should see.
As expected, I see the first few characters of the notification. Moving on, creating a signed data verifier. This requires three pieces of information, starting with the list of Apple Root certificate authorities.
The certificates I downloaded earlier are now in the resources folder. Import the root certificates into a Set.
Since I am using sandbox, I don't require an app Apple ID. Pass null instead in sandbox. Last, whether to perform revocation checking. Because the notification was just received, onlineChecks should be true. For notifications received months or years ago, this should be false because the certificates may have expired. Pass these fields into a new SignedDataVerifier. Then, pass in the notification received earlier, print the result, and then run the program.
Once the program is complete, the program will display a verified and decoded notification. Since this is a test notification, this will have a type of TEST and the app's bundle identifier in the payload, along with a few other fields.
As expected, I see a decoded notification of type TEST.
I expanded on our previous demo to demonstrate the SignedDataVerifier object. Here’s Dave to review some best practices. Dave: Wow, that really illustrates all the steps required to verifying signed data and how the library can handle that complexity for you, so be sure to utilize the SignedDataVerifier for your server-side validation. An important reminder: when you verify data, you still need to confirm the app and product identifiers to ensure you are granting or revoking purchases for the correct app or service. Lastly, as certificates expire and can be revoked, don't hardcode any certificates, client side or server side, and always validate that they are active. Now we'll review another App Store server utility to assist with moving your server-side app receipt validation off the verifyReceipt endpoint and over to the App Store Server API. The App Store Server library offers a utility to help specifically with this migration and ensures no app is left behind.
When considering moving to the App Store Server API, there are many reasons to prioritize this work in your roadmap. The API supports purchase validation and contains additional endpoints useful for customer support, appeasement, and testing App Store Server Notifications V2. As we continue to make updates and release new properties, these will be released only with the JWS signed data, which is supported by StoreKit 2, App Store Server API, and App Store Server Notifications V2.
An additional benefit, the only data you need to record, is an original transaction ID or transaction ID. You no longer need to save base64 encoded receipts in your account system. And with our continued investment in the latest APIs, we have announced that verifyReceipt endpoint is now deprecated. To learn more, check out the session "What's new in App Store server APIs" for a detailed update and guidance. Now here's Alex to share how the App Store server library will assist in your migration. Alex? Alex: Thanks, Dave. Now let's walk through a flow diagram for App Receipts. While StoreKit 2 and the App Store Server API are great tools to use, it is important to support clients on older devices or users that have not updated recently, and for which only App Receipts may be provided to your server. I'll show how these devices were supported previously, and then how you can continue to support these clients following verifyReceipt's deprecation.
First, the device sends a receipt to your server. In the old model, your server passes this receipt to verifyReceipt, and then receives the decoded receipt.
The response contains an originalTransactionId, which is passed to the Get Transaction History endpoint in the App Store Server API.
The App Store Server returns signed transactions you use to provide service to the customer.
Now that verifyReceipt is deprecated, let's replace this section. The receipt utility in the App Store Server Library directly extracts a transaction ID from a receipt. You pass the transaction ID to the App Store Server API, removing the need to make two round trips. After this, store the revision from the endpoint. This removes the need to re-parse the entire history each time.
Because the value extracted from the app receipt may or may not be an original transaction ID, we are excited to announce that many of our endpoints, including the Get Transaction History endpoint, now support any transaction ID as a parameter, not just an original transaction ID.
Now I'll demonstrate extracting transaction IDs for use with the Get Transaction History endpoint using the App Store Server Library. Here I will take an app receipt, extract a transaction ID, and call the Get Transaction History endpoint using the ID. First, the App Receipt. You can get an App Receipt from a device or an App Store Server Notification V1. I have one here already. Next, create an instance of the ReceiptUtility class. To extract the transaction ID, call the extract transaction ID from app receipt method. Not all receipts will have a transaction ID. It is possible the user doesn't have any purchases. Therefore, add a null check.
To provide some more depth to this problem, imagine we want to get information about the most recent consumables for this user and exclude revoked consumables. Create a Transaction History Request object, specify that only wanted are products of type CONSUMABLE to exclude revoked transactions and that the data should be returned in descending order.
We need two helper objects, a response variable, and a list of transactions. A do while loop pages through the responses from the transaction history endpoint. If this isn't the first request, fetch the revision token from the previous response to keep paging through the data. Then, call the Get Transaction History endpoint with the transaction ID from the app receipt, the request object, and the revision. Finally, add all the transactions from the response to the transaction list. Repeat this process until the hasMore field is false in the response.
Print out the transactions to see the result.
Here I see a list of transactions returned from the API. The could be decoded using the SignedDataVerifier from the previous demo.
Thanks for joining our final demonstration that showed how you can use App Receipts with the App Store Server API. Back to Dave to wrap us up. Dave: I'm really excited about the new App Store Server library, and I see these capabilities easing your adoption of our APIs and transitioning to the App Store Server API. Here's a screenshot of the App Store Server API Java repository on Github. On this page, you can find links to our documentation, submit pull requests, and find examples of how to use the library.
You can download the App Store Server library BETA soon and start planning your adoption of the App Store Server API. We look forward to your feedback and feature requests. Please contact us at Feedback Assistant and on Github. Thank you. ♪ ♪
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.