こんにちは。LINEアプリのAndroid開発を担当しているRaphaelです。
サブスクリプションサービスにおいて、避けては通れない課題の一つが「解約」です。解約には、ユーザーが 自ら止める「自発的な解約」以外に、クレジットカードの有効期限切れなど支払い方法の不備によって発生する「非自発的な解約」が存在します。Android版LINEでは、LYPプレミアムをはじめとするサブスクリプションの非自発的な解約による離脱を防ぎ、ユーザーにサービスを提供し続けるため、Google Play Billing Libraryが提供するアプリ内メッセージ(In-App Messaging)の導入を検討しました。
この記事では、開発要件に基づいた技術検証の内容と、そこで見えてきたメリット・懸念点について詳しくご紹介します。
背景:非自発解約をいかに減らすか
Android版LINEにおいて、支払い方法の無効化によるサブスクリプションの離脱は、年間で一定数発生しています。ユーザーが意図せずサービスを利用できなくなることは、ユーザー体験の低下に直結します。
この課題を解決するためには、アプリ内で定期購入の失敗を速やかに通知し、その場で支払い方法を修正してもらう仕組みが必要です。
通常、このような仕組みをフルスクラッチで開発する場合、サーバー側との連携や専用のUI(User Interface)開発が必要になりますが、Google Play Billing LibraryのネイティブAPI(Application Programming Interface)を活用すれば、よりシンプルかつ迅速に実装できる可能性があるため、導入可否の検証を行いました。
Google Play Billing Library「アプリ内メッセージ」とは
このAPIを実装すると、支払いに失敗したユーザーがアプリを起動した際、自動的に下図のように通知スナックバーが表示されます。

基本挙動
- 通知の表示:支払い失敗時、アプリ内に通知スナックバーが表示される
- 即時修正:ユーザーが「更新」ボタンをタップすると、Google Playの支払い方法修正用ボトムシートがアプリ内で起動する
- アプリ内完結:外部ブラウザーやGoogle Play Storeアプリに遷移することなく、その場で新しい支払い情報を登録し、サブスクリプションを継続できる
このメッセージは、Google Play ドキュメントに定義されている「猶予期間(Grace Period)」または「アカウント一時停止(Account Hold)」の期間中に表示されます。
サブスクリプション中に定期購入の支払いが失敗すると、まず猶予期間に入ります。この段階でサービスのコンテンツは利用できます。猶予期間が過ぎるとアカウント一時停止期間に移行し、サービスのコンテンツは利用できなくなります。アカウント一時停止中に支払い方法を修正しないまま期間が過ぎると解約になります。
迅速な実装の可能性
LINEアプリにおけるアプリ内購入は、自社開発のバックエンドサーバーを介して処理されています。もし、この支払い方法の修正を促す通知を既存の仕組みの中でフルスクラッチで実装しようとした場合、おおよそ次のようなフローを実装する必要があります。

