アプリ内課金

【Googleサブスク】リアルタイムデベロッパー通知の種類とハンドリング

記事内に商品プロモーションを含む場合があります

「Google(Androidサブスク)のレシート検証について」で書いたように、Google Play Billing のサブスクリプションの実装に向けて調査を進めてきました。

【Googleサブスク】レシート検証をPHPとKotlinで実現する前回、GooglePlayBilling(Android)の都度課金についてまとめてみましたが、最近は月額課金のサービスが多くなり、都度...

その中で、購読の更新処理をどうしようか迷っていたのですが、最終的に行き着いたのは「リアルタイムデベロッパー通知」になります。

最初は、有効期限が近いものに対してバッチでレシート検証していくことも考えましたが、購読中ではなく、猶予期間(Grace Period)や一時停止期間(Account Hold)のものを頻繁にチェックに行くのは非効率です。

今回は、このリアルタイムデベロッパー通知についてまとめたいと思います。

2020 年に Google の公式ドキュメントが整理され、以前よりも必要な情報へのアクセスがしやすくなりました。こちらをベースにまとめた記事を書いてみましたので、以下の記事も参考にしてみてください。

【2021年版】Googleアプリ内課金の導入と運用方法(GooglePlayBilling)

リアルタイムデベロッパー通知

リアルタイムデベロッパー通知は Google のプラットフォーム側で用意されている Webhook をベースとした仕組みです。

リアルタイム デベロッパー通知は、デベロッパーが SUBSCRIPTION_PURCHASED や SUBSCRIPTION_RECOVERED といった定期購入のステータスの変更を監視できるようにするサーバー プッシュ通知です。

リアルタイム デベロッパー通知の受信後は、Developer API を呼び出して完全なステータスを取得し、バックエンド ステータスを更新する必要があります。
これらの通知は、定期購入のステータスが変更されたことのみを通知するもので、定期購入のステータスの詳細は通知されません。

定期購入固有の機能を追加する
Add real-time developer notifications

ドキュメントには上記のような記載がされていますが、相変わらず GooglePlay のドキュメントは英語版の方が最新なので日本語版の内容にはお気を付けください。

どちらにしても、通知タイプを完全に信頼するわけにはいかず、通知を受け取った際に最新のレシートの状態を確認する必要は出てきそうですね。

ただし、何かしらの変更が生じた場合のみ通知が届くので、バッチで対象ユーザ全員のレシート検証をする必要がなくなり、無駄なリソースを省くことができます。

通知されるメッセージのフォーマット

通知で届くメッセージのフォーマットは以下の通りですが、実際には Pretty Print されていない 1 行の JSON 文字列になります。

GooglePlay コンソールからテスト送信してみた結果は以下の通り。

{“version”:”1.0″,”packageName”:”パッケージ名”,”eventTimeMillis”:”通知日時”,”testNotification”:{“version”:”1.0″}}

通知フォーマットの詳細は、以下で詳しく説明されています。

主に必要となる情報は、以下の 4 つくらいでしょうか。

  • eventTimeMillis(通知イベントの日時)
  • notificationType(通知タイプ)
  • purchaseToken(購入時のレシートトークン)
  • subscriptionId(ストアの商品コードと同じもの)

レシート検証の API のリファレンスもそうですが、subscriptionId は productId(SKU)と同じものを指しているのでややこしいですね。

都度課金の API と同じように、productId に統一してくれたらいいのに・・・。

通知の種類と役割

通知には多くの種類が用意されています。

実際には通知タイプを鵜呑みにせずに、purchaseToken を使ってレシートの状態を確認する必要がありますが、どのようなタイミングで通知が送られてくるか知っておけば、ハンドリングしやすくなると思います。

通知タイプの 10 と 11 がドキュメントにないですが、これらはユーザの一時停止に使われると思われます。
(その後、日本語版のドキュメントにも追加されました)

ユーザの一時停止については後述したいと思います。

SUBSCRIPTION_RECOVERED(1)

Account Hold の状態から購読状態に復帰した時なので、一時停止期間の設定を有効にしている場合のみ発生しそうですね。

SUBSCRIPTION_RENEWED(2)

通常の自動更新が行われた時、または Grace Period から復帰した時なので、購読の有効期限が更新されているか確認します。

SUBSCRIPTION_CANCELED(3)

ユーザが意図的にキャンセルした時、または Grace Period や Account Hold の状態で決済手段が改善されず解約となる時なので、レシートの cancelReason が設定されているか確認します。

SUBSCRIPTION_PURCHASED(4)

初回購入の時、またはキャンセル状態で Google API 経由で再購読した時なので、レシートの継続購読状態や有効期限などを確認します。ただ初回購入の時は、サーバサイド側のサイトのユーザなどと紐付けたい場合もあると思うので、通知のタイミングで非同期に扱うのは難しいケースもあるかもしれません。

