Flutter 整合 Google Wallet 的細節與經驗!

Yii Chen
Flutter Taipei
Published in
14 min readMar 21, 2024

--

為 Android 用戶添加錢包體驗

不知大家對於 Google Wallet 在 Flutter 或 Mobile 上的整合有沒有熟悉,個人過往的經驗上偏少。這次因為 Flutter Global Game Challenge 的活動參與,官方積極地推廣 Google Wallet API,所以在開發上有整合相關功能,也剛好快速的了解了它,其實它有很多有趣的玩法。

通常我們都會跟 Pass 通行證互動,能進行許多用途,包含以下幾種情境:

  • 停車證
  • 學生證
  • 借書證
  • 會員卡
  • 保險卡
  • 餐廳、電影院訂位資訊
  • VIP 認證卡
  • NFC 感應卡

只要是能接受數位資訊的地方都能使用。而在這次 Challenge 活動要開發的就是遊戲,常見地就會聯想到收藏卡片,這也是基本且開發容易的 Normal Pass。

而在本文介紹的 APP 當中,有一個核心功能是拯救和蒐集物種,用戶能將物種的資訊儲存到 Google Wallet,往後就可以收藏和展示相關的 Pass。我也順手將 Google Wallet 的設定、開發與公開通行證的細節都記錄下來,趕快往下了解吧!

Official Page

Google Pay & Wallet Console

Setup

進入到 Google Play & Wallet Console 頁面,只有兩種選項,一個是 Google Pay API,一個是 Google Wallet API。

開啟 Google Wallet API 設定頁面,有幾個選項可以設定,我們直接點擊「建立類別」,需要幫 APP 會用到的 Pass card 指定類別。頁面下方也有按鈕以及清單可以瀏覽。

因為應用本身需要的是收藏卡片、紀念性質的 Pass card,所以選擇基本功能的「一般」。

第一個為必填選項,輸入你的卡片名稱,一個自定義 ID,也可以用類型來命名,而這部分以英文為主。

第二個是裝置和用戶持有者的狀態,通常都選擇 Pass 允許多位持有者,如果沒有任何限制的話,其餘就看實際需求去選擇。

其他設定還有包括,自定義的圖片、文字、連結、Callback,都能讓 Pass 的樣式和資訊更豐富。甚至可以支援智慧感應功能,如果你的 Pass card 可以跟硬體裝置互動,就必須開啟感應功能。

完成後清單上就會有自己的 Pass Class,在應用與 Google Wallet 互動時會用到此 ID,決定要新增的卡片。

新增要進行測試的 Google 帳戶,一個帳號一行,目前沒有限制人數。

最後一個重要步驟,綁定要測試的應用資訊,它的 bundle ID (package name),還有測試裝置的 SHA-1 指紋。當然如果應用上架到商店的話,記得要新增 keystore 的 SHA-1,從 Google Play Console 上也能取得。

額外教學,如何從電腦上取得 SHA-1 呢?非常簡單,使用 keytool CLI,存取 debug.keystore

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

到這裡基本的前置設定就完成了,讓我們趕快回到 Flutter 進行開發!

Flutter Development

經過幾個相關套件的使用後,了解 add_to_google_wallet 是最容易且簡單的選擇,此範例會使用它來實作。

首先要準備 Pass Json,裡面包含了所有資訊。查看官方提供的範例,有幾個是最重要的元素須先準備好,包含 Issuer IdIssuer EmailPass Class,從 console 後台取得。而 Pass 本身能設置的內容非常豐富,基本設置的會有幾種:

  1. 通行證的 Logo 圖像
  2. 通行證的 Title,通常代表某個產品或活動資訊
  3. 通行證的 Header
  4. 通行證的 SubHeader
  5. 通行證的 QRCode 掃描內容,任何的字串或是 JSON 都可以
  6. 通行證的 heroImage → 詳細頁面的圖片,通常跟 Logo 相同,創造動畫跳轉的效果
  7. 通行證的其他屬性,設置 textModulesData 欄位,每個物件需要 idheaderbody 三種資料
{
"iss": "$issuerEmail",
"aud": "google",
"typ": "savetowallet",
"origins": [],
"payload": {
"genericObjects": [
{
"id": "$issuerId.$passId",
"classId": "$issuerId.$passClass",
"genericType": "GENERIC_TYPE_UNSPECIFIED",
"hexBackgroundColor": "#4285f4",
"logo": {
"sourceUri": {
"uri": "$passLogoImageUrl"
}
},
"cardTitle": {
"defaultValue": {
"language": "en",
"value": "$passTitle"
}
},
"subheader": {
"defaultValue": {
"language": "en",
"value": "$passSubHeader"
}
},
"header": {
"defaultValue": {
"language": "en",
"value": "$passHeader"
}
},
"barcode": {
"type": "QR_CODE",
"value": "$passId"
},
"heroImage": {
"sourceUri": {
"uri": "$passDetailImageUrl"
}
},
"textModulesData": [
{
"id": "points",
"header": "POINTS",
"body": "1000"
}
]
}
]
}
}

在 UI code 的部分使用 AddToGoogleWalletButton 元件,設置 pass 為自定義的 pass JSON 即可,用戶在畫面點擊後就會跟 Google Wallet 進行互動。另外,有提供三個 callback,讓我們可以針對各種狀況進行處理,包含成功、結束、錯誤,可以顯示訊息給用戶了解。

AddToGoogleWalletButton(
pass: passJsonString,
onSuccess: () {},
onCanceled: () {},
onError: (Object error) {},
),

應用會透過 Deep Link 與 Google Wallet 溝通,開啟錢包後詢問是否要新增,點擊取消則返回 APP。

優化使用方式

因為我的 Pass 需要給予一些屬性資訊,前面有說到是設置 textModulesData 欄位,當我們有多屬性要動態生成對應的 JSON 字串時如何處理呢?額外創建一個 GoogleWalletPassProperty class 負責儲存每個屬性,包含 idhederbody,在開發時可讀性較高,當然也可以直接使用 Record 處理,根據個人喜好即可。

Dart Code:

  1. 使用 freezed 套件協助 JSON 操作
  2. toJson() 後的字串不是完整的 JSON 格式,要再幫每個 Key、Value 補上雙引號
@freezed
class GoogleWalletPassProperty with _$GoogleWalletPassProperty {
factory GoogleWalletPassProperty({
@JsonKey(name: 'id') required String id,
@JsonKey(name: 'header') required String header,
@JsonKey(name: 'body') required String body,
}) = _GoogleWalletPassProperty;

factory GoogleWalletPassProperty.fromJson(Map<String, dynamic> json) =>
_$GoogleWalletPassPropertyFromJson(json);
}
final propertiesJsonTemp = properties.map((e) => e.toJson()).join(',');

final propertiesJson = propertiesJsonTemp
.replaceAllMapped(RegExp(r'(?<=\{| )\w(.*?)(?=\: |, |})'), (match) {
return '"${match.group(0)!}"';
});

Pass JSON:

"textModulesData": [
$propertiesJson
]

這樣在開發時是不是就更靈活更方便了呢!這部分我也會整理一下發個 PR,希望讓套件更完整,幫助到其他開發者。

Public Pass Application

當我們申請 Public Pass 權限後,很快地會收到一份 Google 的詢問信件,包含一些使用問題。

以下是我們的回覆,不是公司也沒關係,只需誠實地說明申請原因即可,官方團隊不太會刁難。

Q1: 有關您打算如何使用通行證以及通行證上將包含哪些資訊的詳細資訊

🧑🏻‍💻: 正如我所說,如果用戶保存物種,他們可以將物種通行證保存到 Google 錢包中。這張通行證是一張收藏卡。

Q2: 有效的公司名稱和網站

🧑🏻‍💻: 實際上,我們是兩人的開發團隊。沒有合適的公司名稱或網站(WIP)。此應用程式和遊戲為了「Flutter 全球遊戲挑戰賽」活動。我們希望用戶能夠獲得良好的體驗。

Q3: 使用者如何獲得通行證(簡訊、電子郵件等)

🧑🏻‍💻: 用戶將在「Better World」應用程式的頁面上獲得通行證。

Q4: 通行證運輸方式(NFC、條碼)

🧑🏻‍💻: 我們使用通用通行證。

接著將所有相關的螢幕截圖都附上,盡可能將細節展出,讓官方輕鬆就能了解 APP 的相關操作。

最終,我們在初始提交後的一個禮拜收到批准信件,很漫長的過程,不知道實際的審查細節,原本以為是不是會因個人團隊而被取消。有任何問題都能直接透過原信件跟官方團隊互動,他們會給予回覆。

回到 Wallet Console 查看,原有的審核相關資訊都消失了,這時候也不需要設置相關的測試人員,代表 Pass 可以公開給每位用戶使用囉。

提醒

在 APP 上架到 Play Store 之前記得將公開 release 的 SHA-1 certificate fingerprint 新增到權限清單。用戶才能正常地跟 Google Wallet API 互動,將 Pass 通行證新增到錢包,可不要忘記了。

大家有什麼 Google Wallet 的開發經驗可以分享嗎?很好奇每個應用的整合方式,雖然很可惜只有 Android 用戶才能使用。歡迎留言、提出任何想法,期待與你的交流哦!

Global Game Challenge

Other Articles

--

--

Yii Chen
Flutter Taipei

Flutter Lover || Organizer FlutterTaipei || Writer, Speaker || wanna make Flutter strong in Taiwan. https://linktr.ee/yiichenhi