💰

React NativeでRevenue Catを使ってサブスク課金を実装する

2023/12/07に公開

本記事はReact Native アドベントカレンダー 2023の 7 日目の記事です。

今回は Revenue Cat というサービスを利用して、React Native でサブスクリプション課金を実装したので、その特徴や導入方法をご紹介します。

Revenue Cat: https://www.revenuecat.com/

Revenue Cat とは?

モバイルアプリのサブスクリプション課金実装を簡単にしてくれる Saas です。

サブスクリプション実装に必要なバックエンド側の機能が一通り揃っています。

またアプリ側の実装に必要なライブラリも各種プラットフォーム向けに提供されていて、React Native の場合は react-native-purchasesという npm パッケージを利用します。

React Native だけでなく、swift, Kotlin はもちろん Flutter, Unity などモバイルアプリ開発の様々なフレームワークに対応しています。

メリット

iOS, Android の実装差分を吸収できる

React Native は言わずと知れたクロスプラットフォームのフレームワークですが、課金機能となるとiOS, Android の両方で実装する必要がありますよね...

しかし Revenue Cat を利用すると、react-native-purchasesを通して iOS, Android を同じ JavaScrirpt コードで実装できます。

バックエンド側の実装が不要に

またレシート検証であったり、特にサブスクリプションの場合は定期的に期限切れをチェックするなどバックエンド側の実装が必要にあります。

Revenue Cat では購入やサブスク状態のチェックなど、必要なバックエンド機能が既にプラットフォーム側に実装されているので、開発者はバックエンドのコードを書くことなくサブスク課金を実現できます。

ダッシュボードで売上などを閲覧できる

ダッシュボードでは売上などの情報を iOS, Android まとめて閲覧できます。

通常だと app store connect, google play を別々に確認しないといけないので、まとめて見れるのは便利そうです。

デメリット

Revenue Cat 独自の概念がある

一方でデメリットとしては、Revenue Cat 独自の概念がいくつかあって、それを理解する必要があります。

本記事ではこのあたりの独自概念についてもご紹介したいと思います。

手数料がかかる

手数料は売上高に応じてかかるレベニューシェア方式で、ひと月あたりの売上が 2500 ドルを超えると、売上の 1%を手数料として支払う必要があります。

逆に言えば 2500 ドル/月(約 37 万円)の売上に到達するまでは無料で使えます。もし個人で使うとしたら、当分は無料で使えそうです。

Pricing: https://www.revenuecat.com/pricing/

外部サービスに依存することの不安

便利な外部サービスを利用するときの宿命ですが、万が一 Revenue Cat がサービス終了してしまったら厄介ですよね...

この辺は、初期の実装が簡単になることとのトレードオフですが。

導入方法をざっくりと

実際に導入する際には公式のドキュメントを見ていただくとして、ここでは雰囲気を感じられるようにざっくりと全体の流れをざっくりご紹介します。

1. app store connect, google play console で商品を登録する

このあたりは通常のサブスク課金実装と同じです。

2. Revenue Cat にプロジェクト作成

アプリごとにプロジェクトを作成します。
この際に、Bundle ID (iOS) / Package Name (Android) を登録し、実際の iOS/Android アプリと Revenue Cat プロジェクトを紐づけます。

3. 商品を Revenue Cat 側に登録する

Revenue Cat の管理画面で以下を登録します。ここで少し独自の概念が登場してややこしいです。

  • Products
    • 実際の iOS, Android の商品と紐付きます。
    • またこの iOS+Android の商品セットの塊は、Revenue Cat 内では"Package"という名前で呼ばれたりしています。
  • Entitlements
    • Entitlement とは、ユーザが享受できる機能のレベルを表します。
    • 例えば課金プランに、Gold プランと Silver プランがあって、それぞれで提供される機能が異なる場合、Gold と Silver という Entitlement を用意することになります。
    • 実際には 1 つだけの Entitlement で運用することも多いと思います。
  • Offerings
    • いま提供する提供する商品群を予め Offering という集合にしておいて切り替えることができます。
    • 例えば期間限定で割引料金プランを試したいときに、default と discount の offering を用意しておいて、それをワンタッチで切り替えることができます。
    • これも実際のところ default の offering を 1 つだけ使う運用が多い気がします。

以下の公式のドキュメントの図が分かりやすいです。

