このエラーの意味

0:03:00

RevenueCat が 「no products found」「empty offerings」「Error fetching offerings」を報告するとき、意味していることは一つです。SDK は RevenueCat のバックエンドに到達して Offering の構成を取得できたものの、連鎖のどこかで商品 ID を実際に購入できるストアの商品へ解決できなかった、ということです。その結果、現在の Offering が nil になるか、availablePackages がゼロの Offering になり、ペイウォールには何も表示されません。

これは RevenueCat で圧倒的に多い問題です。よい知らせもあります。原因は必ず構成の問題で、その一覧は有限です。しかもそのすべてがこのページで網羅されています。解決するには、getOfferings() を呼ぶたびに SDK がたどる連鎖を理解する必要があります。

text
Store product (App Store / Google Play)        ← must exist, be active/approved & available
        ↓  product ID must match character-for-character
RevenueCat Product (Product Catalog → Products) ← must exist for YOUR platform
        ↓  must be attached to a package
Package (e.g. $rc_monthly)                      ← must contain a product for YOUR store
        ↓  must belong to an offering
Offering (e.g. "default")                       ← must be marked Current
        ↓  fetched via your public API key (appl_… / goog_…)
Your app                                        ← bundle ID / package name must match
        ↓  device store must be able to serve the products
The device                                      ← agreements signed, app published, tester set up

この連鎖の環が一つでも切れていると、Offering は空になります。どちら側を見ればよいかは、たいていエラーメッセージが教えてくれます。RevenueCat 側か、ストア側かです。

エラーメッセージ図鑑

次に挙げるものは、すべて同じ系統の問題です。自分のものを探してください。

text
# iOS: the classic
[RevenueCat] 🍎‼️ Error fetching offerings - The operation couldn't be completed.
There's a problem with your configuration. None of the products registered in the
RevenueCat dashboard could be fetched from App Store Connect (or the StoreKit
Configuration file if one is being used).
More information: https://rev.cat/why-are-offerings-empty

