Flutter

FlutterアプリにRevenueCatで買い切りIAPを実装する

個人開発のFlutterアプリに RevenueCat を使って買い切り型のアプリ内課金(IAP)を実装しました。

この記事では、基本的なセットアップから無料版の制限ロジック、ペイウォール設計、App Storeガイドライン対応まで一通りまとめます。

環境

  • Flutter 3.x
  • purchases_flutter(RevenueCat SDK)
  • Riverpod(状態管理)
  • Flavor: dev / prod

なぜ RevenueCat か

iOS・Android 両対応の IAPを自前で実装すると、StoreKit / BillingClient それぞれのハンドリングが必要になります。RevenueCatを使うと:

  • iOS・Androidを統一APIで扱える
  • エンタイトルメント(購入済み判定)の管理が楽
  • ダッシュボードで売上・コンバージョンを確認できる
  • クライアントサイドのAPIキーなので、ソースコードに含めても問題ない

セットアップ

pubspec.yaml

dependencies:
  purchases_flutter: ^8.x.x

Flavor別にAPIキーを設定

dev と prod でRevenueCatのプロジェクトを分けるのがおすすめです。

// flavor.dart
class FlavorConfig {
  static String get revenueCatApiKey {
    switch (flavor) {
      case Flavor.dev:
        return 'appl_xxxxx_dev';  // RevenueCat dev project
      case Flavor.prod:
        return Platform.isIOS
            ? 'appl_xxxxxxxxxxxxxxxxxxxxxxxx'
            : 'goog_xxxxxxxxxxxxxxxxxxxxxxxx';
    }
  }
}

初期化

// main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Purchases.configure(
    PurchasesConfiguration(FlavorConfig.revenueCatApiKey),
  );

  runApp(const MyApp());
}

Pro判定プロバイダー(Riverpod)

購入状態をアプリ全体で参照するために isProProvider を作ります。

// purchase_provider.dart
final isProProvider = FutureProvider<bool>((ref) async {
  final customerInfo = await Purchases.getCustomerInfo();
  return customerInfo.entitlements.active.containsKey('Pro');
});

エンタイトルメントIDは RevenueCat ダッシュボードで設定したものに合わせます(例:Pro単価いくら Pro など)。

無料版の制限ロジック

無料ユーザーに対して機能を制限する例です。

// folder_provider.dart
const int freeFolderLimit = 3;
const int freeHistoryLimit = 50;

Future<bool> addFolder(String name) async {
  final isPro = ref.read(isProProvider).value ?? false;

  if (!isPro && state.length >= freeFolderLimit) {
    // ペイウォールを表示するなどのハンドリング
    return false;
  }

  // フォルダ追加処理
  ...
  return true;
}

テストでは Riverpod の overrideisProProvider をモックします。

// test
final container = ProviderContainer(
  overrides: [
    isProProvider.overrideWith((ref) => AsyncValue.data(true)), // Pro状態で検証
  ],
);

ペイウォール画面の設計

App Storeガイドライン上の注意点

ペイウォールを実装する際、スキップ可能にすることが重要です。スキップボタンなしの強制ペイウォールはApp Storeレビューで指摘・リジェクトされる可能性があります。

┌─────────────────────────┐
│     ⭐ Pro にアップグレード  │
│                         │
│  ✓ 機能A                │
│  ✓ 機能B                │
│  ✓ 広告・ウォーターマーク除去│
│                         │
│  ┌──────────────────┐   │
│  │  ¥980 で購入      │   │
│  └──────────────────┘   │
│     購入を復元          │
│                         │
│     無料で始める →        │  ← スキップ必須
└─────────────────────────┘

価格の表示

RevenueCat からローカライズ済みの価格文字列を取得できます。ハードコードは不要です。

// paywall_screen.dart
final offerings = await Purchases.getOfferings();
final package = offerings.current?.lifetime;

if (package != null) {
  final price = package.storeProduct.priceString; // "¥980" や "$4.99" など自動取得
}

ユーザーのストア設定(国・通貨)に応じて自動的に正しい通貨で表示されます。

購入処理

Future<void> purchase(Package package) async {
  try {
    final customerInfo = await Purchases.purchasePackage(package);
    final isPro = customerInfo.entitlements.active.containsKey('Pro');
    if (isPro) {
      // 購入成功
    }
  } on PlatformException catch (e) {
    final errorCode = PurchasesErrorHelper.getErrorCode(e);
    if (errorCode == PurchasesErrorCode.purchaseCancelledError) {
      // ユーザーキャンセル(エラー表示不要)
    } else {
      // エラー表示
    }
  }
}

購入の復元

App Storeガイドラインでは、消耗品以外のIAPには購入を復元するボタンを設置することが求められています。

Future<void> restore() async {
  final customerInfo = await Purchases.restorePurchases();
  final isPro = customerInfo.entitlements.active.containsKey('Pro');
  // isPro を元に UI を更新
}

Simulatorで価格を確認する

Simulatorでは RevenueCat の価格取得時にデフォルトでUS(ドル表示)になります。

日本円(¥)で確認したい場合:

Xcode > Product > Scheme > Edit Scheme > Options > StoreKit Configuration

を設定して、Storefrontを Japan (JPY) に変更します。

ただし、実機のSandboxアカウント(日本アカウント)で確認するのが一番確実です。

オンボーディングへの導線

onboarding フローの末尾にペイウォール画面を挟むのが定番です。

Value(機能紹介)→ 権限取得 → Pro upsell → ホーム

この時も「無料で始める」ボタンを目立つ位置に置いて、スキップを容易にしておきます。

まとめ

項目ポイント
APIキーFlavor別に分けて直接記載でOK(クライアントキー)
Pro判定entitlements.active.containsKey で確認
価格表示storeProduct.priceString でローカライズ自動対応
ペイウォールスキップ可能にしないとリジェクトリスクあり
復元ボタン必須(App Storeガイドライン)
テストRiverpod の override でPro状態をモック

RevenueCatを使うと、クロスプラットフォームのIAP実装が大幅に楽になります。特に「購入済み判定」をエンタイトルメントとして扱える点が、自前実装に比べて大きなメリットです。