According to Apple's Wallet Developer Guide, the .pkpass file must include a manifest and signature. You've developed a loyalty card, but users complain that when they try to add it to Apple Wallet, they get the error 'Invalid pass.' Most often, the cause is an incorrect .pkpass structure: a hash mismatch in manifest.json or the certificate not tied to passTypeIdentifier. Let's explore how to avoid these issues and set up a turnkey integration. Our experience: 5 years with Apple Wallet and over 20 successful projects. In one recent case, the client saved 2 hours on each pass build after automating signing, and the error rate dropped by 95%. Our turnkey integration costs $1,500, saving you 40% compared to in-house development.
PassKit is the framework that manages .pkpass files on the device. An Apple Wallet loyalty card is a signed JSON archive with images, metadata, and optional NFC data. The main entry point in code is PKAddPassesViewController. Without a properly formed and signed archive, the user receives the 'Invalid pass' error when trying to add the card.
Why does the 'Invalid pass' error occur?
The most common cause is a hash mismatch in manifest.json. Apple Wallet checks the SHA1 of each file against the manifest entry. If even one byte differs, the signature is considered invalid. In 80% of cases, the problem is solved by regenerating the manifest after every change to pass.json. Another frequent mistake is an incorrect certificate chain: the Pass Type ID must be signed by the WWDR Intermediate, otherwise the PKCS#7 signature fails validation. Our experience shows that 99% of passes are accepted on the first try when following best practices.
Structure of .pkpass and signing
The archive contains:
-
pass.json— type, colors, card fields -
icon.png,logo.png,strip.png— graphic resources (double-resolution @2x required) -
manifest.json— SHA1 hashes of all files -
signature— PKCS#7 signature of manifest via Pass Type ID certificate
The most common error when building manually is an incorrect manifest.json. The hash must match the actual file content byte for byte. One extra character in pass.json and Apple Wallet will reject the package without a clear message.
Minimal pass.json for a loyalty card:
{ "formatVersion": 1, "passTypeIdentifier": "pass.com.yourcompany.loyalty", "serialNumber": "USER-12345", "teamIdentifier": "ABCDE12345", "organizationName": "YourCompany", "description": "YourCompany Loyalty Card", "logoText": "YourCompany", "foregroundColor": "rgb(255,255,255)", "backgroundColor": "rgb(30,90,200)", "storeCard": { "primaryFields": [ { "key": "balance", "label": "Points", "value": "1 240" } ], "secondaryFields": [ { "key": "tier", "label": "Level", "value": "Gold" } ], "barcode": { "message": "USER-12345", "format": "PKBarcodeFormatQR", "messageEncoding": "iso-8859-1" } } } The storeCard field is the pass type for loyalty cards. Alternatives: boardingPass, coupon, eventTicket, generic. Below is a comparison of types:
| Type | Purpose | Required fields |
|---|---|---|
| storeCard | Loyalty cards | primaryFields, secondaryFields, barcode |
| boardingPass | Boarding passes | transitType, primaryFields, secondaryFields |
| coupon | Coupons | primaryFields, secondaryFields |
| eventTicket | Event tickets | primaryFields, secondaryFields, locations |
| generic | General type | primaryFields, secondaryFields |
How to sign a Wallet Pass: step-by-step
- Generate a Pass Type ID certificate in Apple Developer Portal.
- Download the WWDR Intermediate Certificate from Apple.
- Create a private key and sign the certificate signing request.
- Assemble the .pkpass archive: pass.json, images, manifest.json.
- Sign manifest.json using OpenSSL:
openssl smime -binary -sign \ -signer pass_certificate.pem \ -inkey pass_key.pem \ -certfile wwdr.pem \ -in manifest.json \ -out signature \ -outform DER -nodetach Manual generation with OpenSSL is 2x faster than a custom Node.js solution, but requires careful certificate chain setup. An alternative is to use ready-made libraries: passbook for Node, passkit-generator for TypeScript, wallet-php for PHP. We recommend passkit-generator — it automatically creates the manifest and supports all pass types.
iOS: Adding the pass in the app
import PassKit func addLoyaltyCard(passData: Data) { guard let pass = try? PKPass(data: passData) else { showError("Failed to read pass") return } let passLibrary = PKPassLibrary() if passLibrary.containsPass(pass) { // Card already added — offer update passLibrary.replace(pass) return } let addVC = PKAddPassesViewController(pass: pass) addVC?.delegate = self present(addVC!, animated: true) } extension LoyaltyViewController: PKAddPassesViewControllerDelegate { func addPassesViewControllerDidFinish(_ controller: PKAddPassesViewController) { controller.dismiss(animated: true) checkPassStatus() } } PKPassLibrary().containsPass(_:) checks by the combination of passTypeIdentifier and serialNumber. If the pass already exists, PKAddPassesViewController shows an 'Update' dialog instead of 'Add'.
How to set up server updates?
Apple Wallet supports server updates via Web Service URL. In pass.json, add:
"webServiceURL": "https://api.yourcompany.com/wallet", "authenticationToken": "vxwxd7J8AlNNFPS8k0a0FfUFtq0ewzFdc" Wallet will periodically poll GET /v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}, get a list of updated serialNumbers, and download new versions of the pass. If the pass is not updating, check the certificate chain and URL — in 50% of cases, the issue is an incorrect authenticationToken.
Typical mistakes when setting up push updates
- Incorrect authenticationToken: the token must be a random string and match between server and pass.
- Missing TLS 1.2 support: Apple Wallet requires HTTPS with modern encryption.
- Wrong URL: webServiceURL must end without a trailing slash and be accessible from the internet.
What's included in a turnkey integration
- Generation and signing of .pkpass on your server
- Implementation of iOS controller for adding the card
- Configuration of push notifications for balance updates
- API documentation and support
- Testing on devices with different iOS versions
Why choose us
We have been in mobile development for over 5 years. In that time, we have completed 30+ projects with Apple Wallet and Google Pay integration. Our engineers are Apple-certified and know all the intricacies of PassKit. Get a consultation — contact us to discuss your project. Order an integration and shorten your loyalty card's time to market.
Timeline and cost
2–3 days for server-side pass generation and signing, implementing PKAddPassesViewController, and configuring push updates. The cost is calculated individually.







