Google 錢包票證開發流程
Google Wallet API 提供了針對特定用途最佳化的預先定義票證類型,例如禮物卡、登機證和活動票券等等。此外,通用票證類型也適用於不提供特定票證類型的使用。
本文將介紹使用 Google Wallet API 建立及核發票證的基本步驟。有很多方法可以完成下方詳述的部分步驟,但大致來說,所有票證類型都是按照相同的基本開發流程建立。
如需建立票證的詳細逐步操作說明,請參閱網頁、電子郵件和簡訊或 Android 應用程式適用的指南。
![](https://developers.google.cn/static/wallet/images/ticket-class.png?authuser=3&%3Bhl=zh-tw&hl=zh-tw)
1. 建立票證類別
用途
票證類別可定義多張票證通用的一組屬性,類似於範本。舉例來說,如果您要發布活動票券,票證類別會定義所有票券的相同欄位,例如活動名稱、日期和時間。
您核發的每張票證都必須參照一個票證類別。另外,您也必須為已建立的每個票證類別指派專屬 ID,以便在建立票證時用於參照。
操作方式
票證類別是以 JSON 格式定義,您可以透過 Google Wallet REST API、Android SDK 或 Google 錢包商家主控台建立。
顯示票證類別範例
{ "id": "ISSUER_ID.EVENT_CLASS_ID", "issuerName": "[TEST ONLY] Heraldic Event", "localizedIssuerName": { "defaultValue": { "language": "en-US", "value": "[TEST ONLY] Heraldic Event" } }, "logo": { "sourceUri": { "uri": "https://images.unsplash.com/photo-1475721027785-f74eccf877e2?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=660&h=660" }, "contentDescription": { "defaultValue": { "language": "en-US", "value": "LOGO_IMAGE_DESCRIPTION" } } }, "eventName": { "defaultValue": { "language": "en-US", "value": "Google Live" } }, "venue": { "name": { "defaultValue": { "language": "en-US", "value": "Shoreline Amphitheater" } }, "address": { "defaultValue": { "language": "en-US", "value": "ADDRESS_OF_THE_VENUE" } } }, "dateTime": { "start": "2023-04-12T11:30" }, "reviewStatus": "UNDER_REVIEW", "hexBackgroundColor": "#264750", "heroImage": { "sourceUri": { "uri": "https://images.unsplash.com/photo-1501281668745-f7f57925c3b4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1032&h=336" }, "contentDescription": { "defaultValue": { "language": "en-US", "value": "HERO_IMAGE_DESCRIPTION" } } } }
![](https://developers.google.cn/static/wallet/images/ticket-object.png?authuser=3&%3Bhl=zh-tw&hl=zh-tw)
2. 建立票證物件
用途
票證物件會定義要核發給特定使用者的專屬票證屬性。舉例來說,活動票券的票證物件會定義特定票券的專屬欄位,例如該票券的座位號碼或 QR code。
建立票證物件後,Google Wallet API 會儲存新的票證,並將該票證與您的發卡機構帳戶建立關聯。這張已儲存的票證結合了票證物件的專屬屬性,以及相關聯票證類別的範本屬性。
此外,您還必須為每個票證物件指派一組專屬 ID,以便在核發票證時用於參照。
操作方式
「票證物件」是由 JSON 格式定義,您可以透過 Google Wallet REST API 或 Android SDK 來建立。
顯示票證物件範例
{ "id": "ISSUER_ID.OBJECT_ID", "classId": "ISSUER_ID.EVENT_CLASS_ID", "state": "ACTIVE", "seatInfo": { "seat": { "defaultValue": { "language": "en-us", "value": "5" } }, "row": { "defaultValue": { "language": "en-us", "value": "G" } }, "section": { "defaultValue": { "language": "en-us", "value": "40" } }, "gate": { "defaultValue": { "language": "en-us", "value": "3A" } } }, "barcode": { "type": "QR_CODE", "value": "BARCODE_VALUE", "alternateText": "" } }
![](https://developers.google.cn/static/wallet/images/ticket-jwt.png?authuser=3&%3Bhl=zh-tw&hl=zh-tw)
3. 以 JSON Web Token (JWT) 為票證編碼
用途
如要向使用者發出票證,票證類別和票證物件必須以 JSON Web Token (JWT) 進行編碼。JWT 格式是代表雙方之間的憑證附加資訊的常見開放標準。如果是透過 Google Wallet API 發出票證,系統會使用 JWT 傳送憑證附加資訊,讓使用者有權存取與核發機構帳戶相關的特定票證。
將 JWT 傳送至 Google Wallet API 時,系統會使用編碼資料來識別特定票證並核發給使用者。如果票證已核發,這項資料也會讓 Google Wallet API 能夠辨識重複的票證,因此不會重複將票證加入使用者的 Google 錢包中。
操作方式
JWT 是根據 JWT 規格以 JSON 格式定義。如要透過 Google Wallet API 定義核發票證的 JWT,請在 JWT 的 payload
屬性中提供要核發的票證相關資訊。
顯示 JWT 範例
{ "iss": "issuer@example.com", "aud": "google", "typ": "savetowallet", "iat": 1696877738, "origins": [ "www.example.com" ], "payload": { "eventTicketObjects": [ { "id": "ISSUER_ID.LOYALTY_OBJECT_SUFFIX" } ] } }
![](https://developers.google.cn/static/wallet/images/ticket-jwt-signed.png?authuser=3&%3Bhl=zh-tw&hl=zh-tw)
4. 使用憑證簽署 JWT
用途
凡是為了核發票證而傳送至 Google Wallet API 的 JWT,都必須使用先前在 Google 電子錢包商家主控台提供的憑證簽署。簽署功能會使用您的憑證對 JWT 進行加密,藉此確保票證安全無虞,並讓 Google Wallet API 能驗證編碼詳細資料是否有效,並與您的核發者帳戶相關聯。
操作方式
Google Wallet 用戶端程式庫和 Android SDK 可讓您輕鬆簽署 JWT。您也可以使用許多開放原始碼程式庫,處理複雜的程式碼簽署工作供您選用。
針對使用 Google Wallet REST API 發出票證的使用者,JWT 是以 Google Cloud 服務帳戶金鑰簽署。對於使用 Google 錢包 Android SDK 的使用者,SDK 會自動使用應用程式簽署憑證的 SHA-1 指紋簽署 JWT。
為了保護您的憑證,請務必在您的伺服器上簽署 JWT,或是在應用程式中使用 Google Wallet Android SDK。
顯示程式碼範例簽署
Java
// Create the JWT as a HashMap object HashMapclaims = new HashMap (); claims.put("iss", ((ServiceAccountCredentials) credentials).getClientEmail()); claims.put("aud", "google"); claims.put("origins", Arrays.asList("www.example.com")); claims.put("typ", "savetowallet"); // Create the Google Wallet payload and add to the JWT HashMap payload = new HashMap (); payload.put("eventTicketObjects", Arrays.asList(newObject)); claims.put("payload", payload); // Google Cloud service account credentials are used to sign the JWT Algorithm algorithm = Algorithm.RSA256( null, (RSAPrivateKey) ((ServiceAccountCredentials) credentials).getPrivateKey()); String token = JWT.create().withPayload(claims).sign(algorithm);
Node.JS
// Create the JWT claims let claims = { iss: this.credentials.client_email, aud: 'google', origins: ['www.example.com'], typ: 'savetowallet', payload: { eventTicketObjects: [newObject] }, }; // The service account credentials are used to sign the JWT let token = jwt.sign(claims, this.credentials.private_key, { algorithm: 'RS256' });
Python
# Create the JWT claims claims = { 'iss': self.credentials.service_account_email, 'aud': 'google', 'origins': ['www.example.com'], 'typ': 'savetowallet', 'payload': { # The listed classes and objects will be created 'eventTicketObjects': [new_object] } } # The service account credentials are used to sign the JWT signer = crypt.RSASigner.from_service_account_file(self.key_file_path) token = jwt.encode(signer, claims).decode('utf-8')
![](https://developers.google.cn/static/wallet/images/ticket-add-to-wallet.png?authuser=3&%3Bhl=zh-tw&hl=zh-tw)
5. 使用「新增至 Google 錢包」按鈕或連結核發票證
用途
建立已簽署的 JWT 後,您就可以將票證核發給 Google 電子錢包使用者!系統會向使用者顯示「新增至 Google 錢包」按鈕或連結,當使用者點選按鈕或超連結時,系統會將已簽署的 JWT 傳送至 Google Wallet API,並使用已儲存的憑證解密。JWT 簽章通過驗證後,系統就會將票證發給使用者,以便儲存在他們的 Google 錢包中。
操作方式
如要為 Android 應用程式建立「新增至 Google 錢包」按鈕,請使用 Google 錢包 Android SDK,當中提供了產生按鈕的方法。若是其他平台 (包括網頁、電子郵件和簡訊),請以 https://pay.google.com/gp/v/save/<signed_jwt>
格式建立超連結。請盡可能將這個連結顯示為「新增至 Google 錢包」按鈕,
如要進一步瞭解如何使用「新增至 Google 錢包」按鈕,請參閱 Google Wallet API 品牌宣傳指南
顯示範例程式碼
超連結
https://pay.google.com/gp/v/save/<signed_jwt>
Android SDK
private lateinit var walletClient: PayClient private val addToGoogleWalletRequestCode = 1000 private lateinit var addToGoogleWalletButton: View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) walletClient = Pay.getClient(this) addToGoogleWalletButton.setOnClickListener { walletClient.savePasses(newObjectJson, this, addToGoogleWalletRequestCode) } }
![](https://developers.google.cn/static/wallet/images/ticket-enduser-view.png?authuser=3&%3Bhl=zh-tw&hl=zh-tw)
6. 在 Google 錢包中查看
使用者儲存核發的票證後,票證就會與先前儲存的其他票證一併顯示在他們的 Google 錢包應用程式中。
在 JWT 中建立票證物件和票證類別
您可以使用 Google Wallet REST API 或 Android SDK 預先建立票證類別和票證物件。建立票證後,系統會將票證做為 ID 來核發票證。
或者,您也可以將票證類別和票證物件的 JSON 直接嵌入用於核發票證的 JWT,藉此「及時」建立票證類別和票證物件。使用這個方法時,透過「新增至 Google 錢包」按鈕或連結傳送已簽署的 JWT 時,Google Wallet API 會建立「票證類別」和「票證物件」。
例如,下列 JWT 包含以 payload.eventTicketClasses
和 payload.eventTicketObjects
屬性定義的新票證類別和票證物件。請注意,這些屬性是陣列,因此可接受一或多個票證類別或票證物件。您也可以只在 JWT 中,根據 ID 參照現有票證類別的新票證物件。
顯示 JWT 範例
{ "iss": "issuer@example.com", "aud": "google", "typ": "savetowallet", "iat": 1696877738, "origins": [ "www.example.com" ], "payload": { "eventTicketClasses": [{ "id": "ISSUER_ID.EVENT_CLASS_ID", "issuerName": "[TEST ONLY] Heraldic Event", "localizedIssuerName": { "defaultValue": { "language": "en-US", "value": "[TEST ONLY] Heraldic Event" } }, "logo": { "sourceUri": { "uri": "https://images.unsplash.com/photo-1475721027785-f74eccf877e2?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=660&h=660" }, "contentDescription": { "defaultValue": { "language": "en-US", "value": "LOGO_IMAGE_DESCRIPTION" } } }, "eventName": { "defaultValue": { "language": "en-US", "value": "Google Live" } }, "venue": { "name": { "defaultValue": { "language": "en-US", "value": "Shoreline Amphitheater" } }, "address": { "defaultValue": { "language": "en-US", "value": "ADDRESS_OF_THE_VENUE" } } }, "dateTime": { "start": "2023-04-12T11:30" }, "reviewStatus": "UNDER_REVIEW", "hexBackgroundColor": "#264750", "heroImage": { "sourceUri": { "uri": "https://images.unsplash.com/photo-1501281668745-f7f57925c3b4?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1032&h=336" }, "contentDescription": { "defaultValue": { "language": "en-US", "value": "HERO_IMAGE_DESCRIPTION" } } } }], "eventTicketObjects": [{ "id": "ISSUER_ID.OBJECT_ID", "classId": "ISSUER_ID.EVENT_CLASS_ID", "state": "ACTIVE", "seatInfo": { "seat": { "defaultValue": { "language": "en-us", "value": "5" } }, "row": { "defaultValue": { "language": "en-us", "value": "G" } }, "section": { "defaultValue": { "language": "en-us", "value": "40" } }, "gate": { "defaultValue": { "language": "en-us", "value": "3A" } } }, "barcode": { "type": "QR_CODE", "value": "BARCODE_VALUE", "alternateText": "" } }] } }