Migrate from the verifyReceipt API to the new

Apple has deprecated the https://buy.itunes.apple.com/verifyReceipt API and encourages users to migrate to the new https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId} API. However, during the migration, a problem was discovered. Previously, with the verifyReceipt API, the 21007 status code could be used to distinguish between the sandbox and production environments. Now, with the new API, if the {transactionId} is from the sandbox environment, it will directly return {"errorCode":4040010,"errorMessage":"Transaction id not found."}.

How can I distinguish whether a {transactionId} is generated in the sandbox environment or the production environment?

The best practice when sending transactions from your app to your server is to send the full jwsRepresentation. This signed data can be verified as coming from Apple locally on your server, without requiring a call to the App Store Server API. The easiest way to verify signed transactions on your server is to use the App Store Server Library, which has this function built in. See the following links: https://developer.apple.com/documentation/storekit/verificationresult/3868429-jwsrepresentation

https://developer.apple.com/documentation/appstoreserverapi/simplifying_your_implementation_by_using_the_app_store_server_library

The decoded transaction payload also contains an environment field, so you know whether to call the Production or Sandbox endpoints of the App Store Server API for that transaction: https://developer.apple.com/documentation/appstoreserverapi/jwstransactiondecodedpayload

If in some rare edge case you have only a transactionId on hand and don't know its associated environment, call the Production endpoint. If you get the 4040010 error code you described, you can fall back to calling the Sandbox environment. See the steps under "Test using the sandbox environment" here: https://developer.apple.com/documentation/appstoreserverapi#3820693

The error for reference: https://developer.apple.com/documentation/appstoreserverapi/transactionidnotfounderror

Thank you for your response. From your reply, I understand that you want us to use StoreKit 2 on the iOS side, as only StoreKit 2 returns jwsRepresentation. However, our current situation is that all our iOS code is written in Objective-C and uses StoreKit. The time cost to migrate to StoreKit 2 is quite high. But we would like to migrate our backend API to Apple's new API. So, I would like to ask further: can StoreKit and these new interfaces work together?

@aababc While I encourage you to migrate your app to StoreKit 2 when feasible (particularly since the original version of StoreKit was deprecated at WWDC24), this migration is not necessary to use the latest server technologies, including the App Store Server API, App Store Server Notifications, and the App Store Server Library.

Regardless of what StoreKit version you use in your app, data for the purchases users make are available from these modern server APIs. To access this data from the App Store Server API, most endpoints require only a single transactionId for the given user. For example, by calling Get Transaction History with any transactionId belonging to a user, you can access the full record of their transactions in your app. I recommend storing at least one transactionId for each user server-side for this purpose. One option to obtain said transactionId is to send a receipt from the app to your server and use the App Store Server Library server-side. This is what the documentation is describing when it describes "A utility that extracts transaction identifiers from receipts.": https://developer.apple.com/documentation/appstoreserverapi/simplifying_your_implementation_by_using_the_app_store_server_library

See also the "Receipt Usage" example in the README for each language of the library, e.g.:

https://github.com/apple/app-store-server-library-java?tab=readme-ov-file#receipt-usage

In this sense, you can see the direct replacement for verifyReceipt: instead of sending the receipt to verifyReceipt, use it to extract a transactionId, then send that transactionId to the App Store Server API to retrieve the same data and much more.

Understood, but this brings us back to the initial issue. I want to distinguish whether the transaction_id is from sandbox data or live data. I reviewed the receipt-data documentation here and found no field related to the environment. As a result, I have no way to differentiate the environment corresponding to the transaction_id. Consequently, I do not know whether to call https://api.storekit.itunes.apple.com/inApps/v1/transactions/{transactionId} or https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/{transactionId} to retrieve the data.

@aababc For that issue, please reference my initial reply. You should be calling production first and falling back to sandbox if you receive the noted error.

Migrate from the verifyReceipt API to the new
 
 
Q