In the JWS transaction payload (the one you get from Transaction.currentEntitlements client-side, or from Get Transaction Info server-side), there are two fields that together tell you unambiguously:
offerType: an integer. 1 = introductory offer, 2 = promotional offer, 3 = offer code. Free trials are a form of introductory offer.
offerDiscountType: a string enum. Values are FREE_TRIAL, PAY_AS_YOU_GO, or PAY_UP_FRONT. This is the one you probably actually want. If it equals FREE_TRIAL you're definitively in a trial.
Important caveat: these fields are only populated while the transaction is in the promotional or intro period. Once the trial converts to paid, the subsequent renewal transaction won't have offerType set at all. So you can only detect "currently in free trial" from the currently-active transaction, not by looking at historical renewal transactions.
If you need this server-side (not on device):
Call the App Store Server API Get Transaction Info endpoint for the transactionId in question.
Or parse the signedTransactionInfo JWS from your App Store Server Notifications webhook. The SUBSCRIBED notification with offerDiscountType = FREE_TRIAL on initial subscribe is your signal.
Strong recommendation: use the App Store Server Library (available for Swift, Java, Python, Node, Go). It handles JWS verification and decoding for you, rather than rolling your own JWT parse which is easy to get subtly wrong around certificate chain validation.
WWDC22 "Explore App Store server APIs" and WWDC23 "Meet StoreKit 2" both cover this.
Topic:
App Store Distribution & Marketing
SubTopic:
General
Tags: