App Store Server Notifications and API Client - Toggling Sandbox vs Production Environment

The documentation mentions the following:


Verify your receipt first with the production URL; then verify with the sandbox URL if you receive a 21007 status code. This approach ensures you don’t have to switch between URLs while your app is in testing, in review by App Review, or live in the App Store.


This way, you can use one server environment to handle both Sandbox and Production environments. It is necessary to pass App Review.

However, I'm not manually hitting these URLs - I'm using Apple's libraries.

Specifically, the environment is used in SignedDataVerifier and AppStoreServerAPIClient.

(I can't link to these because, for some reason, the domain apple.github.io is not allowed. The documentation for these is only found there. You can find it quickly by searching these terms and the domain.)

Here is how SignedDataVerifier is being used:

const verifier = new SignedDataVerifier(
    appleRootCertificates,
    APPLE_ENABLE_ONLINE_CHECKS,
    APPLE_ENVIRONMENT,
    APPLE_BUNDLE_ID,
    APPLE_APP_ID
)

const verifiedNotification: ResponseBodyV2DecodedPayload = await verifier.verifyAndDecodeNotification(signedPayload)

if (!verifiedNotification)
{
    // Failure
    return
}

Here is how AppStoreServerAPIClient is being used:

const appStoreServerAPIClient = new AppStoreServerAPIClient(
    SIGNING_KEY,
    APPLE_IAP_KEY_ID,
    APPLE_IAP_ISSUER_ID,
    APPLE_BUNDLE_ID,
    APPLE_ENVIRONMENT
)

const statusResponse: StatusResponse = await appStoreServerAPIClient.getAllSubscriptionStatuses(originalTransactionId, [Status.ACTIVE])

In the source code for SignedDataVerifier.verifyAndDecodeNotification, I can see that it throws a VerificationException(VerificationStatus.INVALID_ENVIRONMENT) error .

So for SignedDataVerifier is it as simple as wrapping my code in a try/catch and checking that the error's status code is 21007? I'm unsure about this because if you scroll to the bottom of the linked source code file, you can see the enumeration VerificationStatus, but it's unclear if this member has a value of 21007.

The source code for AppStoreServerAPIClient only says that it throws an APIException if a response could not be processed, so I'm not too sure about how to handle this one.

Verify your receipt first with the production URL; then verify with the sandbox URL if you receive a 21007 status code. This approach ensures you don’t have to switch between URLs while your app is in testing, in review by App Review, or live in the App Store.

This only applies for users of the deprecated original StoreKit and when you don't know the environment. Here, you already know the environment and so can directly use the correct environment. 21007 comes from the Verify Receipt documentation which is for a deprecated endpoint and should not be used for apps using StoreKit 2.

For your Sandbox App Store Server Notification URL, you would directly pass Environment.SANDBOX into your verifier, same with the production URL and Environment.PRODUCTION. For the App Store Server API, you would have the environment passed into the client and would use the corresponding env for the verifier. For signed JWS from the client, you should be passing the expected environment along with the signed JWS and directly validating that matches.

App Store Server Notifications and API Client - Toggling Sandbox vs Production Environment
 
 
Q