Google
PR

【Google】アプリ内課金のサブスクリプションのライフサイクル

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

Google Play のアプリ内課金のうち、定期購入(サブスクリプション)は、最も状態の管理が複雑なテーマです。

更新・解約・猶予期間・一時停止など、ユーザーの操作や支払い状況によって状態が次々に変化していくためです。

このページでは、定期購入のライフサイクルと、各状態でバックエンドがどう処理すべきかをまとめていきます。

アプリ内課金全体の中での位置づけは、以下のまとめページもあわせてご覧ください。

あわせて読みたい
【Google】アプリ内課金(Google Play Billing)の全体像と記事まとめ
【Google】アプリ内課金(Google Play Billing)の全体像と記事まとめ

定期購入のライフサイクル

Google の定期購入は、ユーザーの行動や支払い状況、開発者の管理操作など、さまざまな要因によって状態が変化します。

定期購入の状態を管理するうえで信頼できる方法は、purchases.subscriptionsv2.get API で最新の購入リソースを取得することです。

この API は、購入トークン(purchaseToken)に基づいて最新の購読状態を返し、公式ドキュメントでも定期購入管理における「信頼できる情報源(source of truth)」と位置づけられています。

定期購入の状態別の処理方法

ここから、Google の API から返ってきた状態(status)ごとに、どうハンドリングするのがベターなのか紹介していきます。

各見出しのカッコ内は、リアルタイムデベロッパー通知(RTDN)で届く通知タイプの定数名です。

新規購入(SUBSCRIPTION_PURCHASED)

ユーザーが定期購入を購入すると、このイベントが発生します。

開発者は purchases.subscriptionsv2.get API で状態を確認し、購入を検証したうえで、ユーザーにコンテンツへのアクセス権を付与します。

RTDN でも購入の通知は届きますが、コンテンツ側のユーザーとの紐付けを考えると、Android アプリ経由でバックエンドへ購入レシート情報を送って処理する方が扱いやすいですね。

また、新規購入は 3 日以内に承認(acknowledge)しないと、自動的に払い戻しが行われ、購入が取り消されます。

購入をサイト側のユーザーと結び付けたい場合は、購入フロー(BillingFlowParams)で外部アカウント識別子を設定しておく方法もあります。

設定した値は obfuscatedExternalAccountId として購入リソースから取得でき、レシートが流出した場合の本人確認にも役立ちます。

なお、この識別子にメールアドレスなどの個人情報を平文で入れると購入がブロックされるため、ハッシュ化などの難読化が必要です。

更新(SUBSCRIPTION_RENEWED)

自動更新型の定期購入が更新されると、このイベントが発生します。

ユーザーが引き続き購読資格を有することを確認し、更新された expiryTime をコンテンツ側に反映します。

猶予期間(SUBSCRIPTION_IN_GRACE_PERIOD)

支払いに問題が発生した場合、猶予期間が設けられます。

この期間中は引き続きユーザーがコンテンツにアクセスできるようにし、決済不備の修正を促すのが望ましいです。

猶予期間の長さは Google Play Console で調整・無効化できます。なお 0 日に設定しても、Play は決済リトライのため最低 1 日は待機します。

サイレント猶予

この最低 1 日の待機は「サイレント猶予」と呼ばれ、決済処理のセーフティネットになっています。

この間、サブスクリプションは ACTIVE のまま保持されるため、猶予設定が 0 日でも、すぐにアカウント保留へ移るわけではありません。

アカウントの保留(SUBSCRIPTION_ON_HOLD)

猶予期間中に支払い問題が解決されない場合、アカウントが保留状態になります。

ユーザーのコンテンツへのアクセスをブロックし、保留期間のあいだ Google 側の決済不備解消を待つことになります。

アカウント保留の長さは、デフォルトでは「60 日 − 猶予期間の日数」で自動計算されます。猶予期間と同様に、Google Play Console で調整・無効化が可能です。

アカウントの保留からの回復(SUBSCRIPTION_RECOVERED)

保留状態のアカウントで支払い問題が解決されると、このイベントが発生します。

コンテンツへのアクセスを復元します。

期限切れ(SUBSCRIPTION_EXPIRED)

定期購入の有効期限が切れると、このイベントが発生します。

ユーザーのアクセス権を削除し、購入ステータスを無効に設定します。

キャンセル(SUBSCRIPTION_CANCELED)

ユーザーが定期購入をキャンセルするか、アカウント保留から回復しなかった場合、このイベントが発生します。

現在の請求サイクルの終了までは、ユーザーは引き続きコンテンツにアクセスできます。

サイクル終了後に期限切れ状態になるので、そのタイミングでアクセス権を削除します。

分割払いの場合は、キャンセルは現在のコミットメント期間の終了時に有効になります。

取り消し(SUBSCRIPTION_REVOKED)

開発者による取り消しやチャージバックなどにより定期購入が取り消されると、このイベントが発生します。

この場合は、直ちにユーザーのアクセス権を取り消します。

延期(SUBSCRIPTION_DEFERRED)

開発者が Google Play Developer API を使用して次回の請求日を延期した場合、このイベントが発生します。

延期期間中、ユーザーは無料でコンテンツにアクセスできます。

Google のサンドボックス環境で DEFERRED の API を試したのですが、うまく状態反映ができませんでした。公式ドキュメントも詳しくは書かれていないので、必要に迫られたら改めて調査したいと思います。

一時停止(SUBSCRIPTION_PAUSED / SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED)

ユーザーが定期購入を一時停止すると、これらのイベントが発生します。

一時停止中はコンテンツにアクセスできず、料金も発生しません。

一時停止期間が終了すると、定期購入は再開されます。

再開できない場合は、アカウントが保留状態になります。

アップグレード・ダウングレード・再購読

ユーザーがアップグレード、ダウングレード、またはキャンセル後に再購読した場合、古い定期購入は無効になり、新しい購入トークンを持つ定期購入が作成されます。

このとき、新しい購入リソースには linkedPurchaseToken フィールドが含まれ、アップグレード・ダウングレード・再購読の元となった古い購入を示します。

このトークンで古い定期購入をたどれば、既存のユーザーアカウントを特定し、新しい購入を同じアカウントに紐付けられます。

逆に、購読が継続しているあいだは購入トークンは変わりません。

更新のたびに変わるのは注文ID(orderId)のほうで、初回の注文IDに連番が付く形になります。

再購読は、アプリ内から購入したときと、Google Play ストアから再開したときで挙動が異なるので注意が必要です。

定期購入商品の構成(基本プランと特典)

定期購入商品は、2022 年の Google Play Billing Library 5 以降、「基本プラン(base plan)」と「特典(offer)」の階層構造になっています。

基本プランは、請求期間(1 か月・1 年など)や価格を定義する土台です。

その基本プランに、ユーザーを呼び込むための特典(オファー)を紐づけていきます。

特典(オファー)の種類

特典には、大きく次の 3 種類があります。

種類内容
無料トライアル一定期間を無料で利用できる
1 回限りのお支払い最初の 1 請求期間だけ割引価格にする
割引が適用された定期的なお支払い複数の請求期間にわたって割引価格を適用する
※表は横スクロールできます

現在のフェーズ(offerPhase)を判定する

ユーザーがいま無料トライアル中なのか割引中なのかは、purchases.subscriptionsv2.get のレスポンスの lineItems から判定できます。

各 lineItem の offerPhase には、次のいずれかの情報が入ります。

フィールド内容
freeTrial無料トライアル中
introductoryPrice割引価格(導入価格)での支払い中
basePrice基本プランの通常価格での支払い中
prorationPeriodプラン変更などにともなう日割り計算期間
※表は横スクロールできます

ただし、subscriptionsv2 でわかるのはフェーズの種類までで、適用中の正確な割引額や期間までは取得できません。

具体的な金額や期間まで必要な場合は、注文情報(orders)API を併用して補完します。

あわせて読みたい
【Google】アプリ内課金の注文情報(orders)を取得する
【Google】アプリ内課金の注文情報(orders)を取得する

基本プランや特典そのものの管理(作成・更新)には、古い inappproducts API ではなく monetization.subscriptions API を使います。

状態の変化は RTDN で受け取る

ここまで紹介した状態変化は、リアルタイムデベロッパー通知(RTDN)を使うと、発生のたびにサーバーへ届きます。

有効期限が近い購読をバッチで定期チェックする必要がなくなるため、サブスクリプションの運用では RTDN の利用が基本になります。

RTDN の仕組みと設定方法は、以下のページで解説しています。

あわせて読みたい
【Google】リアルタイムデベロッパー通知(RTDN)の仕組みと設定
【Google】リアルタイムデベロッパー通知(RTDN)の仕組みと設定

まとめ

Android アプリにおける定期購入のライフサイクル管理について紹介してきました。

定期購入には、プリペイドプランやプラン変更予約(ReplacementMode.DEFERRED)など、サービスの幅を広げる機能も用意されています。

ですが、意外にも新しい機能は公式ドキュメントで詳細に説明されていないことも多く、挙動を確認してみないと実態がわからないケースも少なくありません。

これまでの経験上、ドキュメントの内容が薄い機能を試して何度かバグに遭遇したので、新機能を導入する際は調査や検証に時間をかけた方が良さそうです。

参考(出典)

ABOUT ME
saratoga
saratoga
フリーランスエンジニア
仕事にも趣味にも IT を駆使するフリーランスエンジニア。技術的な TIPS や日々の生活の中で深堀りしてみたくなったことを備忘録として残していきます。
記事URLをコピーしました