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.
Topic:
App & System Services
SubTopic:
StoreKit
Tags: