Google 錢包票證開發流程

Google Wallet API 提供一組預先定義的票證類型,已針對特定用途 (例如禮物卡、登機證、活動票券等) 進行最佳化。即使無法使用特定票證類型,也可使用一般票證類型。

本文將介紹使用 Google Wallet API 建立票證及核發票證所需的基本步驟。有很多方法可以完成下文所述的某些步驟,但整體來說,所有票證類型都是按照相同的基本開發流程建立。

如需建立票證的詳細逐步操作說明,請參閱 Google Wallet REST APIAndroid SDK 的使用指南。

用途

票證類別可定義多張票證通用的屬性組合,這類似於範本。舉例來說,假設要核發活動票券,「票證類別」就會針對所有票券定義相同的欄位,例如活動名稱、日期和時間。

核發的每條票證都必須參照票證類別。另外,你也必須為您建立的每個票證類別指派專屬 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"
      }
    }
  }
}
    

用途

票證物件會定義要核發給特定使用者的專屬票證屬性。舉例來說,活動票券的票證物件會定義特定票券專屬的欄位,例如座位號碼或該票券的 QR code。

建立票證物件後,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": ""
  }
}
    

用途

如要向使用者核發票證,票證類別和票證物件必須以 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"
      }
    ]
  }
}
    

用途

所有傳送至 Google Wallet API 的 JWT 都必須使用您先前在 Google 錢包商家主控台中提供的憑證進行簽署。簽署程序會使用您的憑證加密 JWT,藉此確保票證安全無虞,並允許 Google Wallet API 驗證編碼中的票證詳細資料是否有效,並與核發機構帳戶連結。

運作方式

Google 錢包用戶端程式庫和 Android SDK 提供方便簽署 JWT 的方法。市面上還有許多開放原始碼程式庫,可處理複雜的程式碼簽署工作以供您選擇。

針對使用 Google Wallet REST API 發送票證的使用者,必須使用 Google Cloud 服務帳戶金鑰簽署 JWT。如果您使用 Google Wallet Android SDK,SDK 會自動使用應用程式簽署憑證的 SHA-1 指紋簽署 JWT。

為了保護憑證,JWT 只應在您的伺服器上簽署,或是在應用程式中使用 Google Wallet Android SDK。

顯示程式碼範例簽署

Java

  // Create the JWT as a HashMap object
  HashMap claims = 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')
        

用途

建立已簽署的 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)
    }
  }
        

使用者儲存核發的票證後,票證會連同他們儲存的任何其他票證一起顯示在他們的 Google 錢包應用程式中。

在 JWT 中建立票證物件和票證類別

您可以使用 Google Wallet REST API 或 Android SDK 預先建立票證類別和票證物件。建立完成後,系統就會透過參照 ID 來核發票證。

或者,你也可以「及時」將「票證」類別和「票證物件」嵌入用於核發票證的 JWT 中,來建立票證類別和票證物件。在這個方法中,當您使用「新增至 Google 錢包」按鈕或連結傳送已簽署的 JWT 時,Google Wallet API 就會建立票證類別和票證物件。

舉例來說,以下的 JWT 含有以 payload.eventTicketClassespayload.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": ""
        }
      }]
    }
  }