SUBSCRIPTION_ON_HOLD(5)

決済手段に問題が有り Account Hold になった時なので、レシートで有効期限が過去になっていることを確認します。

SUBSCRIPTION_IN_GRACE_PERIOD(6)

決済手段に問題が有り Grace Period になった時なので、レシートで有効期限内が過去になっていることを確認します。

SUBSCRIPTION_RESTARTED(7)

キャンセル状態のユーザが Google Play の設定から再購読した時なので、レシートの継続購読状態や有効期限などを確認します。SUBSCRIPTION_PURCHASED の Google API 経由で再購読した時とは少し異なりますが、対応としては同じで良さそうです。

SUBSCRIPTION_PRICE_CHANGE_CONFIRMED(8)

購読価格の変更がユーザーによって正常に確認された時なので、次回の購読更新も通常通り継続されます。この通知が来ない場合は、次の購読更新のタイミングで停止状態になります。商品の価格変更はユーザ離脱のリスクもあるので、値上げについてはなるべくなら回避したいですね。

SUBSCRIPTION_DEFERRED(9)

定期購読の繰り返し期間が延長された時。ここはイメージできていないので、もう少し調査が必要です。

SUBSCRIPTION_REVOKED(12)

有効期限が切れる前に購読が取り消された時なので(金銭の発生しない解約状態)、実質の購入キャンセルになります。レシートは無効な状態になっているのでコンテンツの提供も止めます。

この場合のキャンセル理由(cancelReason)がユーザキャンセルの場合は、キャンセル日時(userCancellationTimeMillis)がレシートに記載されますが、決済の問題の場合はキャンセル日時がレシートに含まれません。

デベロッパー通知以外のタイミングで REVOKE かどうか判断する決め手はないので、デベロッパー通知が送られてきた時は、通知の内容(履歴)をログやデータベースに残しておくといいですね。

SUBSCRIPTION_EXPIRED(13)

サブスクリプションが期限切れの時ですが、SUBSCRIPTION_ON_HOLD や SUBSCRIPTION_IN_GRACE_PERIOD とタイミングが被らないのか疑問です。ここも調査が必要です。

このように、イメージしやすい通知もありますが、実際に通知を送ってみないと挙動がわからないものもあります。

通知を利用するシーン、Android アプリを介して同期的に処理する場面を明確にしておきたいですね。

リアルタイムデベロッパー通知の利用方法

通知を受けるには、GCP(Google Cloud Platform) の Google Cloud Pub/Sub を利用する必要があります。

オンプレや AWS などの別のクラウドサービスでサイトを展開している場合は、この通知のために GCP の契約しないといけないの?って思うかもしれませんが、Android アプリだし仕方ないよねってところでしょうか・・・。

Cloud Pub/Sub の設定は少し長くなるので別の記事にまとめました。

【Googleサブスク】Pub/Subからデベロッパー通知をプルする以前、Google Play Billing の月額課金(サブスクリプション)の状態は、「リアルタイムデベロッパー通知をトリガーに管理・...

ユーザの一時停止について

購読で「Account Hold」の状態は「アカウントの一時停止」と呼ばれますが、別途、ユーザの一時停止のステータスが 2019 年に追加されました。

これは GooglePlay ストアからユーザが行う一時停止で、一定期間後に自動的に購読が再開されるものになります。

この状態になった場合、リアルタイムデベロッパー通知では通知タイプ 11 が送られてきて、その後続けて 10 が送られてきますが、その際のレシート内容には差がありません。

多分、英語ドキュメントに書かれている「SUBSCRIPTION_PAUSED」のことだと思うのですが、10 と 11 の区別がつかないですね。
(日本語ドキュメントにはこの項目が現在はないので注意)

When querying a subscription that is currently paused, if expiryTimeMillis is a past date, and autoResumeTimeMillis is a future date, you should suspend user access to the content and consider the user’s entitlement as paused:

User has paused their subscription – SUBSCRIPTION_PAUSED

共通しているのは、レシートのレスポンスに autoResumeTimeMillis が設定されていることで、これが再開日時となります。

ちなみに、再開された場合は通知タイプ 1 が返ってきます。

ドキュメントの更新はあまり期待できませんが、ユーザの一時停止についてはこのような挙動だと覚えておきましょう。

っと書いておきながら、上記ページのもう少し下に詳しい説明がありました。

SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED: A user has chosen to pause or resume the subscription before the pause has taken effect.
SUBSCRIPTION_PAUSED: The subscription is currently paused.
SUBSCRIPTION_RENEWED: The subscription has been resumed successfully.
SUBSCRIPTION_ON_HOLD: An attempt to resume the subscription wasn’t successful, and the subscription is currently in account hold.

Pause a subscription

きっと SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED が 10 で、SUBSCRIPTION_PAUSED が 11 ですね。