接入你的 App
Google Play 訂閱驗證
適合透過 Google Play 銷售訂閱、自己沒有後端的 Android App。App 用 API key 把購買憑證發給閘道,閘道用你的服務帳戶去問 Google,告訴你這個訂閱現在是否有效。驗證免費。
#開始之前
- 一個 Play Console 帳號,需要是帳號所有者或管理員,才能邀請使用者。
- 一個能在 Google Cloud 控制台建專案的 Google 帳號(任何 Google 帳號都可以)。
- App 的套件名稱(applicationId),例如 com.example.app;並且已在 Play Console 建好至少一個訂閱商品。
- 操作大約 15 分鐘;之後 Google 讓權限生效最多要一天。
#1. 取得服務帳戶 JSON
這個 JSON 檔案就是服務帳戶的金鑰,閘道只用它向 Google 查詢你這個 App 的購買,不做別的。Google 稱它為「服務帳戶金鑰」,不是 OAuth 客戶端檔案(client_secret_….json)。
- 1
開啟 Google Cloud 控制台,選一個專案
console.cloud.google.com用頂部的專案選擇器。哪個專案都行;單獨新建一個專門用來驗證購買更清楚。
- 2
啟用 Play API
API 和服務(APIs & Services)→ 程式庫(Library)→ 搜尋 Google Play Android Developer API → 啟用(Enable)按完整名稱搜尋。如果按鈕顯示「管理(Manage)」,說明已經啟用了。
- 3
建立服務帳戶
IAM 與管理(IAM & Admin)→ 服務帳戶(Service accounts)→ 建立服務帳戶(Create service account)名稱隨意,例如 play-verify。後面「授予專案存取權限」「授予使用者存取權限」兩個選用步驟直接跳過,它不需要任何 Cloud 角色。點「完成(Done)」。
- 4
下載它的 JSON 金鑰
點開這個服務帳戶 → 金鑰(Keys)→ 新增金鑰(Add key)→ 建立新金鑰(Create new key)→ 選 JSON → 建立(Create)瀏覽器會下載一個 .json 檔案。請妥善保存:Google 不會再顯示第二次,拿到它的人就能查詢你 App 的購買記錄。
- 5
複製服務帳戶電子郵件
形如 play-verify@your-project.iam.gserviceaccount.com,在服務帳戶頁面能看到,檔案裡的 client_email 也是它。下一步要用。
「建立新金鑰」是灰色的或被拒絕?
#2. 在 Play Console 給它開權限
- 1
邀請服務帳戶
play.google.com/console → 使用者和權限(Users and permissions)→ 邀請新使用者(Invite new users)「電子郵件地址」填上一步複製的服務帳戶電子郵件。服務帳戶不需要接受邀請。
- 2
勾選這兩項權限
應用程式權限(App permissions)→ 新增應用程式(Add app)→ 選你的 App勾選「檢視財務資料、訂單和取消調查問卷回覆(View financial data, orders, and cancellation survey responses)」和「管理訂單和訂閱(Manage orders and subscriptions)」,其他都不用勾。如果改在「帳號權限(Account permissions)」裡勾這兩項,就對你所有 App 生效。
- 3
傳送邀請
套用(Apply)→ 邀請使用者(Invite user)之後在「使用者和權限」裡能看到這個服務帳戶,狀態為「有效(Active)」。
為什麼要這兩項權限
Google 最多要一天才讓權限生效
#3. 填到 API 金鑰上
- 1
開啟 App 用的那個金鑰
控制台 → API 金鑰 → 編輯建議給 App 單獨建一個金鑰(見下方)。
- 2
填寫「Google Play 訂閱驗證」
設定 Google Play 驗證「套件名稱」填 App 的 build.gradle 裡的 applicationId,必須完全一致;選擇第 1 步下載的 JSON 檔案,儲存。
- 3
看儲存後的提示
儲存前閘道會先拿這個檔案向 Google 試一次。如果 Google 還是拒絕,對話框會說明缺什麼,通常是第 1 步的 API 沒啟用或第 2 步的權限還沒生效。檔案加密儲存,之後不再顯示;要更換就重新上傳。
給 App 單獨用一個 key
#4. 在 App 裡驗證購買
購買完成後馬上驗證一次;之後每次開啟 App,用儲存的憑證再驗證一次。不要讓 App 啟動等著結果:先按上次儲存的結果顯示,驗證回傳後再更新。同一憑證 1 分鐘內直接回傳快取結果。每個 key 預設每分鐘最多驗證 120 次,目前上限顯示在 key 的 Google Play 設定裡,需要更多可以聯絡客服為這個 key 單獨調高。超過上限的請求回傳 429,不會記進請求日誌,所以要沿用上次儲存的結果。
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:in_app_purchase/in_app_purchase.dart';
const apiKey = 'sk-YOUR_KEY';
Future<Map<String, dynamic>?> checkSubscription(String purchaseToken) async {
final res = await http.post(
Uri.parse('https://www.aisplice.store/v1/google-play/verify'),
headers: {
'Authorization': 'Bearer $apiKey',
'Content-Type': 'application/json',
// Request signing on for this key? Then this call must be signed too:
// ...signedHeaders('POST', '/v1/google-play/verify'),
},
body: jsonEncode({'purchase_token': purchaseToken}),
);
if (res.statusCode != 200) return null; // keep the last saved answer
return jsonDecode(res.body) as Map<String, dynamic>;
}
// In your purchaseStream listener:
Future<void> onPurchase(PurchaseDetails purchase) async {
if (purchase.status != PurchaseStatus.purchased &&
purchase.status != PurchaseStatus.restored) return;
final token = purchase.verificationData.serverVerificationData;
final status = await checkSubscription(token);
if (status != null && status['active'] == true) {
await saveEntitlement(token, status); // your local storage
unlockPaidFeatures();
}
if (purchase.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchase); // acknowledges on Android
}
}#5. 決定解鎖什麼
| 驗證結果 | App 怎麼做 | 常見原因 |
|---|---|---|
| active: true, in_trial: true | 解鎖,並按 expiry_time 顯示「試用到期時間」。 | 試用中 |
| active: true | 解鎖,有效到 expiry_time。 | 已付費,包括已取消自動續訂但還沒到期的 |
| subscribed: true, active: false | 不解鎖,顯示續訂頁面。 | 已過期、已暫停、帳戶保留 |
| subscribed: false | 不解鎖,顯示訂閱頁面。 | 無法識別的憑證、付款還沒完成 |
| HTTP 502 / 503 | 沿用上次儲存的結果,稍後重試。 | Google 沒回應,或服務帳戶有問題 |
把 expiry_time 和結果一起儲存。兩次驗證之間,到這個時間之前都當作可用,這樣手機離線也能正常用。
#6. 每筆購買都要確認
未確認的購買 3 天後會被自動退款
#7. 保護 App 裡的金鑰
放在 App 裡的 key 只要攔截流量就能看到。給這個 key 開啟請求簽名,這樣別人複製走 key 也無法單獨使用;建置時做混淆,並給 key 設額度上限。
簽名對這個 key 的所有呼叫都生效
#8. 檢視驗證記錄
每次驗證(包括命中快取和失敗的)都會出現在控制台 → 請求日誌裡,記為這個 key 下一筆免費的「Google Play 驗證」。可以按完整購買憑證、憑證末尾幾位,或使用者從 Google 收據郵件裡發給你的訂單編號(GPA.…)搜尋。
- google_play_api_disabled:JSON 檔案所屬的專案沒有啟用第 1 步的 Play API。
- google_play_permission_denied:第 2 步的邀請沒做、沒勾那兩項權限、加到了別的 App,或者還沒生效。
- google_play_credentials_invalid:這個 JSON 金鑰已在 Google Cloud 被刪除或停用。新建一個金鑰,重新上傳到 API 金鑰上。
- 儲存時提示「不是服務帳戶金鑰」:你選的是 OAuth 客戶端檔案。請到服務帳戶的「金鑰」分頁下載。
- 每次都提示找不到購買:金鑰上填的套件名稱和 App 的 applicationId 不一致,或者購買憑證來自別的 App。
- 授權測試人員的測試購買會回傳 test_purchase: true。
還是沒解決?
把請求 ID 或錯誤訊息傳給我們,我們來查。