Verify the authenticity and integrity of an App Store receipt.
Runs four checks in parallel: certificate chain, signature, hash, and metadata.
let result = await receipt.validate()
switch result {
case .valid:
break
case .invalid(let error):
print(error)
}The validator auto-selects the Apple root certificate based on the receipt's environment.
Build your own with @VerifierBuilder:
let validator = ReceiptValidator {
X509ChainVerifier(rootCertificates: [rootCert])
SignatureVerifier()
HashVerifier(deviceIdentifier: deviceId)
MetaVerifier(
appVersionProvider: { Bundle.main.appVersion },
bundleIdentifierProvider: { Bundle.main.bundleIdentifier }
)
}
let result = await validator.validate(receipt)Include or exclude any verifier. They run in parallel via TaskGroup — if any fails, the rest are cancelled.
| Verifier | What it checks |
|---|---|
X509ChainVerifier |
Certificate chain against Apple root (swift-certificates) |
SecChainVerifier |
Certificate chain via Security.framework |
SignatureVerifier |
PKCS#7 signature (swift-crypto) |
SecSignatureVerifier |
PKCS#7 signature via Security.framework |
HashVerifier |
SHA-1 hash: device ID + opaque value + bundle ID |
MetaVerifier |
Bundle identifier and app version match |
X509/Signature variants are async. Sec variants use Security.framework and work in blocking contexts.
ReceiptValidatorError— invalid structure, missing root certificate, missing device identifierChainVerificationError— certificate data invalid, chain validation failed, revocation check failedSignatureVerificationError— invalid key, invalid signatureHashVerificationError— missing device identifier, hash mismatchMetaVerificationError— bundle identifier mismatch, version mismatch
See doc:Architecture for how the validation layer fits into the overall design.