- 画面遷移の検知:アプリ側で、ユーザーが指定のActivity(ホームやトークなど)に入る
- ステータスの問い合わせ:アプリからバックエンドサーバーに対し、当該ユーザーの定期購入ステータスを問い合わせる
- 情報の返却:バックエンドサーバーは、Google Play サーバーとの通信によって保持しているステータスをアプリ側に返す
- 表示判定:「支払い失敗による無効状態」である場合、アプリ側で通知メッセージを生成・表示する
- 修正フローの実行:ユーザーが通知から支払い方法を修正するアクションを取る
- 復旧:定期購入が再登録され、正常な購読状態に戻る
これら一連の挙動を自前で構築するには、クライアントとサーバー側双方での機能追加が必要になります。
Google Play Billing LibraryのIn-App Messaging APIを利用すると、BillingClient.showInAppMessagesメソッドを呼び出すだけで、指定したActivity内に支払い通知メッセージを表示させることが可能になります。(Googleドキュメント参照)
このAPIの特徴は、BillingClientが直接Google Play サーバーと通信を行う点にあります。これにより、前述したフローの「2〜5」にあたる開発要素を、すべてスキップできるのです。そのため、サーバー・クライアント間のロジック、UIデザインや多言語対応の開発リソースを抑えつつ、よりスピーディーにユーザーへ価値(非自発解約の防止)を提供できることが、導入検討における大きな推進力となりました。
導入に向けた技術検証:LINEにおける要件との適合性
LINEのように大規模かつグローバルに展開されるアプリに導入する場合、単に「動く」だけでなく、クリアすべき要件が多岐にわたります。ユーザー体験(UX)を損なうことなく非自発解約を効果的に防げるかを判断するため、企画と開発チームで以下の観点で検討を行いました。
開発にあたっての考慮事項
- ユーザー体験
- メッセージの文言が多言語でユーザーに適切に伝わり、アクションをどう取るのかはユーザーが自然にわかる。また、スナックバーの表示位置がLINEのメイン操作を妨げない
- グローバル各地域で提供している異なるサブスクリプションに対し、支払い失敗による無効状態のユーザーだけを正確にターゲットできる
- ユーザーの利用頻度が高い「ホーム」や「トーク」など、特定の画面やタブに絞って表示できる
- 通知メッセージの目的はサブスクリプションを利用し続けたいユーザーにリマインドすること。ユーザーを困惑させることを避けたいため、表示タイミングと期間の工夫が必要。例えば「1日1回、初回訪問時のみ表示」「一定時間での自動消去」「特定の期間(日数)のみ表示」
- ビジネス分析
- 本機能がどれだけサブスクリプションの再登録に寄与したか、修正完了率を正確に計測するためのログを取得 したい
技術調査と検証項目
上記開発の考慮事項により、主に以下の項目についての調査・検証が必要であることがわかりました。
- 表示UI:テキストやボタン配置、スナックバーの表示位置などのカスタマイズ可否
- 表示対象ユーザー:グローバル各地域のサブスクリプションを指定し、支払い失敗ユーザーにのみ正確に表示できるか
- 表示スクリーン:特定の画面やタブ内のみに限定して表示できるか
- 表示タイミングと持続期間:表示頻度を1日1回に限定できるか、またスナックバーの表示時間を指定できるか
- ビジネス分析用のログ取得:支払い方法の修正を完了したユーザー数の割合を正確に把握できるか
LINEアプリにおけるトライアル実装と検証結果
これらを確認するため、実際にAndroid版LINEへのトライアル実装を通じて、In-App Messaging APIの挙動を深く掘り下げて検証しました。
Googleドキュメントのアプリ統合例を参照し、下記コード(一部抜粋)を実装しました。
private fun executeShowMessage(activity: Activity, billingClient: BillingClient) {
val params = InAppMessageParams.newBuilder()
.addInAppMessageCategoryToShow(InAppMessageParams.InAppMessageCategoryId.TRANSACTIONAL)
.build()
val listener = InAppMessageResponseListener { inAppMessageResult ->
// inAppMessageResultはResponseCodeと定期購入対象商品のPurchaseTokenを含める
if (inAppMessageResult.responseCode == InAppMessageResult.InAppMessageResponseCode.NO_ACTION_NEEDED) {
// 特に後続処理する必要はない(PurchaseTokenはnull)
} else { // inAppMessageResult.responseCode == InAppMessageResult.InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED
// 支払い方法が修正され、サブスクリプションステータスが有効に変更された
// PurchaseTokenを使用して後続処理は可能
}
}
// アプリ内メッセージを呼び出す
billingClient.showInAppMessages(activity, params, listener)
}
今回の検証では、Google Playが提供するライセンス テスターの仕組みを利用しました。本番環境よりも短い定期購入の更新期間(数分単位など)を設定し、支払い失敗から猶予期間、一時停止、解約に至るライフサイクルを短時間で再現しました。また、テスト用のクレジットカードが提供され、手動で「有効/無効」に切り替えることで狙ったタイミングで支払い失敗を発生させ、通知メッセージの表示条件を確認することができました。
In-App Messaging APIの動作仕組み
In-App Messaging APIは、LINEアプリとGoogle Play Storeアプリ間のプロセス間通信を利用しています。アプリ側でshowInAppMessagesメソッドを呼び出すと、Binder経由でGoogle Play Storeアプリと通信が行われ、通知メッセージの表示要否の判断やUI処理が実行されます(下図参照)。adb shell dumpsys activity process でLINEアプリのシステムログを確認すると、mConnection に com.android.vending/com.google.android.finsky.billing.iab.InAppBillingServiceが記録されており、LINEアプリがGoogle Play Storeアプリ(com.android.vending)にバインドされていることが確認できました。

