Apple Wallet Integration for Coupons and Discounts in Mobile Apps
A coupon in Apple Wallet is a coupon-type .pkpass. Structurally similar to storeCard, but the coupon type provides a special visual style: perforated bottom edge of the card. This is what makes a coupon visually appear as a coupon rather than just a regular card.
Coupon Structure
{
"formatVersion": 1,
"passTypeIdentifier": "pass.com.yourshop.coupon",
"serialNumber": "COUPON-SUMMER2024-USER789",
"teamIdentifier": "ABCDE12345",
"organizationName": "YourShop",
"description": "20% discount on summer collection",
"foregroundColor": "rgb(255,255,255)",
"backgroundColor": "rgb(200,50,50)",
"coupon": {
"primaryFields": [
{ "key": "offer", "value": "−20%", "label": "Discount" }
],
"secondaryFields": [
{
"key": "expires",
"value": "2024-08-31T23:59+03:00",
"label": "Valid Until",
"dateStyle": "PKDateStyleShort",
"timeStyle": "PKDateStyleNone"
}
],
"auxiliaryFields": [
{ "key": "conditions", "value": "Minimum purchase 3,000 rubles", "label": "Terms" }
],
"barcode": {
"message": "COUPON-SUMMER2024-USER789",
"format": "PKBarcodeFormatCode128",
"messageEncoding": "iso-8859-1"
}
},
"expirationDate": "2024-08-31T23:59+03:00",
"voided": false
}
The voided: true field — Wallet visually "strikes through" the coupon and moves it to the archive. Set via push-update after redemption.
Coupon Redemption
Redemption logic is entirely server-side. The QR/barcode contains serialNumber or a unique token. The cashier scans the code, and the server:
- Checks
serialNumberin the database - Confirms the coupon hasn't expired and hasn't been redeemed
- Applies the discount
- Sets flag
used = true - Sends APN push → device downloads updated
.pkpasswithvoided: true
There may be a 10–30 second delay between step 4 and 5 — this is normal. The key point is that validation happens on the server, not on the device.
Personalization and Bulk Distribution
Each coupon is a unique .pkpass for a specific user. The server generator receives userId, creates an archive on-the-fly, signs it, and provides a download link. The link can be embedded in email or a push notification.
Link format: https://api.yourshop.com/wallet/coupons/{token}.pkpass
When opened on iOS Safari, it will automatically offer "Add to Wallet" if the MIME type is application/vnd.apple.pkpass.
Geotargeting
A coupon can be tied to retail locations:
"locations": [
{ "latitude": 55.7512, "longitude": 37.6184, "relevantText": "Store on Tverskaya" },
{ "latitude": 59.9386, "longitude": 30.3141, "relevantText": "Store on Nevsky" }
]
Wallet will display a notification with relevantText on the lock screen when a user is near any location (~100 meter radius). Maximum 10 locations per pass.
Batch Distribution: Multiple Coupons in One Request
When distributing coupons to all users in a segment — the generator must work in the background. Synchronous generation of 10,000 .pkpass files will block the API server.
The right approach: job queue. Request POST /campaigns/{id}/distribute queues a task (Sidekiq, Celery, BullMQ), workers generate passes in batches and store links in the database. Users receive a push with "Add to Wallet" button — not immediately, but as generation progresses.
Each generation must use a new serialNumber and new authenticationToken in pass.json. Never distribute identical passes to different users — update systems will conflict.
Timeline
1–3 days: server-side coupon generation, redemption API, push-based invalidation via voided. Pricing is calculated individually.