products
(ref: https://www.revenuecat.com/docs/entitlements)

4. SDK セットアップ

react-native-purchasesという Revenue Cat が提供している npm パッケージを利用します。

yarn add react-native-purchases

Expo の managed workflow でも使えます!

bare workflow だとネイティブ側の設定も少しあります。

詳細は公式ドキュメントへ。

5. SDK 初期化とサブスクの有効チェック

Revenue Cat から提供されるpublic_sdk_keyを使って初期化します。

Purchases.logIn(<自サービスのユーザーID>)とすることで Revenue Cat 側のユーザー情報と紐づけます。

前のステップで"premium"とう名前の entitlement を設定していたとします。"premium"のサブスクが有効かどうかは、以下のようなコードで確認できます。

if (typeof customerInfo.entitlements.active["premium"] !== "undefined") `

まとめると以下のような流れになります。

import Purchases from "react-native-purchases";

// Revenue Catの管理画面で確認
const APIKeys = {
  apple: "appl_xxxxxxxxxxxx",
  google: "goog_xxxxxxxxxxx",
};

export const AuthScreen: React.FC<Props> = ({}) => {
  useEffect(() => {
    const exec = async () => {
      // 何らかのログイン処理
      const user = await signIn();

      //
      // purchase関連の初期設定
      //

      // 1. SDK初期化
      if (Platform.OS === "android") {
        await Purchases.configure({ apiKey: APIKeys.google });
      } else {
        await Purchases.configure({ apiKey: APIKeys.apple });
      }

      // 2. アプリのユーザーIDをRevenue CatのユーザーIDを紐づけし、ユーザー情報を取得
      const { customerInfo } = await Purchases.logIn(user.id);

      // 3. サブスクが有効かチェック
      //    この例では"premium"というentitlementを設定済みだとする
      if (typeof customerInfo.entitlements.active["premium"] !== "undefined") {
        // サブスク(entitlement = "premium")が有効!
      } else {
        // サブスク(entitlement = "premium")が無効
      }
    };
    exec();
  }, [setPowderAlertUser, setSubscriptionActive]);
};

6. 商品一覧の表示

Purchases.getOfferings()で現在有効な商品(offering)を取得できます。

export const PurchaseScreen: React.FC<Props> = ({}) => {
  const [currentOffering, setCurrentOffering] =
    useState<PurchasesOffering | null>(null);

  useEffect(() => {
    const setup = useCallback(async () => {
      // 商品情報を取得
      const offerings = await Purchases.getOfferings();
      setCurrentOffering(offerings.current);
    }, []);
    exec();
  }, [setPowderAlertUser, setSubscriptionActive]);

  return (
    <>
      <Text>商品一覧</Text>
      {currentOffering.availablePackages.map((pkg, i) => {
        return (
          <View>
            <Text>{pkg.product.identifier}</Text>
            <Button onPress={() => onPressPurchase(pkg)}>購入する</Button>
          </View>
        );
      })}
    </>
  );
};

参考: https://www.revenuecat.com/docs/user-ids

7. 商品の購入

上記のコードの続きで、購入ボタンが押されたときの処理です。

Purchases.purchasePackage(pkg)で購入できます。

// 購入ボタンを押したときの処理
const onPressPurchase = async (pkg: PurchasesPackage) => {
  // 購入処理
  const { customerInfo: _customerInfo, productIdentifier } =
    await Purchases.purchasePackage(pkg);

  if (typeof customerInfo.entitlements.active["premium"] !== "undefined") {
    // 購入完了!
    // 課金機能をアンロックする
  }
};

実際にこのpurchasePackage()を実行すると、OS の購入モーダルが表示されて購入の操作をすることになります。

参考: https://www.revenuecat.com/docs/making-purchases

8. 購入の復元

もしユーザーがスマホを買い替えたときなど、以前の購入を復元する必要があります。

同じストアアカウント(apple, google)で以前に購入したコンテンツを再び有効にすることができます。

// 復元ボタンを押したときの処理
const onPressRestore = async () => {
  const customerInfo = await Purchases.restorePurchases();
  if (typeof customerInfo.entitlements.active["premium"] !== "undefined") {
    // 有効なサブスク有り!
    // 課金機能をアンロックする
  }
};

参考: https://www.revenuecat.com/docs/restoring-purchases

9. REST API や webhook もある

場合によっては自社のサーバーからサブスクの有効状態をチェックしたい場合があると思います。

そのようなケースを想定して REST API が提供されています。

例えば以下のような API で特定ユーザーのサブスク状態をチェックできます。

curl --request GET \
     --url https://api.revenuecat.com/v1/subscribers/<user-id> \
     --header 'X-Platform: ios' \
     --header 'accept: application/json'

REST API: https://www.revenuecat.com/reference/basic

あるいは Webhook も用意されており、イベント発生時にトリガーされます。
自社 DB とサブスクの状態を同期したいときなどに便利でしょう。

webhook: https://www.revenuecat.com/docs/webhooks

Expo managed workflow での使い方

特筆すべきは managed workflow でも使えることです。

少し前の私の常識では、アプリ内課金を実装する = bare workflow にしないといけない、でしたが、今は必ずしもそうではないようです。(managed workflow 大好き!)

ただアプリ内課金ではネイティブライブラリを使用することになるので、development build というものを作成する必要があります。

development build とは何か?

development build=あなた専用の Expo Go アプリ、というどこかで見た説明がわかりやすかったです。

react-native-purchasesに関するネイティブコードが入った版の Expo Go アプリを作るイメージでしょうか。

development build の作成方法は公式のドキュメントや Revenue Cat のブログ記事が分かりやすいです。

ちなみに課金のテストはシミュレータではなく、実機で行う必要があります。

まとめ

以上になります!

外部サービスに依存する不安はありつつも、課金に関するコード実装を大幅に削減できることは魅力だと感じました。

公式曰く、37,000 行のコード実装を削減できるとのことです。確かに各 OS でのレシート検証や復元の処理など、それくらいのコード量になりそうな気はします。

サブスク機能を実装したいときは、一度検討してみる価値はあるかと思います!

Discussion