Hi,
I'm using AppTransaction.originalAppVersion to detect whether a user originally purchased the app under the old paid model, so I can automatically unlock the app for them as a courtesy when migrating to freemium.
Background
On iOS, originalAppVersion returns CFBundleVersion (the build number). When I transitioned the app from paid (v1.x) to freemium (v2.0), I defined a numeric threshold for CFBundleVersion to distinguish legacy purchasers from new users:
Build number below the threshold → v1.x purchase → auto-unlock
Build number at or above the threshold → v2.0+ install → requires IAP
In Production, originalAppVersion correctly returns the actual build number, and the comparison works as intended.
Detection logic (simplified)
// Determine environment via receipt URL
func detectStoreEnvironment() -> String {
if let url = Bundle.main.appStoreReceiptURL,
url.lastPathComponent == "sandboxReceipt" {
return "Sandbox"
}
return "Production"
}
// Legacy check using numeric comparison
static func isLegacyPaidUser(version: String, threshold: String) -> Bool {
guard !version.isEmpty else { return false }
return version.compare(threshold, options: .numeric) == .orderedAscending
}
// In checkLegacyPurchase():
let version = appTransaction.originalAppVersion
let isLegacy = isLegacyPaidUser(version: version, threshold: legacyBuildNumberThreshold)
let env = detectStoreEnvironment()
let shouldAutoUnlock = isLegacy && env != "Sandbox"
The problem
I know that in the Sandbox environment, originalAppVersion always returns "1.0" — this is mentioned in the AppTransaction documentation. My code already suppresses the auto-unlock for Sandbox (env != "Sandbox").
However, it appears that the App Review environment also returns "1.0" for originalAppVersion. Because the receipt URL path component is "receipt" (not "sandboxReceipt"), my environment detection classifies it as "Production" — so the Sandbox suppression doesn't apply. The reviewer is incorrectly identified as a legacy paid user and the app is unlocked without a purchase.
This caused our v2.0 submission to be rejected under Guideline 2.1a.
Questions
Is it documented that the App Review environment returns "1.0" for AppTransaction.originalAppVersion, similar to Sandbox?
Is there a reliable way to detect the App Review environment specifically — separate from both Sandbox and Production? For example, does the receipt URL differ, or is there another API?
Is using originalAppVersion for legacy paid-user detection a supported pattern? If so, what is the recommended approach to handle the App Review case?
Any guidance would be greatly appreciated. Thank you.