RevenueCatは、モバイルアプリ向けのサブスクリプションプラットフォームです。アプリ内課金を簡単に扱えるようになるSaaSといったところでしょうか。
最新の料金体系は、こちらから確認することができます。2020年11月28日現在、無料プランでは、月$10,000までの決済が可能になります。日本円で約100万円/月なので、大抵の場合はこちらのプランで十分でしょう。それ以上の決済額の場合やその他便利な機能を利用したい場合はAnalyzeプラン($119/月)以上に加入する必要があります。Analyzeプラン以上ではWebhookなど便利な機能を利用することができるようです。
※ (2022/03/23追記)Starterプランが追加され、月$1,000までは無料、それ以降は$1,000ごとに$8がかかるプランが用意されました。Webhookを活用して実装を楽したい場合はStarterプランもオススメです。この本では、上記のタイミングで実装しているためFreeプランでの実装方法を紹介しています。今後強い要望があればWebhookでの実装を追記いたします。
セットアップの方法も英語にはなりますがドキュメントにしっかりと書かれています。つまずきやすいAppStore/PlayStoreの設定まで書かれているため、RevenueCatを利用しない人でもありがたいくらいです。提供されているSDKも豊富で、iOS/Android/Flutter/ReactNativeを始めとして様々なプラットフォームやフレームワークで利用できるように提供されています。詳しくはこちらをご覧ください。
RevenueCatのトップページの文言を読むと、「サーバーのコード無しでクロスプラットフォームのアプリ内課金を管理できる」と書かれています。一体どういう仕組みなのでしょうか。
アプリ内課金の仕組み
前提として、アプリ内課金ではレシート
という用語が出てきます。これはアプリ内課金を行なったときにApp/Play Storeが発行してくれるデータで、購入した内容や日時、サブスクリプションの場合は期限が切れる日時などが入っており、アプリ側で取得することができます。通常は、base64エンコードした文字列にしてサーバーとやり取りします。このレシートが改ざんされていないかどうかはApple/Googleに問い合わせるしかなく、問い合わせにはSecretKey的なものを使用するのでセキュリティの観点から通常はバックエンドで行います。
上記を踏まえた上で、アプリ内課金を実装する場合の一般的な処理の流れは以下のようになります。
- アプリ側で決済処理を行う
- アプリ側で取得できるレシートをバックエンドに送る
- バックエンドで受け取ったレシートをApple/Googleに問い合わせてレシートを検証する
- レシートが正しい場合はレシート情報を元にサブスク加入状態にする
RevenueCatの仕組み
RevenueCatは、上述のバックエンドを提供してくれるため「サーバーのコード無しでクロスプラットフォームのアプリ内課金を管理できる」わけです。さらに、アプリ側の複雑な決済処理やRevenueCatバックエンドとのやりとりは、公式が提供しているSDKを使えばまるっと行なってくれます。
Flutterの場合、公式SDKを使うと、サブスクリプションに加入するコードは以下で完結します。本当にコレでいいのかと思ってしまうくらいには単純です。
await Purchases.purchaseProduct('your_product_id');
サブスクリプション加入状態は以下のように確認することができます。先述の通り、レシート検証はRevenueCatが行なってくれているため、APIを呼ぶだけで課金状態かどうかがわかるようになっています。このフラグを元に有料機能へのアクセスを制御することになります。
final purchaserInfo = await Purchases.getPurchaserInfo();
final isSubscribing = purchaserInfo.entitlements.all['your_entitlement_identifier'].isActive;
購入する際にproduct_id
ベースで処理を行なっていますが、加入状態を確認するときにはentitlement_id
ベースで処理しています。これは例えば、プレミアム会員というサブスクリプションが存在している場合に、月額と年額の2つの支払い方法があるとすると、加入する際はどちらの払い方で加入するかが必要ですが、そのユーザーが加入しているかどうかはどちらの方法で支払われていたとしてもプレミアム会員かどうかで確認しますよね。これはそういうことで、RevenueCatでは、プレミアム会員の立ち位置のものはentitlementと言い、支払い方法などの違いはそれぞれproductと呼ばれています。これはセットアップの章でも解説しますので詳しく知りたい場合はそちらも合わせてご覧ください。
これではうまくいかないケース
上述の実装では自サービスのバックエンドを介することなく、アプリ側のみでサブスクリプション加入状態を確認しています。アプリ側で画面の導線や機能を制御するだけで良い場合はこの実装で全く問題ありません。
しかし、自サービスのバックエンド側でサブスクリプション加入状態を元に、アプリの機能を提供するかどうか決めるようなサービスの場合、このままではうまくいきません。例えば、サブスクリプション加入ユーザーにのみお得なプッシュ通知を毎日10時に送るというようなバックエンドから機能を提供するような場合です。上記のコードでアプリ側からRevenueCatのSDKを通じて取得できるサブスクリプション加入状態を、自サービスのバックエンドに送信して利用すればよいのでしょうか?それはあまりにも脆弱で簡単にクラックできてしまい、行なうべきではありません。では、どうすればよいのでしょうか?
以降の有料チャプターではこの問いに対する答えも含めて
- FlutterでiOS/Androidアプリにアプリ内課金でサブスクリプションを実現するためのRevenueCatとの連携と設計
- Flutterで実装する上での重要なポイント(コード付き)
- Firebase(Firestore, Authentication, Functions)を用いたバックエンドの設計と重要な部分の実装(TypeScript)
を中心に解説していきます。アプリ側はDartのコードにはなりますが、各プラットフォームでもSDKのインターフェースや仕様は同じだと思うので十分参考になるかと思います。ちなみに、RevenueCatや各Storeの設定はドキュメントに書かれているので詳しくはお話しませんのでご注意ください。また、コピペで動く!というレベルで全て書いているわけではありません。
宣伝ですが、Flutter x Firebase x RevenueCatの導入支援のお仕事も受け付けております。Flutterの部分はモバイルであればどのプラットフォームでも問題ありません。もしご興味がある場合はTwitterまでDMください。
また、RevenueCatを用いた非消耗型の課金は下記のsakutaroさんの記事が参考になります。