Google 錢包票證開發流程
Google Wallet API 提供了針對特定用途最佳化的預先定義票證類型,例如禮物卡、登機證和活動票券等等。此外,通用票證類型也適用於不提供特定票證類型的使用。
本文將介紹使用 Google Wallet API 建立及核發票證的基本步驟。有很多方法可以完成下方詳述的部分步驟,但大致來說,所有票證類型都是按照相同的基本開發流程建立。
如需建立票證的詳細逐步操作說明,請參閱網頁、電子郵件和簡訊或 Android 應用程式適用的指南。
![](https://developers-dot-devsite-v2-prod.appspot.com/static/wallet/images/ticket-class.png?authuser=2&%3Bhl=zh-tw&hl=zh-tw)
1. 建立票證類別
用途
Pass 類別可定義多張票證通用的屬性,類似於範本。舉例來說,如果您核發了活動票券,則票證類別會定義所有票券中相同的欄位,例如活動名稱、日期和時間。
您核發的每張票證都必須參照票證類別。您也必須為您建立的每個票證類別指派專屬 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-dot-devsite-v2-prod.appspot.com/static/wallet/images/ticket-object.png?authuser=2&%3Bhl=zh-tw&hl=zh-tw)
2. 建立票證物件
用途
票證物件會定義專屬票證的屬性,將核發給特定使用者。舉例來說,活動票券的 Passes 物件會定義特定票券專屬欄位,例如座位號碼或 QR code。
建立 Pass 物件後,Google Wallet API 會儲存新的票證,並連結至你的發卡機構帳戶。這類儲存的票證是由票證物件的專屬屬性和相關聯票證類別的範本屬性結合而成。
您也必須為每個票證物件指派專屬 ID,這組 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-dot-devsite-v2-prod.appspot.com/static/wallet/images/ticket-jwt.png?authuser=2&%3Bhl=zh-tw&hl=zh-tw)
3. 以 JSON Web Token (JWT) 為票證編碼
用途
如要核發票證給使用者,您必須以 JSON Web Token (JWT) 編碼方式為 Passes Class 和 Passes Object。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-dot-devsite-v2-prod.appspot.com/static/wallet/images/ticket-jwt-signed.png?authuser=2&%3Bhl=zh-tw&hl=zh-tw)
4. 使用憑證簽署 JWT
用途
凡是傳送至 Google Wallet API 以核發票證的 JWT,都必須使用先前在 Google 錢包商家主控台中提供的憑證簽署。簽署時會使用您的憑證將 JWT 加密,藉此保障票證的安全性,並允許 Google Wallet API 驗證編碼後的票證詳細資料是否有效,且與您的核發機構帳戶建立關聯。
進行方式
Google 電子錢包用戶端程式庫和 Android SDK 提供便利的方法來簽署 JWT。此外,市面上也有許多開放原始碼程式庫,能處理繁雜的程式碼簽署程序,讓您有所選擇。
如果是使用 Google Wallet REST API 核發票證的人,系統會以 Google Cloud 服務帳戶金鑰簽署 JWT。如果您使用 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-dot-devsite-v2-prod.appspot.com/static/wallet/images/ticket-add-to-wallet.png?authuser=2&%3Bhl=zh-tw&hl=zh-tw)
5. 使用「新增至 Google 錢包」按鈕或連結核發票證
用途
建立已簽署的 JWT 後,即可將票證核發給 Google 錢包使用者!做法是向使用者顯示「新增至 Google 錢包」按鈕或連結。當使用者點選按鈕或超連結時,系統會將已簽署的 JWT 傳送至 Google Wallet API,並使用您儲存的憑證解密。JWT 簽名通過驗證後,系統會將票證核發給使用者,以便儲存至他們的 Google 錢包。
進行方式
如要建立「新增至 Google 錢包」按鈕,請使用 Google Wallet 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-dot-devsite-v2-prod.appspot.com/static/wallet/images/ticket-enduser-view.png?authuser=2&%3Bhl=zh-tw&hl=zh-tw)
6. 在 Google 錢包中查看
使用者儲存核發的票證後,票證就會與先前儲存的其他票證一併顯示在他們的 Google 錢包應用程式中。
在 JWT 中建立票證物件和票證類別
您可以使用 Google Wallet REST API 或 Android SDK 預先建立票證類別和票證物件。建立完成後,即可參照票證來核發票證。
或者,您也可以「及時」建立票證類別和票證物件直接將 JSON 嵌入用於核發票證給使用者的 JWT 中。在這個方法中,當您使用「新增至 Google 錢包」功能傳送已簽署的 JWT 時,Google Wallet API 會建立 Passes 類別和 Passes 物件。按鈕或連結。
舉例來說,以下範例顯示的 JWT 含有使用 payload.eventTicketClasses
和 payload.eventTicketObjects
屬性定義的新票證類別和票證物件。請注意,這些屬性為陣列,因此可接受一或多個票證類別或票證物件。您也可以僅在 JWT 中指定新的 Passes Object,依據 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": "" } }] } }