前言
本篇紀錄以後端角度來串接 GooglePlay IAP 訂閱制的流程
APP內訂閱流程
Android-Developer Purchasing Products
當 GooglePlay IAP 建立商店訂單,取得加密訂單、明文訂單後,由後端來驗證訂單
後端收到訂單後,需將加密訂單、明文訂單與後端的公鑰做驗證
.Net Framework 可透過 套件 Portable.BouncyCastle 1.9.0 來驗證
string publicKey = "MIIBI...";
string signedData = "{\"orderId\":\"GPA.1234-1234-1234-92201\",\"packageName\":\"xxx\", ...";
string signature = "QYAjwIA...";
byte[] publicKeyBytes = Convert.FromBase64String(publicKey);
byte[] purchaseInfoBytes = Encoding.UTF8.GetBytes(signedData);
byte[] signatureBytes = Convert.FromBase64String(signature);
var pubKey = PublicKeyFactory.CreateKey(publicKeyBytes);
ISigner signer = SignerUtilities.GetSigner("SHA-1withRSA");
signer.Init(false, pubKey);
signer.BlockUpdate(purchaseInfoBytes, 0, purchaseInfoBytes.Length);
var isVerify = signer.VerifySignature(signatureBytes);
驗證成功後,則可照業務邏輯跑完訂單流程
後端建單成功後,API 吐成功狀態讓 Android GP APP 確認訂單
自動續訂訂閱流程
RTDN 是透過 Google Cloud Pub/Sub 來做即時通知,設定方式參考 Android-Developer 即時開發人員通知參考指南
RTDN 推送 msg 到 Pub/Sub 時,不會傳 ordering key,所以建立 Topic 時,Message ordering 不用勾
主要流程是
GooglePlay Server => 發送 msg 至 Topic => 後端接收 Topic 的 msg =>
帶入 subscriptionNotification.purchaseToken 來呼叫 Android-Developer Method: purchases.subscriptionsv2.get 用以取得詳細訂單
呼叫 subscriptionsv2 要先取得 Google OAuth Token Android-Developer android-publisher/authorization
以 notificationType 來判斷通知類型
當通知處理成功後,則回 Ack 確認成功處理了Message,若發生異常回 Nack,則 Pub/Sub Topic 會依據設定好的 Retry Policy 來進行 Retry
Android-Developer SubscriptionNotification
subscriptionsv2 Response Model 參考 Android-Developer SubscriptionPurchaseV2
latestOrderId = 訂單編號
lineItems.First().productId = 產品ID (群組編號)
lineItems.First().offerDetails.basePlanId = 方案ID (群組底下的方案編號)
訂閱通知
notificationType = 4 (SUBSCRIPTION_PURCHASED 訂閱了新品項)、2 (SUBSCRIPTION_RENEWED 自動續訂)
收到訂閱通知取得詳細訂閱單資訊後,確認品項正確後即可走建單流程
正常一連串續訂的訂單編號有規律
ex:
第一筆訂閱單是 GPA.3348-6666-7777-07764
第二筆續訂單是 GPA.3348-6666-7777-07764..0
第三筆續訂單是 GPA.3348-6666-7777-07764..1
依此類推
可透過此規律來反推該訂閱單是第幾期、主訂閱單的所屬會員,用以建單
但要注意的是 GooglePlay 若是商店頁訂閱的話,可與安卓開發夥伴討論
APP內訂閱可設定 externalAccountIdentifiers,用以判斷是 APP 訂閱的續訂單
若 externalAccountIdentifiers = null,則代表是商店頁訂閱
而商店頁訂閱,因為是新訂閱單,GooglePlay 會產生全新一筆訂閱編號,以至於無法透過上面規律來找出是哪位會員訂閱
可參考此篇解釋來處理商店頁訂閱查無會員的問題,這篇主要是說當發生商店頁訂閱時,先把訂單資訊暫存下來
等會員登入 APP 後,則 APP 再把訂閱中的訂單編號拋到後端,後端再把訂閱單跟會員 Mapping 起來
StackOverflow Google play store does not return obfuscatedExternalAccountId when resubscribing after expiration
需要注意的是 GooglePlay 建商店單後,需在三天內確認訂單,否則 GooglePlay 會自動退款
若確認訂單的動作都是在 APP 內執行的話,則代表若自動續訂成功,會員沒登入APP,三天內沒確認訂單則自動退款
Android-Developer 處理購買交易
取消訂閱通知
notificationType = 3 (SUBSCRIPTION_CANCELED 會員取消訂閱)
收到此通知後,抓取 latestOrderId 該訂單編號來更新訂閱狀態
恢復訂閱通知
notificationType = 7 (SUBSCRIPTION_RESTARTED 會員恢復訂閱)
收到此通知後,抓取 latestOrderId 該訂單編號來更新訂閱狀態
退款
GooglePlay 要查退款訂單,則透過 API 查詢
查看已作廢的購買交易
抓 orderId 查詢到訂閱單後,更新訂閱狀態,並做出相對應處置
寬限期
notificationType = 6 (SUBSCRIPTION_IN_GRACE_PERIOD 寬限期)
當續訂日期到時,且GP向用戶扣款失敗,則會進入寬限期
保留狀態
notificationType = 5 (SUBSCRIPTION_ON_HOLD 保留狀態)
當寬限期過後,用戶仍未完成付款,則進入保留狀態 (30天)
保留狀態內,GP仍然會嘗試向用戶扣款,用保留狀態過後,仍未付款則取消訂閱
已從帳戶保留狀態恢復
notificationType = 1 (SUBSCRIPTION_RECOVERED 從帳戶保留狀態恢復)
當帳戶在保留狀態內已付款,則 GP 會通知此類型,而到期時間會從扣款時間開始計算,而不是最初的應續訂日期
備註
GP 扣款會在續訂到期前 24小時嘗試扣款,若扣款成功,則會在續訂到期時,通知 notificationType = 2 (自動續訂成功)
轉載請註明來源,若有任何錯誤或表達不清楚的地方,歡迎在下方評論區留言,也可以來信至 leozheng0621@gmail.com
如果文章對您有幫助,歡迎斗內(donate),請我喝杯咖啡