Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Google Play IAB(In-App Billing) 〜Railsでのサーバサイド...

MITSUBOSHI
September 24, 2021

Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜

MITSUBOSHI

September 24, 2021
Tweet

More Decks by MITSUBOSHI

Other Decks in Technology

Transcript

  1. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 Google Play IAB(In-App Billing)

    〜Railsでのサーバサイド対応のすべて〜 三星祐也 @MITSUBOSHI 銀座Rails #37
  2. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Google Play IABのサーバサイド対応情報があまり発信されていない

    ◆ Androidクライアントサイドの情報は多い ◆ Google公式の情報もサーバサイド側の話が少ない Motivation
  3. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 Agenda | 01 02

    03 04 Google Play IAB(In-App Billing)の仕様 Google Play IAB(In-App Billing)の対応方針と設計 Androidクライアント間との処理 Cloud Pub/Subを用いたRTDNイベント処理
  4. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Google Playの課金システム ◆

    ざっくり言うと、Androidスマホ・タブレットにおけるアプリ内決済 Google Play IAB(In-App Billing)とは
  5. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 買い切り型 ◆ consumable

    … 複数回購入可能 • 例: ゲーム内通貨 ◆ non-consumable … 一度だけ購入可能 • 例: 永久会員権, 電子書籍 One-time products
  6. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 定期購入型 ◆ 例:

    月額課金系のサービス • => スタディサプリ 中学/高校/大学受験講座 Subscriptions
  7. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ ユーザがIAB Subscriptionを購入してから3日以内に承認をしなかった場合、自動 返金される

    ➔ acknowledge方法 ◆ Androidクライアントライブラリ(PBL: Play Billing Library)経由 ◆ Google Play Developer API経由 acknowledge
  8. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ ユーザがIAB Subscriptionを購入してから3日以内に承認をしなかった場合、自動 返金される

    ➔ acknowledge方法 ◆ Androidクライアントライブラリ(PBL: Play Billing Library)経由 ◆ Google Play Developer API経由 acknowledge
  9. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ IAB Subscriptionの購入(申し込み) +

    サービス側の役務提供の開始 (acknowledge)の2つを以って、定期購入開始とする acknowledge
  10. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ obfuscatedExternalAccountId ◆ Androidクライアント側(Play

    Billing Library)でIAB Subscriptionに対して任 意の文字列を指定出来る • => 難読化されたユーザID ◆ Google Play Developer API経由で取得する最新のsubscriptionデータ内に もfieldとして表出するようになる ◆ Subscriptionの購入者と利用権付与対象者が一致するかの検証にも利用 IAB Subscriptionとユーザの紐付け
  11. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Google Play Developer

    API ➔ Androidクライアント経由でのレシート情報取得 ➔ Real Time Developer Notification サーバ側からSubscriptionの状態を取得する方法
  12. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 特定のsubscription(SubscriptionPurchaseリソース)に対する操作が可能 ◆ Ref:

    https://developers.google.com/android-publisher/api-ref/rest/v3/purchases.subscri ptions ➔ 可能な操作 ◆ 最新リソースの取得(get) ◆ subscriptionの承認(acknowledge) ◆ etc. Google Play Developer API
  13. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ GET https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageNa me}/purchases/subscriptions/{subscriptionId}/tokens/{token}

    ➔ 必須パラメータ ◆ packageName … Android app固有の一意な識別子 (別名: applicationId) ◆ subscriptionId … サービスが提供するsubscription毎に一意な識別子(別名: SKU or productId) ◆ token … ユーザが購入したsubscription毎の一意な識別子 (別名: purchaseToken) Subscriptionの最新状態の取得
  14. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 注意点 ◆ リクエスト時点のsubscription情報しか取得できない

    • => 過去時点のsubscription状態を把握したいケースがある場合は、細かく記録しておく ことが重要 Subscriptionの最新状態の取得
  15. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Play Billing Library(PBL)経由でレシート情報を取得可能

    ◆ https://developer.android.com/reference/com/android/billingclient/a pi/Purchase Androidクライアント経由でのレシート情報取得
  16. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Subscriptionの状態変化毎に通知を受け取ることが出来る機構 ◆ Google

    Cloud Pub/Subを利用する必要がある ◆ 受け取るメッセージにはsubscriptionの詳細情報は含まれていないため、 Google Play Developer APIを利用する必要がある ➔ 以降は、RTDNと略します Real Time Developer Notification
  17. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 新規購入 … Androidクライアント間

    ◆ 理由: subscriptionの新規購入はアプリ内での操作であるため(+ ユーザに同 期的にフィードバックをしたい) エンドポイント設計
  18. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 新規購入以外 … RTDN間

    ◆ 理由: subscriptionの新規購入以外はアプリ外での操作/状態変化であるた め(例: 解約, 再開, 月額更新, 猶予期間) エンドポイント設計
  19. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 役割 ◆ サービスが提供するsubscriptionのマスタデータ

    ➔ fields ◆ package_name … Android app固有の一意な識別子 (別名: applicationId) ◆ product_id … サービスが提供するsubscription毎に一意な識別子(別名: SKU or subscriptionId) GooglePlayIabPackage
  20. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 役割 ◆ 各subscriptionの管理用

    • ※subscriptionの有効期限等は関連のpaymentに同期させている ➔ fields ◆ purchase_token … ユーザが購入したsubscription毎の一意な識別子 ◆ google_play_iab_package_id ◆ payment_id GooglePlayIabReceipt
  21. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 役割 ◆ 各処理時点でのsubscription情報の履歴管理用

    (※追記型) ➔ 主なfields ◆ raw_subscription_purchase … json型であり、subscriptionの情報をそのまま突っ込んでいる • 検索に必要なものは fieldとして切り出して定義している ◆ source_type … Android側が取得したレシート or Google Play Developer API。STIにはしていな い。 ◆ rtdn_type … Real Time Developer Notificationのイベント種別 GooglePlayIabSubscriptionPurchaseHistory
  22. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Endpoint: POST /api/v1/android/iab/subscriptions

    ➔ Request parameter: ◆ receipt … クライアントが取得可能な購入情報を含むデータ • https://developer.android.com/reference/com/android/billingclie nt/api/Purchase#getOriginalJson() ◆ signature … 購入情報を含むreceiptを開発者用の秘密鍵で署名したもの • https://developer.android.com/reference/com/android/billingclie nt/api/Purchase#getSignature() 定期購入開始用エンドポイント
  23. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 主な処理の流れ ◆ 署名検証

    ◆ subscriptionの最新状態の取得 + 検証 ◆ 利用権付与 + acknowledge処理 定期購入開始用エンドポイント
  24. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 署名検証 LICENSE PUBLIC KEYはGoogle

    Play Consoleから取得出来る。 Base64エンコードされているため、デコードする。
  25. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Google Play側の購入状態とサービス側の利用権状態がズレているとき ◆

    a) Google Play側の購入状態(有) + Quipperの利用権状態(無)の場合 • a-1) 対応するGooglePlayIabReceiptが存在しない(≒ 1回も利用権が付 与出来ていない) • a-2) 対応するGooglePlayIabReceiptが存在する(≒ 最低1回は利用権 が付与出来ている) 復元の定義
  26. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Endpoint: POST /api/v1/android/iab/subscriptions/restore

    ➔ Request parameter: ◆ receipt … クライアントが取得可能な購入情報を含むデータ ◆ signature … 購入情報を含むreceiptを開発者用の秘密鍵で署名したもの ※定期購入開始用エンドポイントと同じ 復元用エンドポイント
  27. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 主な処理の流れ ◆ 署名検証

    ◆ GooglePlayIabReceiptの有無の確認 ◆ subscriptionの最新状態の取得 + 検証 ◆ 利用権付与 + acknowledge処理 復元用エンドポイント
  28. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 新規購入以外 … RTDN間

    ◆ 理由: subscriptionの新規購入以外はアプリ外での操作/状態変化であるた め(例: 解約, 再開, 月額更新, 猶予期間) エンドポイント設計 (思い出し)
  29. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Topic ◆ Publisherがメッセージ(≒

    queue)を送信する窓口 ◆ 複数のsubscriptionを紐付けさせることが出来る ◆ 関連付けられたすべてのsubscriptionで対象メッセージがacknowledgeされた時点でメッ セージを破棄する ➔ Subscription ◆ Subscriberがメッセージ(≒ queue)を受け取る窓口 ◆ 対象メッセージがacknowledgeされた時点でメッセージを破棄する Google Cloud Pub/Sub TopicとSubscription
  30. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Pull型 ◆ 受信方法

    … subscriptionに対してメッセージ取得のリクエストをする(能動的) ◆ acknowledge方法 … SDKのacknowledgeメソッド呼び出し ➔ Push型 ◆ 受信方法 … subscribe用のendpointを指定し、メッセージをHTTP Requestとして受け取れる (受動的) ◆ acknowledge方法 … HTTP Status Code(102, 200, etc.) Pub/Sub Subscriptionのメッセージ受信/acknowledge方法
  31. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ At-Least-Once delivery ◆

    同じメッセージが1回以上配信されることを考慮する必要がある • => 処理の冪等性 ➔ エラーの処理方針 ◆ Exponential Backoff ◆ Dead letter topic • RTDNの場合は最悪メッセージが処理不可 or 欠如しても困らないため設定していない ◦ => 想定内のエラー + リトライ不要な場合は acknowledgeする方針にしている Pub/Sub Subscriptionの注意点
  32. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 主な処理 ◆ JWT認証

    ◆ messageのRTDN種別に応じた処理 Cloud Pub/Subのpush型エンドポイント
  33. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 認証ヘッダーにエンコードされたJWTが含まれている ◆ header.payload.signature

    と区切られている • header ... 電子署名のアルゴリズムについて • payload ... GCP サービスアカウントのemailクレームやaudクレームなど の情報 JWT認証
  34. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ message.dataに以下3種類のうちどれか1つが排他的に含まれている ◆ subscriptionNotification

    … 定期購入型の場合 ◆ oneTimeProductNotification … 買い切り型の場合 ◆ testNotification … テスト用。Google Play Consoleからtest notificationを 使って通知出来る。 Cloud Pub/Subのtopic message
  35. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ notificationType … 2

    ➔ 定期購入(≒月額課金)成功を意味する ➔ サーバ側はsubscriptionの状態に応じて、有効期限の延長処理を行う SUBSCRIPTION_RENEWED
  36. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ IAB Subscriptionのacknowledgeは有り難い仕様 ➔

    Google Play Developer API経由で取得できるsubscriptionは最新状態のみのた め、処理毎に履歴を保存しておくと良い ➔ Real Time Developer Notificationは便利 Conclusion
  37. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ 公式ドキュメント: Google Play

    請求サービス ◆ https://developer.android.com/google/play/billing ➔ Re:ゼロから始める Play Billing Library ◆ https://speakerdeck.com/syarihu/re-zero-starting-uses-of-play-billing-l ibrary ➔ Google Play アプリ内定期購入を実装する ◆ https://techlife.cookpad.com/entry/2018/03/14/090000 参考資料
  38. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Free trial ◆

    新規購入者向けの無料期間の設定。 ➔ Introductory price ◆ 新規購入者向けの特別価格の設定。 Subscription毎の設定値
  39. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Grace Period ◆

    猶予期間の有効/無効設定 • ユーザが定期購入の支払いに失敗した場合、猶予期間の間は有料状態としてサービス を使い続けることが出来る(ようにサービス側は対応しなければならない)。 ➔ Resubscribe ◆ アプリ外購入の有効/無効設定 • 定期購入の有効期限切れ後、Google Play Store上(≒アプリ外)から新規購入が可能に なる。 Subscription毎の設定値
  40. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Pause ◆ 定期購入の一時停止機能の有効/無効設定

    • 一時停止期間終了後に定期購入が再開する ➔ Free trial limit ◆ 無料期間の付与条件の設定 • One per subscription • One across all subscriptions アプリ内全Subscriptionsへの共通設定値
  41. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ テスト購入が行える仕組みのこと ◆ https://developer.android.com/google/play/billing/test

    ➔ 注意点 ◆ テスト購入ではGoogle Play Store上、pauseやresubscribeが有効になって いる • => そのため、subscriptionの設定値の反映確認は実購入で試した License Tester
  42. #ginzarails Google Play IAB(In-App Billing) 〜Railsでのサーバサイド対応のすべて〜 ➔ Google Pub/SubのDatadog integrationを使用

    ➔ 採用したメトリクスの種類 ◆ gcp.pubsub.subscription.oldest_unacked_message_age ◆ gcp.pubsub.subscription.num_unacked_messages_by_region ◆ gcp.pubsub.subscription.push_request_count ◆ gcp.pubsub.subscription.push_request_latencies.avg Google Cloud Pub/Subの監視