Promotional offer purchase fails in Sandbox with ASDServerErrorDomain 3902 after payment sheet

Hello,

I’m integrating promotional offers for auto-renewable subscriptions using StoreKit 2.

The offer is displayed correctly, the Apple purchase sheet appears, and I can start the payment flow. The sheet shows the correct discounted price and the end date of the offer. However, after confirming the purchase, an alert appears saying “Unable to Purchase - Contact the developer for more information”

When dismissing the alert, Xcode logs the following:

Purchase did not return a transaction:
Error Domain=ASDServerErrorDomain Code=3902
"No se ha podido realizar la compra"
UserInfo={
    NSLocalizedFailureReason=No se ha podido realizar la compra,
    client-environment-type=Sandbox,
    AMSServerErrorCode=3902,
    storefront-country-code=ESP
}

Test environment:

  • App installed from Xcode on a real iPhone
  • Logged in with a Sandbox Apple ID
  • Using StoreKit 2
  • Promotional offer applied using:
Product.PurchaseOption.promotionalOffer(_:compactJWS:)

On the server side, I generate the promotional offer signature exactly as described in Apple’s documentation: https://developer.apple.com/documentation/storekit/generating-a-signature-for-promotional-offers

  • The signature is generated using a Subscription Key
  • Signed with ECDSA + SHA256
  • Uses the correct invisible separator (U+2063)
  • The signature is validated locally using the derived public key and verifies correctly

The sandbox user has had previous subscriptions, which is why this promotional offer is eligible and shown.

Given that:

  • The offer is displayed correctly
  • The purchase sheet shows the discounted price and duration
  • The signature validates locally
  • The error occurs only after confirming the purchase

My question is:

Is this a known limitation or issue with promotional offers in the Sandbox environment? Should promotional offers be tested exclusively via TestFlight instead of Sandbox?

Any clarification would be greatly appreciated.

Thank you!

Answered by App Store Commerce Engineer in 869604022

We recommend using https://developer.apple.com/documentation/appstoreserverapi/simplifying-your-implementation-by-using-the-app-store-server-library to create the promotional offer signatures, which can help fix issues of malformed signatures.

Accepted Answer

We recommend using https://developer.apple.com/documentation/appstoreserverapi/simplifying-your-implementation-by-using-the-app-store-server-library to create the promotional offer signatures, which can help fix issues of malformed signatures.

Thanks for the guidance!

The issue was indeed related to a malformed promotional offer signature. I was generating the “classic” promotional offer signature (string + ES256) instead of the Promotional Offer V2 JWS that StoreKit expects.

Following the recommendation to use the App Store Server Library as a reference, I updated my backend implementation to generate a proper JWS (ES256) with:

  • aud = "promotional-offer"
  • iss = issuerId
  • bid = bundleId
  • iat and nonce
  • productId and offerIdentifier (and optionally transactionId)
  • Header including typ: "JWT" and the correct kid

After generating a fully compliant V2 JWS and passing it to promotionalOffer(_:compactJWS:), the purchase works correctly in Sandbox on a real device.

Thanks again for pointing me in the right direction — using the official library as a reference was the key.

Promotional offer purchase fails in Sandbox with ASDServerErrorDomain 3902 after payment sheet
 
 
Q