Интеграция Google Wallet для пропусков в мобильном приложении
Пропуск в Google Wallet — это GenericObject поверх GenericClass. Generic — универсальный тип для всего, что не является билетом, посадочным талоном или купоном: пропуск на парковку, членская карта, карта лояльности, абонемент, рабочий бейдж. В API это самый гибкий тип: структура полей задаётся произвольно через textModulesData, linksModuleData и imageModulesData.
Структура GenericObject
def create_access_pass(class_id: str, holder_name: str,
pass_id: str, access_level: str) -> dict:
issuer_id = "YOUR_ISSUER_ID"
return {
"id": f"{issuer_id}.pass_{pass_id}",
"classId": class_id,
"state": "ACTIVE",
"cardTitle": {
"defaultValue": {"language": "ru", "value": "Пропуск сотрудника"}
},
"subheader": {
"defaultValue": {"language": "ru", "value": "Уровень доступа"}
},
"header": {
"defaultValue": {"language": "ru", "value": access_level} # "A1 — все зоны"
},
"textModulesData": [
{
"header": "Владелец",
"body": holder_name,
"id": "holder"
},
{
"header": "ID пропуска",
"body": pass_id,
"id": "pass_id"
},
{
"header": "Действителен до",
"body": "31.12.2024",
"id": "valid_until"
}
],
"barcode": {
"type": "QR_CODE",
"value": pass_id,
"alternateText": pass_id[:8].upper()
},
"heroImage": {
"sourceUri": {
"uri": "https://yourcompany.com/office-photo.jpg"
}
},
"validTimeInterval": {
"start": {"date": "2024-01-01T00:00:00Z"},
"end": {"date": "2024-12-31T23:59:59Z"}
},
"notifications": {
"expiryNotification": {
"enableNotification": True
}
}
}
notifications.expiryNotification — Google Wallet автоматически уведомит пользователя за 3 дня до истечения пропуска. Никакой дополнительной серверной логики не требуется.
GenericClass: минимальная конфигурация
def create_generic_class(class_suffix: str) -> dict:
issuer_id = "YOUR_ISSUER_ID"
return {
"id": f"{issuer_id}.{class_suffix}",
"issuerName": "Your Company",
"reviewStatus": "UNDER_REVIEW",
"enableSmartTap": False, # True только если есть NFC-ридеры
"multipleDevicesAndHoldersAllowedStatus": "ONE_USER_ONE_DEVICE"
}
ONE_USER_ONE_DEVICE — пропуск нельзя передать другому пользователю или сохранить на нескольких устройствах одного аккаунта. Для корпоративных пропусков это правильное ограничение.
Создание класса через REST API перед первым JWT
Класс нужно создать один раз через API — в JWT можно только встраивать объект, класс через JWT не создаётся:
from googleapiclient.discovery import build
from google.oauth2 import service_account
def ensure_generic_class_exists(class_data: dict) -> bool:
creds = service_account.Credentials.from_service_account_file(
'service-account.json',
scopes=['https://www.googleapis.com/auth/wallet_object.issuer']
)
service = build('walletobjects', 'v1', credentials=creds)
try:
service.genericclass().get(resourceId=class_data["id"]).execute()
return True # уже существует
except HttpError as e:
if e.resp.status == 404:
service.genericclass().insert(body=class_data).execute()
return True
return False
Android: полный flow
class PassActivity : AppCompatActivity() {
private val walletClient by lazy {
Wallet.getWalletClient(this, WalletOptions.Builder()
.setEnvironment(WalletConstants.ENVIRONMENT_PRODUCTION)
.build())
}
private val savePassLauncher = registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
) { result ->
when (result.resultCode) {
RESULT_OK -> {
analytics.track("wallet_pass_added")
binding.addToWalletBtn.text = "Уже в Wallet"
binding.addToWalletBtn.isEnabled = false
}
RESULT_CANCELED -> Unit
}
}
fun onAddToWalletClicked() {
viewModel.generatePassJwt().observe(this) { jwt ->
walletClient.savePassesViaIntent(
SavePassesRequest.newBuilder().setJwt(jwt).build()
) { result ->
result.intentSender?.let {
savePassLauncher.launch(IntentSenderRequest.Builder(it).build())
} ?: run {
// Уже добавлен
binding.addToWalletBtn.isEnabled = false
}
}
}
}
}
Обновление пропуска (смена уровня доступа)
def update_pass_access_level(object_id: str, new_level: str):
service = build('walletobjects', 'v1', credentials=creds)
service.genericobject().patch(
resourceId=object_id,
body={
"header": {
"defaultValue": {"language": "ru", "value": new_level}
}
}
).execute()
Изменения отображаются в Wallet пользователя в течение нескольких минут — без переиздания пропуска.
Сроки
1–3 дня. Базовый пропуск с QR-кодом и автоуведомлением об истечении — 1,5 дня. Обновления через API + ограничения на перенос между устройствами — 2–3 дня. Стоимость рассчитывается индивидуально.