また、APIのリスポンスCallbackには、ユーザーが支払い方法を修正した場合のSUBSCRIPTION_STATUS_UPDATEDと、それ以外のNO_ACTION_NEEDEDの2種類が存在します(Googleドキュメント参照)。LINEアプリでCallback取得後に後続の処理を行うことは可能です。注意が必要なのは、SUBSCRIPTION_STATUS_UPDATEDは「ユーザーが支払い方法の修正が必要な期間(猶予期間や一時停止期間)にいつつ支払い方法修正を対応した」場合で、NO_ACTION_NEEDEDはそれ以外の場合(「ユーザーがそもそも支払い方法の修正が必要ない」と「ユーザーが支払い方法の修正が必要な期間にいるが支払い方法修正を行わなかった」の両方が含まれる)です(下図参照)。

表示UIの検証
表示されるスナックバーやボトムシートはGoogle Play Storeアプリが管理するUIであり、アプリ側から文言やレイアウト、ページ遷移をカスタマイズするインターフェースも提供されていないため、UIはカスタマイズ不可でGoogle Play Storeアプリの仕様に従うことがわかりました。
また、スナックバーが表示される間に、アプリ画面はモーダル化されず普通に操作できますが、仕様上の制約でスナックバーの表示位置を指定できません。スナックバー表示中にLINEアプリの下部ナビゲーションバーがオーバーレイされてしまい、ユーザーがタブ移動操作を行えなくなります。
| スナックバー表示前 | スナックバー表示中 |
|---|---|
![]() | ![]() |
スナックバーやボトムシートの操作について、スナックバー自体は左から右へのスワイプで消去可能ですが、視覚的な誘導がないため初めて利用するユーザーは消去方法に迷う可能性があります。スナックバー上の更新ボタンをタップするとボトムシートが表示されてフルスクリーンになり、下図のようにそこで新しい支払い方法の設定もしくは「戻るボタン」をタップして消去はできます。ボトムシートの操作方法は比較的わかりやすいです。