# Android: platform mismatch (ConfigurationError)
[RevenueCat] 😿‼️ Error fetching offerings -
PurchasesError(code=ConfigurationError, underlyingErrorMessage=You have configured
the SDK with a Play Store API key, but there are no Play Store products registered
in the RevenueCat dashboard for your offerings. If you don't want to use the
offerings system, you can safely ignore this message. To configure offerings and
their products, follow the instructions in https://rev.cat/how-to-configure-offerings.
More information: https://rev.cat/why-are-offerings-empty

# iOS: products exist but were never submitted
WARN: ⚠️ RevenueCat SDK is configured correctly, but contains some issues…
  ⚠️ monthly (monthly): This product's status (READY_TO_SUBMIT) requires you to
     take action in App Store Connect before using it in production purchases.

# Android: store can't serve the SKU
BillingClient: Product not found - SKU is not available for purchase
BillingResponseCode: ITEM_UNAVAILABLE (4)

# Both: silent variant (no error at all)
offerings.current == nil          // or
offerings.current.availablePackages.count == 0

# Both: network family (different root cause: connectivity, not configuration)
Error: Unable to fetch offerings: NetworkError / Request timed out
Note: このエラーでアプリがクラッシュすることはありません。ペイウォールに商品を表示できないだけです。ただし原因が未署名の Agreement や未提出の商品にある場合は、確実に本番まで持ち越されます。回避策で再試行してごまかすのではなく、きちんと直してください。

5 分でできる切り分け

0:03:00

ほかを確認する前に、デバッグログを有効にしてください。以降の診断は、いずれもログの内容から始まります。

text
Swift:         Purchases.logLevel = .debug          (before configure)
Kotlin:        Purchases.logLevel = LogLevel.DEBUG  (before configure)
React Native:  Purchases.setLogLevel(LOG_LEVEL.DEBUG)
Flutter:       await Purchases.setLogLevel(LogLevel.debug)

次に、この表で自分の症状を照合してください。各行は、検証済みの対処を載せたセクションにリンクしています。

症状もっとも可能性の高い原因移動先
ログに ConfigurationError… Play Store API key, but there are no Play Store products registered と出る (または App Store 版)Offering にもう一方のストア用の商品しか入っていないダッシュボードの対処、原因 D
offerings.currentnil、ログにストアのエラーはないCurrent に設定した Offering がない、またはパッケージ・商品が入っていないダッシュボードの対処、原因 A〜C
「None of the products registered… could be fetched」、iOS、初めての連携Paid Applications Agreement が Active でない、または StoreKit Configuration ファイルなしのシミュレーターiOS の対処、原因 1 と 6
ログに READY_TO_SUBMIT の警告が出る / 商品が「Missing Metadata」と表示される商品が未完成、またはビルドと一緒に提出したことがないiOS の対処、原因 2 と 3
RevenueCat の REST API / ダッシュボードはすべて正しいのに、iOS で availablePackages が 0バイナリを一度もアップロードしておらず、StoreKit が商品 ID を有効化していないiOS の対処、原因 4
自分では動くのに、別の国のユーザーで失敗する商品の提供が特定の国や地域に限定されているiOS の対処、原因 7 / Android の対処、原因 6
Android: 実機で ITEM_UNAVAILABLE または Offering が空アプリがテストトラックにない、テスターが opt-in していない、インストールしたビルドが Play 由来でないAndroid の対処、原因 1〜3
Android: ダッシュボードに認証情報の警告が出る、または設定が 36 時間以内Play サービス認証情報が無効、またはまだ反映中Android の対処、原因 5
Expo Go の React Native: ネイティブモジュールがない / 商品が空Expo Go はネイティブモジュールを実行できないため、開発ビルドが必要クロスプラットフォーム
getProducts() は動くのに getOfferings() が空切れているのは、ダッシュボードの商品と Offering の間ダッシュボードの対処
昨日は動いたのに、今日は空。何も変えていないストアやキャッシュの反映、または誰かが Offering を編集したまだ解決しない?
NetworkError / タイムアウト接続、VPN/ファイアウォール、まれにサービス障害。構成の問題ではないまだ解決しない?
いちばん役立つ診断: デバッグログには、RevenueCat がストアに要求した商品と、返ってきた内容が表示されます。ログに商品 ID の要求は出ているのに返りがゼロなら、問題はストア側です(ステップ 04〜05)。ログに商品 ID の要求すら出ないなら、問題はRevenueCat ダッシュボード側です(ステップ 03)。

RevenueCat ダッシュボードの対処

0:04:00

RevenueCat ダッシュボードで、次を順に確認してください。連鎖のダッシュボード側の切れをすべて網羅しています。

A. Offering を一つ「Current」にする

getOfferings() は、current プロパティで現在の Offering を返します。Product Catalog → Offerings を開き、Current バッジが付いた Offering がちょうど一つあるか確認してください。なければ、その Offering のメニューから Make Current を選びます。これがないと、ほかがすべて完璧でも offerings.currentnil になります。

B. 現在の Offering にパッケージがあること

現在の Offering を開きます。Packages セクションには、少なくとも一つのパッケージ (例: $rc_monthly$rc_annual) が並んでいる必要があります。パッケージがゼロの Offering は、エラーを出さずに空のペイウォールを返します。

C. どのパッケージにも、自分のプラットフォーム用の商品が必要

各パッケージを開き、紐付いた商品を確認します。パッケージは App Store の商品 Google Play の商品を両方保持でき、リリースするすべてのプラットフォームに一つずつ必要です。iOS の商品しか入っていないパッケージは、iOS でテストすると問題なく見えますが、Android では静かに壊れます。

D. ConfigurationError によるプラットフォームの不一致

これは下記エラーそのものの原因で、純粋にダッシュボードの構成の問題です。

text
PurchasesError(code=ConfigurationError, underlyingErrorMessage=You have configured
the SDK with a Play Store API key, but there are no Play Store products registered
in the RevenueCat dashboard for your offerings. …)

言い換えると、アプリは goog_ の API キーで動いているのに、現在の Offering のパッケージに入っている商品がすべて App Store のものになっています (appl_ キーで Play 専用の商品という逆パターンもあります)。SDK が話しかけるストアには、取得すべきものが何もありません。対処は次のとおりです。

  1. Product Catalog → Products+ New をクリックし、足りないストアの商品を作成します (Google Play の商品 ID。基本プランを持つサブスクリプションなら例: premium_monthly:monthly-autorenew。自動インポートも可)。
  2. 現在の Offering → 各パッケージ → Edit と進み、既存の商品の隣に新しいストアの商品を紐付けます。
  3. アプリを再実行します。どのパッケージにもアクティブなストア用の商品がそろえば、エラーは消えます。

E. 商品 ID はストアと 1 文字単位で一致させる

Product Catalog → Products で、各 ID を App Store Connect / Google Play Console と突き合わせます。一致は大文字小文字と空白を区別します。com.app.monthlycom.app.Monthly であり、末尾の空白 (貼り付け時に紛れ込みやすい) は静かに破綻させます。迷ったら ID を削除し、慎重に貼り直してください。

F. 正しいプロジェクトの、正しい API キー

  • Project Settings → API Keys にある公開のプラットフォーム別キーを使ってください。App Store は appl_…、Play は goog_… です。アプリにシークレットの sk_… キーを使ってはいけません。
  • RevenueCat プロジェクトが複数ある場合(例: staging/production)、そのキーが、編集しているダッシュボードのプロジェクトのものか確認します。別プロジェクトのキーは、そのプロジェクトの(空の)Offering を取得します。「全部設定したのに!」という典型的な落とし穴です。
  • Apps & providers のバンドル ID (iOS) / パッケージ名 (Android) は、ビルドが実際に使うものと一致している必要があります。.debug やフレーバーのサフィックス (例: com.app.dev) も含めてです。サフィックス付きのデバッグビルドは、com.app として構成したアプリとは一致しません。
チェックリストまとめ: ストアに商品がある → RevenueCat に同一の商品 ID → 自分のプラットフォーム用に商品をパッケージに紐付け → パッケージを Offering に → Offering を Current に → 正しい公開 API キー + 一致するバンドル ID / パッケージ名。

iOS / App Store の対処

0:05:00

解決済みのコミュニティスレッドをもとに、実際に真の原因だった頻度の高い順に並べています。

1. Paid Applications Agreement(最も見落とされやすい原因)

この Agreement が Active になるまで、Apple はアプリ内課金の商品を一つもアプリに配信しません。サンドボックスも含めてです。しかも静かに失効することがあり、「何か月も動いていた」アプリが突然ペイウォールを空にするのはこのためです。複数のコミュニティスレッドで確認された対処は次のとおりです。

  1. App Store ConnectBusiness(旧 Agreements, Tax, and Banking)を開きます。
  2. Paid Applications AgreementActive と表示されている必要があります。「Pending」でも「New」でも失効でもいけません。求められたら最新の条項に同意してください。
  3. Banking InformationTax Forms(米国は W-9、米国外は W-8BEN)を完了します。両方が済むまで Agreement は Active になりません。
  4. 完了後、Apple の確認まで 24〜48 時間ほどかかります。

2. 商品のステータス: メタデータを完成させる

App Store Connect → 対象アプリ → Subscriptions / In-App Purchases で、各商品が Missing MetadataDeveloper Action Needed の状態であってはいけません。取得できる状態にするには、少なくとも 1 地域の価格、少なくとも一つのローカライズ(表示名 + 説明)、審査用スクリーンショットが必要です。Ready to Submit はサンドボックスや StoreKit のテストには問題ありません。本番には Approved が必要です。

3. サブスクリプションは Subscription Group に入れる

Apple は、自動更新サブスクリプションをすべて Subscription Group に属させることを求めます。API で作成したり急いで作ったりした場合は、各商品がグループに割り当てられているか確認してください。グループのないサブスクリプションは解決しません(これは広く共有されたコミュニティの報告で確認された対処です)。

4. バイナリを一度アップロードする(StoreKit の商品有効化)

新規アプリがはまりやすい落とし穴です。すべて正しく構成され、RevenueCat の REST API は Offering を返すのに、端末では availablePackages が 0 になります。Apple が新しい商品 ID をアプリに紐付けるには、少なくとも一つアップロードされたバイナリを処理する必要があります。任意のビルドをアーカイブして App Store Connect にアップロードし (TestFlight で十分、審査は不要)、処理を待つと (15 分から数時間)、商品が解決し始めます。

5. バンドル ID と capability

  • Xcode → target → Signing & Capabilities: Bundle Identifier は App Store Connect RevenueCat → Apps & providers の両方と完全に一致している必要があります。
  • In-App Purchase capability がなければ、target に追加します。

6. シミュレーターには StoreKit Configuration ファイルが必要

iOS シミュレーターは App Store Connect と通信しません。スキームで StoreKit Configuration ファイルを選択していないと、商品の要求は空を返します。これは初日に最もよくある経験です。次のいずれかで対処します。

  1. File → New → File → StoreKit Configuration File で、まったく同じ商品 ID を使って商品を追加し、Product → Scheme → Edit Scheme → Run → Options → StoreKit Configuration でそのファイルを選択する。または
  2. サンドボックステスターを使った実機でテストし (Settings → App Store → Sandbox Account)、StoreKit Configuration は None のままにする。
逆の落とし穴: 実際の App Store / サンドボックスの商品を期待しているのに StoreKit Configuration ファイルを有効なままにしている(または商品 ID が App Store Connect からずれている)場合も、Offering は空になります。サンドボックスに対してテストするときは、StoreKit Configuration を None に戻してください。

7. 国 / 地域の提供状況

検証済みのコミュニティの対処です。サブスクリプションが 1 か国だけで提供されており、ほかの国のテスターには Offering が空になっていました。App Store Connect で各商品の Availability を確認し、自分(とテスター)がいるすべての国を含めてください。サンドボックスアカウントにも国があることを忘れずに。

8. 反映にかかる時間

新しい商品やメタデータの編集は、Apple のシステムに反映されるまで数時間から約 24 時間かかることがあります。数分前に商品を作成したばかりで、上記のチェック項目がすべて通るなら、ほかを変える前に待ってください。

App Review の注意: Apple の審査環境は、商品の取得が不安定なことで知られています。Offering の取得に失敗しても、常に丁寧に処理してください(空の画面ではなく再試行ボタン)。審査担当者に「商品が出ない」と報告されたら、上記のどれもがデグレしていないか確認して再提出してください。

Android / Google Play の対処

0:05:00

iOS と違い、Android にはオフラインの StoreKit 相当がありません。Billing Library は常にライブの Google Play バックエンドと通信するため、Play がアプリ、商品、そしてテスターを把握している必要があります。頻度の高い順に並べます。

1. アプリをテストトラックに公開する

Google Play は、一度も見たことのないアプリには商品を返しません。署名済みのリリースビルド (AAB) を、少なくとも Internal testing トラック (Play Console → Release → Testing → Internal testing) にアップロードし、そのトラックがテスターに利用可能と表示されるようにしてください。Internal testing に審査は不要です。

2. テスターを追加し、かつ opt-in させる

これは誰もが見落とすステップです。Google アカウントをテスターリストに追加するだけでは足りません。

  1. 自分の Google アカウントを、トラックのテスターとして追加します。
  2. opt-in リンク(Play Console の「How testers join your test」に表示)を、そのアカウントでサインインした端末で開き、Become a tester をタップします。
  3. そのリンク経由で、Google Play からアプリを少なくとも一度インストールします。その後は、同じ applicationId と署名設定を持つローカルビルドのデバッグビルドでも商品を取得できます。

3. 端末上のビルドが Play の想定と一致していること

  • build.gradleapplicationId は、Play Console と RevenueCat → Apps & providers の両方と完全に一致している必要があります。ビルドタイプが付与する .debug サフィックスに注意してください。
  • 端末のメインの Google Play アカウントがテスターアカウントである必要があります。複数アカウントの端末は、間違ったアカウントで Play に問い合わせがちです。不安なら Play Store のデータを消去するか、テスターアカウントだけのプロファイルを使ってください。
  • エミュレーターには Google Play Store イメージ(単なる「Google APIs」ではなく)を含め、テスターでサインインしてください。

4. 商品が Active であること(買い切り商品も有効化)

Play Console → Monetize → Products で、Offering が参照するサブスクリプションとアプリ内商品はすべて Active と表示されている必要があります。新しく作成したアプリ内商品は、Activate を押すまで下書きのままです。サブスクリプションは、基本プランも有効か確認してください。有効な基本プランのないサブスクリプションは ITEM_UNAVAILABLE を返します。

5. サービス認証情報: 有効かつ反映済み(最大 36 時間)

RevenueCat が購入を検証し、(インポート時に)商品を読み取るには、有効な Play サービス認証情報が必要です。RevenueCat → Apps & providers → Google Play で、認証情報のチェックが通る必要があります。検証済みの落とし穴が二つあります。

  • 作成したばかりのサービス認証情報は、Google のシステムに反映されるまで最大 36 時間かかることがあります。今日 Play の設定を終えたばかりで、ほかがすべて問題ないなら、これが答えの可能性が高いです。待ってください。
  • 反映を早める手っ取り早い方法として、Play Console で任意の商品の説明を編集して保存すると、Google のキャッシュを更新できます。

6. 国の提供状況

アプリの国の提供状況 (Play Console → Release → Production → Countries/regions) と、各サブスクリプションの地域別価格を確認してください。アプリや商品が提供されていない国のテスターには、空の結果が返ります。

7. それでも ITEM_UNAVAILABLE?

このレスポンスコードは「Play はアプリを知っているが、この SKU をこのユーザーには売らない」という意味です。この観点で原因 1〜3 を再点検してください。正しいトラックか? 正しいアカウントか? Play から少なくとも一度インストールしたか? ほぼ必ずこの三つのどれかに行き着きます。

待っている間に順番待ちをスキップ: RevenueCat の Android 用 Test Store を使えば、Play Console の設定なしで購入フロー全体をビルドしてテストできます。トラックの公開や認証情報の反映を待つ間に便利です。

React Native、Flutter とクロスプラットフォーム

0:03:00

ステップ 03〜05 の内容は、React Native、Flutter、KMP にもそのまま当てはまります。SDK は最終的に同じネイティブの StoreKit / Play Billing API を呼び出すためです。次の原因は、クロスプラットフォームの構成に特有のものです。

Expo Go では商品を取得できない

react-native-purchases はネイティブモジュールで、Expo Go には含まれていません。そこでは Offering や商品が常に空で返ります。EAS Build で開発ビルドを作成し (npx expo run:ios / eas build --profile development)、そこでテストしてください。ネイティブモジュールを取り除くあらゆる環境にも同じことが言えます。

取得する前に構成する(競合状態)

Purchases.configure() の完了前に getOfferings() を呼ぶと (JS のモジュール境界をまたぐときや、並行する useEffect で起こりがちです)、エラーや空の結果が返ります。構成は一度だけ、できるだけ早く (例: アプリのルートで) 行い、API が許すなら Offering の取得前に await してください。

各プラットフォームで、そのプラットフォーム用のキーを使う

クロスプラットフォームのアプリでは両方のキーが必要で、実行時に選択します。

typescript
// React Native
import { Platform } from 'react-native';
import Purchases, { LOG_LEVEL } from 'react-native-purchases';

Purchases.setLogLevel(LOG_LEVEL.DEBUG);
Purchases.configure({
  apiKey: Platform.OS === 'ios' ? 'appl_YOUR_IOS_KEY' : 'goog_YOUR_ANDROID_KEY',
});

Android で iOS のキーを渡すと(またはその逆)、ステップ 03 で扱った ConfigurationError がそのまま発生します。

getProducts() と getOfferings() で切り分ける

React Native のスレッドで報告されているパターンです。Purchases.getProducts([...]) は商品を返すのに、getOfferings() が空になります。これは実はよい知らせです。ストア側が動いていることを証明し、切れ目をダッシュボードの連鎖 (パッケージ → Offering → current) に絞り込めます。ステップ 03 に戻り、原因 A〜D を確認してください。iOS では Paid Applications Agreement も確認します。構成によっては、getProducts が部分的に動いたまま Offering だけが壊れることがあります。

Flutter の注意: 相当する呼び出しは purchases_flutterPurchases.getOfferings() / Purchases.getProducts() で、同じ切り分けが当てはまります。ホットリロードでは configure() は再実行されません。キーを変えたら完全に再起動してください。

コードでデバッグする

0:04:00

次のスニペットは、連鎖のどこが切れているかをそのまま出力します。Offering オブジェクト → 現在の Offering → パッケージ → 商品、の順です。いずれかを実行して出力を読めば、このガイドの適切なセクションを指し示してくれます。

swift
// Swift: walk the chain
Purchases.logLevel = .debug  // before configure

Purchases.shared.getOfferings { offerings, error in
    if let error = error {
        print("❌ Offerings error: \(error.localizedDescription)")
        // ConfigurationError → step 03 (platform mismatch / dashboard)
        // Network errors    → step 08
        return
    }
    guard let offerings = offerings else {
        print("❌ Offerings object is nil → check API key & project (step 03-F)")
        return
    }
    print("All offerings: \(offerings.all.keys.joined(separator: ", "))")

    guard let current = offerings.current else {
        print("⚠️ No current offering → mark one Current (step 03-A)")
        return
    }
    print("Current offering: \(current.identifier)")

    if current.availablePackages.isEmpty {
        print("⚠️ 0 packages resolved → store could not serve the products (steps 04-05)")
        print("   (If the dashboard shows packages, this is store-side: agreement,")
        print("    product status, binary activation, testing track, or tester setup.)")
    } else {
        for package in current.availablePackages {
            print("✅ \(package.identifier) → \(package.storeProduct.productIdentifier) @ \(package.storeProduct.localizedPriceString)")
        }
    }
}
kotlin
// Kotlin: walk the chain
Purchases.logLevel = LogLevel.DEBUG  // before configure

Purchases.sharedInstance.getOfferingsWith(
    onError = { error ->
        Log.e("RC", "❌ Offerings error: ${error.message} (code: ${error.code})")
        // ConfigurationError → step 03 (platform mismatch / dashboard)
    },
    onSuccess = { offerings ->
        Log.d("RC", "All offerings: ${offerings.all.keys.joinToString()}")

        val current = offerings.current
        if (current == null) {
            Log.w("RC", "⚠️ No current offering → mark one Current (step 03-A)")
            return@getOfferingsWith
        }
        Log.d("RC", "Current offering: ${current.identifier}")

        if (current.availablePackages.isEmpty()) {
            Log.w("RC", "⚠️ 0 packages resolved → Play could not serve the products (step 05)")
            Log.w("RC", "   Check: testing track, tester opt-in, install-from-Play, Active products")
        } else {
            current.availablePackages.forEach { pkg ->
                Log.d("RC", "✅ ${pkg.identifier} → ${pkg.product.id} @ ${pkg.product.price.formatted}")
            }
        }
    }
)
typescript
// React Native: walk the chain (same logic for Flutter's purchases_flutter)
import Purchases, { LOG_LEVEL } from 'react-native-purchases';

Purchases.setLogLevel(LOG_LEVEL.DEBUG);

try {
  const offerings = await Purchases.getOfferings();
  console.log('All offerings:', Object.keys(offerings.all));

  if (!offerings.current) {
    console.warn('⚠️ No current offering → mark one Current (step 03-A)');
  } else if (offerings.current.availablePackages.length === 0) {
    console.warn('⚠️ 0 packages resolved → store-side issue (steps 04-05)');
  } else {
    offerings.current.availablePackages.forEach((pkg) =>
      console.log(`✅ ${pkg.identifier} → ${pkg.product.identifier} @ ${pkg.product.priceString}`),
    );
  }
} catch (e) {
  console.error('❌ Offerings error:', e);
  // ConfigurationError → step 03; native module missing in Expo Go → step 06
}

デバッグログの読み方

  • Configuring Purchases with API key: appl_/goog_…: 想定どおりのキーとプラットフォームか確認します。
  • Requesting products from the store with identifiers: …: ダッシュボードの連鎖は動いています。問われているのは、ストアが返す内容です。
  • Fetched 0 products from the store / 無効な ID が列挙される: ストア側の原因です。ステップ 04 (iOS) または ステップ 05 (Android) へ。
  • 商品の要求ログがまったく出ない: ダッシュボード側の原因です。ステップ 03 へ。

まだ解決しない? + 関連ガイド

0:02:00

切り分けツール: どの層が壊れているかを突き止める

  • 反映を待つ: 新しい商品(Apple では最大約 24 時間)、新しい Play 認証情報(最大 36 時間)、新規の Agreement 署名は、いずれも時間がかかります。今日設定したなら、最も効く対処は明日待つことだったりします。
  • Network 系のエラー (NetworkError、タイムアウト): 接続を確認し、VPN/プロキシを無効にし、api.revenuecat.com がファイアウォールで遮断されていないか確かめ、ペイウォールの読み込みにバックオフ付きの再試行を追加してください。とくに App Store の審査中は、ペイウォールが丁寧に劣化するよう設計します。
  • アプリなしでダッシュボードの連鎖をテスト: REST API を呼び出します。これが商品付きの Offering を返せば、ダッシュボードは問題なく、原因はストア側かアプリ側です。
    bash
    curl -s 'https://api.revenuecat.com/v1/subscribers/test_user/offerings' \
      -H 'Authorization: Bearer YOUR_PUBLIC_API_KEY' \
      -H 'X-Platform: ios'   # or android
  • 自分のコードなしで構成をテスト: RevenueCat の SampleCat サンプルアプリに、自分の API キーを入れて試します。SampleCat では Offering が取得できるのに自分のアプリではできないなら、違いはアプリ側にあります(初期化の順序、キー、StoreKit の構成、バンドル ID)。
  • ストアなしでコードをテスト: RevenueCat の Test Store (iOS) / Test Store (Android) は、Apple/Google を完全にバイパスします。Test Store のキーで Offering が読み込めれば、コードとダッシュボードは問題なく、原因はストア側のセットアップです。
  • ダッシュボード編集後の古いキャッシュ: Offering は約 5 分間キャッシュされます。ダッシュボードを変更したら、アプリを強制終了して再起動してください(Android では Play Store アプリ自体のキャッシュも遅れることがあり、Play Store のデータ消去が有効です)。

完全なチェックリスト

  • Current に設定した Offering が一つ。パッケージがあり、各パッケージが自分のプラットフォーム用の商品を保持
  • ☐ 商品 ID がストアと同一(大文字小文字、空白)
  • ☐ 正しいプロジェクトの正しい公開 API キー (appl_/goog_)。バンドル ID / applicationId が一致
  • ☐ iOS: Paid Applications Agreement が Active(銀行口座 + 税務が完了、失効していない)
  • ☐ iOS: 商品に価格・ローカライズ・審査用スクリーンショットがある。サブスクリプションが Subscription Group に入っている
  • ☐ iOS: バイナリを少なくとも一つアップロードして処理済み。シミュレーターは StoreKit Configuration ファイルを使用(または実機 + サンドボックスアカウントで None に設定)
  • ☐ Android: 署名済みビルドが Internal testing にある。テスターを追加し、opt-in 済み。Play から一度インストール済み
  • ☐ Android: 商品(と基本プラン)が Active。Play 認証情報が有効で、作成から 36 時間以上経過
  • ☐ 両方: 商品/アプリの提供状況が自分の国を含む。デバッグログを端から端まで確認済み

すべての項目にチェックが付いても Offering が空のままなら、デバッグログを集め、RevenueCat コミュニティで質問するかサポートに連絡してください。Error fetching offerings 前後のログ行を添えてください。

関連ガイド