プログラミング

モバイルのサブスクリプションのアーキテクチャ

mobile-subscription-architecture

運営しているアプリ(iOSとAndroid)に月額のサブスクリプション機能を追加しました。

調査した結果、現時点で一番良さげなアーキテクチャを実装したのですが、その過程で色々と悩むことが多かったです。

今回の記事では、サーバーサイドを含めた全体的なアーキテクチャを記載します。(iOS、Androidのそれぞれの詳細なコードは記載していません。)

アーキテクチャのポイントは以下の2点です。

  • AppleやGoogleが提供する通知機能を用いて、最新のレシート情報を常にサーバー側に持たせる。
  • モバイルアプリでの立ち上げ時に、サーバー側で保管しているレシート(支払い情報)を確認し、サブスクリプションを契約しているかどうかを、モバイルにレスポンスする。

参考情報になりますが、今回のアプリでのサブスクリプションの特典は以下の2点です。

  • すべての広告の非表示
  • 翻訳機能などの機能制限開放
mobile-subscription-architecture

サブスクリプションのアーキテクチャ概要

これまでのサブスクリプションでは、モバイル側でアプリ立ち上げ時等にサブスクリプションの状態を確認し、最新情報をサーバーサイドに送信する必要がありました。(サーバーサイドで色々するなら、というかユーザーからの問い合わせに対応するには、どこかにレシート情報を保管する必要があった。。)

しかし、2020年時点では、サブスクリプションが更新されると、通知を直接サーバー側に送ってくれるというサービスがiOS、Androidともにありました!確認してみると、どうやらここ1~2年ぐらいで追加された機能のようです。

このサービスを用いて、常に最新の支払い情報をサーバー側にもたせておくことで、モバイルでいろいろやる必要がなくなりました。

アーキテクチャの簡単な概要図

mobile-subscription-architecture
  • App Store Server Notification (iOS)とRealtime Developer Notification (Android) を用いて、最新のレシートを常にサーバーサイドに保管する。
  • モバイルでのアプリ立ち上げ時のユーザー情報取得時に、サーバー側に保管している支払情報を元に”is_premium: true or false”をレスポンスする。

というアーキテクチャになります。

これにより最新のレシート情報が常にサーバーに保管されていることになり、モバイルはその情報を確認するだけでよくなります。

このアーキテクチャを実現するために、以下のAppleとGoogleの2つのサービスを使用します。

AppleのApp Store Server Notification

[Apple Developer Documentation]

こちらは、定期購読のステータスの変更及び更新が行われる度に、事前に指定したエンドポイントにHTTP POSTによりJSONを飛ばしてくるサービスです。

Androidの Realtime Developer Notification

Google Play Storeにも、Realtime Developer Notificationという、iOSのApp Store Server Notificationと同等のサービスがあります。

同じく、定期購読が更新されるたびに、Cloud Pub/Subにメッセージが飛んでくるため、その通知を受信することで、レシート情報を更新します。

プロセスフロー

定期購読に関する処理には、主に①新規購読、②継続更新、③解約の3つのプロセスがあります。

それぞれのプロセスを図を用いて解説します。

①新規購読プロセス

mobile-subscription-architecture
  • モバイル内で新規購読ボタンタップで、AppleやGoogleとの通信で定期購読処理を実行します。
  • 成功すると、最新のレシート情報をサービスのサーバーに格納します。
  • この時、レシート情報を一意に判別する値はiOSでは”original_transaction_id”、Androidでは、”purchace_token”となります。これらのデータを、ユーザーと紐付けてDBに保存します。
  • 新規購読処理が完了し、サーバーへのレシート情報保存が完了すると、購読済みユーザーとして利用できるように、モバイルでのUIの更新等を行います。

②継続更新プロセス

継続更新では、上記にも記載したように、AppleのApp Store Server Notification、Google PlayのRealtime Developer Notificationを用いて、定期購読の更新のたびに通知を受け取り、最新のレシート情報をDBに保存します。

mobile-subscription-architecture

その結果、モバイルでは、アプリ立ち上げ時にサーバー内のレシート期限を確認するだけで、購読済みか否かを判別することが可能になります。

Androidの場合は、間にCloud Pub/Subが入ることになります。

詳細は以下の記事を参照してください。

③解約プロセス

解約は、iOS、Androidともに、アプリ内で行うことは出来ません。AppleではApp Storeアプリや設定アプリからのサブスクリプション設定での解約、AndoridでもGoogle Play Storeからの解約処理が必要となります。

mobile-subscription-architecture

解約処理がなされると、それ以降はレシート情報が更新されなくなるので、自動的に期限が切れ、時間とともに無料ユーザーに戻るという流れです。特に処理は必要ありません。

最新のレシート情報を保存するテーブル

サービスのサーバーに最低限保持しておく必要のあるデータは以下の通りです。

  • user_id(どのユーザーのレシートか)
  • expired_date(レシートの有効期限)
  • receipt_id(レシートを一意に判別する)

加えて、iosかandroidかというdevice情報やレシートのオリジナルデータ等も含めると管理が楽になるかもしれません。

適宜要件に併せて、データベースにtableを実装してください。

懸念点と対処法

このアーキテクチャでいくと、サーバーから送られてくる、premiumのbool値のみを元に、購読済みかどうかを判断することができるので、モバイルの実装が楽になります。

ただし、確実にサービスのサーバー内に最新のレシート情報を保存出来ているということが必要条件になってきます。

懸念①:AppleやGoogleから購読更新の通知が飛んで来なかったら?

もし仮に、AppleやGoogleの課金サーバーが(落ちることはさすがにないと思うが)レスポンスに時間がかかりすぎるなどなんらかの原因で疎通がうまく行かなかった場合どうなるか?お金を支払っているにも関わらず、無料アカウントとして認識されてしまうユーザーが出てくる可能性があります。

mobile-subscription-architecture

懸念①の対応:期限切れのレシートも念の為に最新レシートを確認するバッチ処理を行う

何があるかはわかりませんので、この懸念に対して「期限切れのレシートの最新情報を念の為取得する」という処理を1日に1回バッチで回すことにしました。

mobile-subscription-architecture

懸念②:Androidで課金していてiOSに乗り換えた場合はどうなる?

例えば、Androidで課金していて、iOSに乗り換えるユーザーがいた場合はどうなるのか考えてみました。

結論は、対策の必要なしと判断しました。

mobile-subscription-architecture

Andoridでの課金が終了したとしても、最初のうちはレシート期限が残っているので、iOSに変わってもそのまま購読済みユーザーとしてアプリを利用することが出来ます。

期限が切れると自動的に無料会員に戻るので、その後必要であればiOSで再度課金処理を行なっていただくのみ、という流れです。

アプリや公式サイトにて、サブスクリプションの解約手順を明確に記載する、ぐらいかなと思います。これはすでに行なっていたので、特に対応は不要と判断しました。

モバイルアプリのサブスクリプション実装のまとめ

以上、iOSとAndroidでの定期購読を、サーバーサイドを含めたアーキテクチャを紹介してきました。

実装するにあたって、非常に調査に時間を使ってしまったことや、AppleのSnadboxがドキュメント通りに動かないこと、リリースしてからもAndoridでのacknowledge処理を忘れてしまい、すべて自動でシステムによる解約処理が行われてしまうなど、様々なトラブルがありましたが、現在なんとか運用出来ています。

定期購読処理をどのように実装していくのかを自分の中で整理するために纏めていたパワポも使って今回記事にしてみました。

参考になりましたら幸いです。