これらのUIは呼び出し元のActivityとライフサイクルを共にするため、アプリを終了してActivityが破棄されると、表示中のスナックバーやボトムシートも同時に消える仕様であることを検証で確認しました。
表示対象ユーザーの検証
APIの仕組み上、Google Play Store側が各地域の支払いステータスを直接参照して判別を行うため、LINEアプリ側で地域別のサブスクリプションを個別に判別するロジックを実装する必要がありません。
表示スクリーンの検証
showInAppMessagesメソッドには引数としてActivityを渡す必要があるため、特定の画面(Activity)を指定して表示させることは可能です。また、同一Activity内の特定のタブでのみ表示させたい場合でも、タブのタッチイベントなどでAPIを呼び出すように実装することで柔軟に対応可能です。
表示タイミングと期間の検証
Google Playの仕様により、猶予期間およびアカウント一時停止期間中の表示頻度は自動的に1日1回に制限されます。ユーザーが修正を行わなかった場合でも、同日内に再び通知が表示されることはありません(Google Playのドキュメント参照)。
一方で、スナックバーが表示された後の「持続時間」の制御には注意が必要です。標準の仕様では、一度表示されたスナックバーは、ユーザーが手動で閉じるか、支払い方法の修正を完了しない限り、画面に表示され続けます。
特定の時間(例えば5秒間など)が経過した後に自動で消去させたい場合、API単体ではそのような設定値が提供されていません。これに対処するため、専用の透明なActivity(Transparent Activity)を介してAPIを呼び出す方法を検討しました。これは、通知を表示するための透明な画面を立ち上げ、指定時間経過後にそのActivity自体を終了させることで、間接的にスナックバーを消去するという手法です。
しかし、このTransparent Activityを用いた制御を行うと、スナックバーが表示されている間は別Activityが前面にある状態となるため、背面にある本来のスクリーン(ホームやトークなど)の操作ができなくなり、影響が生じる可能性があります。
その 対策として背景をグレーアウトさせスナックバーにフォーカスさせる方法は考えられますが、In-App Messagingの仕組み上、Transparent Activityに遷移した後にGoogle Play側が「表示が必要か」を判断することは検証でわかりました。その結果、表示不要と判断されたユーザーに対しても、Activityの切り替えに伴う「一瞬(目安0.5秒以内)の画面のグレーアウト」が発生してしまい、アプリ全体の快適性を損ねる懸念があります。
このように、表示タイミングや期間を細かく制御しようとすると、APIの構造上の制約から新たなUX課題が生まれるトレードオフが存在することがわかりました。
ログ取得の検証
ビジネス分析において「修正が必要だったユーザーのうち、何割が対応してくれたのか」という対応完了率を把握したい場合、下記式で計算すると想定しています。
In-App Messaging APIでは、ユーザーがスナックバー経由で支払い方法の修正を完了すると、SUBSCRIPTION_STATUS_UPDATEDのリスポンスCallbackが返されます。このイベントをフックしてログを送信することで、「通知をきっかけにアクションを起こし、正常に支払い方法を更新したユーザー数」を正確に把握することが可能です。
しかし、完了率の分母となる「支払い方法の修正が必要なユーザーの総数」はAPIだけでは取得できません。なぜなら、APIの動作仕組みにより、もう一つのCallbackであるNO_ACTION_NEEDEDは「ユーザーがそもそも支払い方法の修正が必要ない」と「ユーザーが支払い方法の修正が必要な期間にいるが支払い方法修正を行わなかった」の両方のケースで返される仕様となっているためです(In-App Messaging APIの動作仕組みセクション参照)。これらが混在しているため、APIから得られる結果だけでは、通知の「表示対象となったはずのユーザー数」を特定することができません。
そのため、支払い方法の修正が必要なユーザーの総数を取得したい場合、クライアント側のAPIだけに頼るのではなく、バックエンドサーバー側と連携する必要があると想定しています。
検証結果まとめ
これまでの検証結果を整理し、In-App Messaging APIの導入を判断するためのポイントをまとめます。
実装の迅速さと確実なステータス判別
APIを導入することで、Google Play Storeアプリとのプロセス間通信を介して、ユーザーの購読ステータスに基づいた通知表示や支払い方法の修正フローを、フルスクラッチ からの開発よりシンプルかつ速やかに実装できます。また、グローバル展開における地域別のサブスクリプション判別もGoogle Play側に委ねられるため、アプリ側のロジックを大幅に簡略化できます。
UI/UXのカスタマイズ制限
通知スナックバーの表示位置が固定されているため、LINEアプリの下部ナビゲーションバーを覆ってしまうなど、既存のUI操作を妨げる懸念があります。また、メッセージ内容やレイアウトの変更、スナックバーの自動消去といった細かな制御が難しく、独自のUX基準を満たすにはワークアラウンドが必要ですが、それ自体がフリッカーなどの新たな課題を生むトレードオフが存在します。
ビジネス分析のためのデータ取得制限
APIのCallbackだけでは「修正が必要な全ユーザー数」を特定できません。
おわりに
この検証により、In-App Messaging API は開発スピードと実装の容易さにおいては優れているものの、ユーザー体験の最適化と詳細な分析においては一定の制約があることが明確になりました。
今回の検証で得られた知見を判断材料として、関係部署との継続的な協議を行っています。開発コストを抑えつつ早期にリリースするメリットと、LINEとして提供すべき高品質のユーザー体験のバランスを見極めながら、より価値のある改善をユーザーに届けたいと思います。




