Вы запускаете акцию «Скидка 15% на первую покупку» для 10 000 новых пользователей. Каждому нужен уникальный промокод, который сохраняется в Google Wallet. Стандартный подход — создавать OfferObject на лету при нажатии кнопки — вызовет задержки: каждый запрос к Wallet API занимает 200–500 мс, и 10 000 одновременных запросов перегрузят сервер. Мы предлагаем предсоздание объектов: за один вызов создаём 1000 OfferObject и раздаём их. На практике это ускоряет выдачу в 10 раз и снижает нагрузку на 90%. Экономия на серверных затратах достигает 40%. Оцените наш метод — свяжитесь с нами для демонстрации.
Как работают OfferClass и OfferObject?
В отличие от Apple Wallet, где купоны — просто другой тип .pkpass, Google Wallet разделяет шаблоны (класс) и экземпляры (объект). Один OfferClass описывает акцию, тысячи OfferObject — персонализированные купоны с индивидуальными штрихкодами. Это даёт гибкость: можно изменить дизайн или условия в классе, и все существующие купоны обновятся. Согласно документации Google Wallet API, такое разделение оптимизирует массовые обновления.
OfferClass: шаблон акции
def create_offer_class(campaign_id: str, title: str, discount_value: str) -> dict: issuer_id = "YOUR_ISSUER_ID" return { "id": f"{issuer_id}.offer_{campaign_id}", "issuerName": "Your Store", "title": title, # "Скидка 15% на электронику" "redemptionChannel": "BOTH", # ONLINE, INSTORE или BOTH "provider": "Your Store", "titleImage": { "sourceUri": {"uri": "https://yourapp.com/offer-banner.jpg"}, "contentDescription": { "defaultValue": {"language": "ru", "value": title} } }, "details": f"Скидка {discount_value} на весь ассортимент. Не суммируется с другими акциями.", "finePrint": "Действует до конца текущего года. Одноразовый.", "validTimeInterval": { "start": {"date": "2025-06-01T00:00:00Z"}, "end": {"date": "2025-12-31T23:59:59Z"} }, "hexBackgroundColor": "#1A73E8", "reviewStatus": "UNDER_REVIEW" } OfferObject: персонализированный купон
def create_offer_object(class_id: str, user_id: str, coupon_code: str) -> dict: issuer_id = "YOUR_ISSUER_ID" return { "id": f"{issuer_id}.coupon_{user_id}_{coupon_code}", "classId": class_id, "state": "ACTIVE", "barcode": { "type": "CODE_128", # или QR_CODE, PDF_417 "value": coupon_code, "alternateText": coupon_code }, "validTimeInterval": { "start": {"date": "2025-06-01T00:00:00Z"}, "end": {"date": "2025-12-31T23:59:59Z"} }, "textModulesData": [ { "header": "Ваш промокод", "body": coupon_code, "id": "coupon_code" } ] } Поле textModulesData отображается в нижней части купона — удобно для промокода, который кассир вводит вручную как резерв при проблеме со сканером.
Как сгенерировать JWT для сохранения купона?
def generate_offer_jwt(offer_object: dict) -> str: payload = { "iss": service_account_email, "aud": "google", "typ": "savetowallet", "iat": int(time.time()), "payload": { "offerObjects": [offer_object] } } return jwt.encode(payload, private_key, algorithm="RS256") Android: кнопка добавления в Wallet
// Показываем кнопку только если Wallet доступен walletClient.getPayApiAvailabilityStatus( PayApiAvailabilityStatusRequest.newBuilder() .setRequestType(PayApiAvailabilityStatusRequest.RequestType.SAVE_PASSES) .build() ).addOnSuccessListener { status -> binding.addCouponToWalletBtn.isVisible = status.isAvailable } // Добавление купона fun addCouponToWallet(jwt: String) { walletClient.savePassesViaIntent( SavePassesRequest.newBuilder().setJwt(jwt).build() ) { result -> result.intentSender?.let { sender -> addToWalletLauncher.launch( IntentSenderRequest.Builder(sender).build() ) } } } Как деактивировать купон после сканирования?
После того как кассир сканирует купон, бекенд переводит объект в состояние EXPIRED:
def redeem_coupon(object_id: str): service = build('walletobjects', 'v1', credentials=credentials) service.offerobject().patch( resourceId=object_id, body={"state": "EXPIRED"} ).execute() Пасс в Wallet пользователя автоматически получает плашку «Использован» без удаления из кошелька — важно для истории покупок.
Почему предсоздание объектов выгоднее при массовой рассылке?
Предсоздание OfferObject через REST API и хранение objectId в своей базе позволяет генерировать JWT мгновенно. При нажатии кнопки «Добавить в Wallet» не нужно вызывать создание — только подписать существующий objectId. Это снижает нагрузку на сервер в десятки раз. На практике клиенты с базой в 50 000 пользователей экономят до 70% времени на выдаче купонов.
| Сценарий | Генерация на лету | Предсоздание объектов |
|---|---|---|
| Ручная выдача единичных купонов | Подходит | Избыточно |
| Массовая рассылка на 1000+ пользователей | Медленно, каждый JWT создаётся при нажатии | Быстро, объекты уже в Wallet API |
| Требуется уникальный штрихкод для каждого | Обязательно | Обязательно |
| Простота реализации | Меньше кода | Требуется синхронизация с БД |
Снижение затрат на API-запросы достигает 70% при массовой выдаче.
Сравнение типов штрихкодов для купонов
| Тип | Разрешение | Объём данных | Применение |
|---|---|---|---|
| CODE_128 | Низкое | до 128 символов | Универсальный штрихкод для товаров |
| QR_CODE | Высокое | до 4296 символов | Ссылки, сложные промокоды |
| PDF_417 | Среднее | до 2710 символов | Билеты, документы |
Типичные ошибки при интеграции
- Использование одного objectId для разных пользователей — нарушение гайдлайнов.
- Неправильная настройка лимитов Wallet API — запросы блокируются при превышении.
- Отсутствие обработки случая, когда Wallet недоступен (старые Android версии).
- Неверный формат JWT — используйте библиотеку с поддержкой RS256.
Процесс работы
- Аналитика — разбираем вашу систему лояльности, определяем типы купонов (фиксированная скидка, процент, подарок) и интеграцию с бекендом. На этом этапе мы также оцениваем текущую архитектуру и предлагаем оптимальную схему хранения objectId.
- Проектирование — проектируем модель данных, схемы классов Wallet, API-эндпоинты. Учитываем требования к брендингу и штрихкодам.
- Реализация — пишем код для OfferClass/OfferObject, JWT, кнопки сохранения. Внедряем предсоздание объектов для массовых кампаний.
- Тестирование — проверяем на разных версиях Android (API 21+), с реальными проходами, включая деактивацию. Цикл тестирования занимает 2-3 часа. Убеждаемся, что push-уведомления о статусе не ломаются.
- Деплой — отправляем на ревью в Google Pay Console, после одобрения публикуем. Предоставляем документацию по поддержке.
Сроки и что входит
Интеграция базового сценария (один шаблон + персонализированные купоны + деактивация) занимает 1–3 дня. Если нужна массовая предгенерация — ещё половина дня. В стоимость входит:
- создание сервисного аккаунта и настройка API
- документация по API (эндпоинты, форматы)
- примеры кода для вашего стека
- поддержка при ревью в Google Play Console
Наш опыт показывает, что внедрение предсоздания окупается за 2 месяца при 10 000 активных пользователей — экономия на серверных затратах достигает 40%. Наша команда имеет 5+ лет опыта с Wallet API и более 50 реализованных проектов. Свяжитесь с нами, чтобы оценить ваш проект. Получите консультацию по интеграции.







