Реализация push-обновлений Wallet Pass в мобильном приложении
Apple Wallet Pass — это не статичная карточка. Loyalt-программа, авиабилет, купон — данные меняются в реальном времени, и пользователь должен видеть актуальную информацию без переустановки приложения. Именно для этого Apple реализовала механизм серверных push-обновлений для PassKit.
Как работает механизм обновлений
Архитектурно схема выглядит так: ваш сервер регистрирует устройство через PassKit Web Service API, хранит пару deviceLibraryIdentifier + pushToken, и при изменении данных отправляет push через APNs на этот токен. iOS «просыпается», делает GET-запрос к серверу за обновлённым .pkpass файлом, и карточка обновляется без участия пользователя.
Реализация разбивается на две части — серверную и клиентскую, причём клиентская почти нулевая: PassKit сам обрабатывает весь цикл регистрации, если сервер реализует протокол корректно.
Серверный протокол PassKit Web Service
Сервер обязан поднять четыре эндпоинта:
-
POST /v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}/{serialNumber}— регистрация устройства -
DELETE /v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}/{serialNumber}— дерегистрация -
GET /v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}?passesUpdatedSince={tag}— список обновлённых passes -
GET /v1/passes/{passTypeIdentifier}/{serialNumber}— скачивание актуального.pkpass
Самая частая ошибка — неверный HTTP-статус. Apple PassKit крайне чувствителен: 200 с пустым телом на DELETE → iOS ломает дерегистрацию. Нужно 204 No Content. На GET со списком обновлений без изменений — строго 204, не 200 [].
// Пример структуры ответа на GET /registrations
{
"serialNumbers": ["ABC123", "DEF456"],
"lastUpdated": "1711234567"
}
Поле lastUpdated — это UNIX timestamp строкой. iOS передаёт его обратно в passesUpdatedSince при следующем запросе. Если вернуть timestamp в неправильном формате, устройство будет постоянно запрашивать все passes, игнорируя инкрементальную логику.
APNs push для обновления
Push для Wallet — нестандартный. Payload минимален:
{
"aps": {}
}
Именно так — пустой aps. Никакого alert, badge, sound. iOS при получении такого push-а молча идёт к серверу за обновлениями. Отправлять нужно через APNs с apns-topic равным passTypeIdentifier приложения (формат: pass.com.yourcompany.appname), не bundleIdentifier.
Сертификат для PassKit отдельный — это Pass Type ID Certificate из Apple Developer Portal, не обычный APN-сертификат приложения. Путают их регулярно, в результате APNs принимает запрос, но push не доставляется.
# Пример отправки через httpx (Python, APNs HTTP/2)
headers = {
"apns-topic": "pass.com.example.loyalty",
"apns-push-type": "background",
"apns-priority": "5",
"authorization": f"bearer {jwt_token}"
}
payload = json.dumps({"aps": {}})
response = await client.post(
f"https://api.push.apple.com/3/device/{push_token}",
content=payload,
headers=headers
)
apns-priority: 5 — обязателен для фоновых push. Приоритет 10 для Wallet не работает так, как ожидается.
Подпись .pkpass
Каждый .pkpass — ZIP-архив с файлом manifest.json (SHA-1 хеши всех файлов) и signature (PKCS#7 detached signature). При обновлении Pass нужно пересчитать манифест и пересоздать подпись. Использование старой подписи с новыми данными → iOS молча игнорирует файл.
Генерация подписи через openssl:
openssl smime -binary -sign \
-certfile AppleWWDRCA.pem \
-signer passcertificate.pem \
-inkey passkey.pem \
-in manifest.json \
-out signature \
-outform DER
Библиотека signpass от Apple удобна для тестирования, но в продакшене лучше реализовать подпись нативно на сервере — без внешних бинарников.
Тестирование цикла обновлений
Инструмент PassKit Companion App (macOS) позволяет симулировать регистрацию и проверять эндпоинты напрямую. Для дебага серверных запросов — включить WKWebsiteDataStore логирование на iOS-симуляторе не получится, зато Charles Proxy на реальном устройстве перехватывает все запросы PassKit полностью.
Типичный цикл отладки: добавить pass через приложение → в Charles проверить POST-регистрацию → дёрнуть push вручную через curl → убедиться, что iOS сделал GET за обновлённым pass → проверить UI карточки.
Процесс работы
Аудит текущей серверной инфраструктуры или реализация с нуля: Pass Type ID, серверные эндпоинты, хранение pushToken.
Настройка APNs с Pass Type ID Certificate, реализация отправки push при изменении данных.
Генерация и подпись .pkpass на сервере, настройка инкрементальных обновлений.
Тестирование полного цикла: регистрация → изменение данных → push → обновление карточки.
Ориентиры по срокам
Если серверная инфраструктура уже есть и нужно только добавить поддержку PassKit Web Service — 3–5 дней. Реализация с нуля, включая генерацию pass-файлов и серверный бэкенд — от 1 до 2 недель в зависимости от сложности логики обновлений.







