◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SONNET 4.5 — Claude API での提供終了は 11/30、残り27日。API 連携を足したアプリは移行先の確認を●SDK 58 — React Native 0.88 に更新されました。Android のリリース版で R8 が既定オンになり、iOS は SceneDelegate を生成します●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SONNET 4.5 — Claude API での提供終了は 11/30、残り27日。API 連携を足したアプリは移行先の確認を●SDK 58 — React Native 0.88 に更新されました。Android のリリース版で R8 が既定オンになり、iOS は SceneDelegate を生成します●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-06-14上級

Rork のサブスクを RevenueCat の Entitlement で組む — アクセス判定・Offering 駆動のペイウォール・復元の実装メモ

Rork(Expo)アプリの課金を RevenueCat で実装する際の設計メモです。Entitlement を唯一の真実とするアクセス判定、Offering 駆動でハードコードを避けるペイウォール、購入復元とリスナーの実装、サンドボックスで詰まりやすい点まで動くコードで整理します。

Rork611RevenueCat30サブスクリプション66Expo234課金9

✦ プレミアム記事

Rork で形になったアプリに課金を載せようとして、最初に手が止まるのは「会員かどうかをアプリのどこで、何を根拠に判定するか」という一点ではないでしょうか。商品 ID を直接見るのか、レシートを保存するのか、購入済みフラグを端末に書くのか。ここを曖昧にしたまま実装を進めると、価格を変えた途端に判定が崩れたり、機種変更したユーザーから「課金したのに使えない」という問い合わせが届いたりします。

RevenueCat を入れる本当の利点は「数十行で課金が書ける」ことよりも、判定の根拠を商品 ID ではなく Entitlement(権利)に寄せられることにあります。Rork が生成する Expo(React Native)アプリを前提に、react-native-purchases を使った Entitlement 中心の設計と、Offering 駆動のペイウォール、購入復元、そしてサンドボックスで実際に詰まった箇所を、動くコードと一緒に整理していきます。商品設定の画面手順そのものより、後から効いてくる構造のほうを厚めに書きます。

なぜ「商品 ID で判定」が後で崩れるのか

課金実装でよくある初手は、購入した商品 ID(com.example.app.pro_monthly など)をそのまま判定に使うやり方です。動くことは動きます。ただ、運用に入ると次のような場面で必ず引っかかります。

月額と年額を両方売り始めたら、pro_monthly と pro_annual の両方を if で見る必要が出ます。値上げのために新しい商品 ID を切ったら、旧 ID 加入者を救うコードを足すことになります。iOS と Android で商品 ID の命名規則を変えてしまっていたら、プラットフォーム分岐が二重に増えます。気づくと「Pro 機能を解放してよいか」を判定する関数が、商品 ID の羅列で膨らんでいきます。

RevenueCat の Entitlement は、この羅列を一段抽象化する仕組みです。ダッシュボードで pro という Entitlement を一つ作り、そこに「どの商品を買えば pro が有効になるか」を紐づけます。アプリ側は商品 ID を一切知らずに「pro が active かどうか」だけを見ます。商品を増やそうが値上げしようが、紐づけはダッシュボード側の作業になり、アプリのコードは変わりません。私はここを最初に固めるかどうかで、その後半年の保守コストが変わると考えています。

判定軸はひとつに絞ります。「Entitlement が active か」だけがアクセスの真実で、端末に書いた購入フラグや商品 ID は参考情報にすぎない、という前提でコード全体を組みます。

サービス層を Entitlement 中心に書く

まず SDK を入れます。Rork(Expo)の場合、課金はネイティブモジュールを含むため Expo Go では動かず、開発ビルド(expo-dev-client)か EAS Build が必要です。ここを知らずに Expo Go で試して「購入が呼べない」と悩むのが最初の落とし穴なので先に書いておきます。

npx expo install react-native-purchases
# 開発ビルドを作る(Expo Go では課金は動かない)
npx expo prebuild
eas build --profile development --platform ios

パッケージ名は react-native-purchases です(スコープ付きの古い表記を見かけますが、現行はこちらです)。サービス層は、アプリの他の部分が「商品」や「レシート」を意識せずに済むよう、Entitlement を返す薄い窓口として書きます。

// src/services/purchases.ts
import Purchases, {
  CustomerInfo,
  PurchasesPackage,
  LOG_LEVEL,
} from 'react-native-purchases';
import { Platform } from 'react-native';
 
// 判定に使う Entitlement は「ひとつ」に決める。商品 ID はここに出てこない
export const ENTITLEMENT_ID = 'pro';
 
const API_KEY = Platform.select({
  ios: process.env.EXPO_PUBLIC_RC_IOS_KEY ?? '',
  android: process.env.EXPO_PUBLIC_RC_ANDROID_KEY ?? '',
}) as string;
 
export async function configurePurchases(appUserId?: string) {
  if (__DEV__) Purchases.setLogLevel(LOG_LEVEL.DEBUG);
  // appUserId を渡さなければ RevenueCat が匿名 ID を自動採番する。
  // 自前の認証がある場合のみ「ログイン後に logIn() で紐づけ」が正解で、
  // configure に最初から渡すと匿名→既知の付け替えが二重に走りやすい
  await Purchases.configure({ apiKey: API_KEY });
}
 
// 唯一のアクセス判定。これ以外でプレミアム可否を判断しない
export function isPro(info: CustomerInfo): boolean {
  return info.entitlements.active[ENTITLEMENT_ID] !== undefined;
}
 
export async function getCustomerInfo(): Promise<CustomerInfo> {
  return Purchases.getCustomerInfo();
}

isPro が CustomerInfo を引数に取り、内部状態を持たない純粋な関数になっている点が肝心です。判定がどこか一箇所のグローバル変数に依存していると、購入直後・復元直後・起動直後で値がずれます。常に「いま手元にある最新の CustomerInfo を渡して聞く」形にしておくと、後述のリスナーと素直につながります。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦Entitlement を唯一の判定軸にし、商品 ID 直書きを排して価格改定・プラットフォーム差に強くする実装
✦react-native-purchases の Offering / Package と priceString で価格を遠隔差し替えするペイウォール
✦restorePurchases と CustomerInfo リスナー、サンドボックスで詰まる4つの箇所と回避策
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ 開発ツール2026-10-30
開発ビルドでは動くのに、Release の iOS だけ起動直後に落ちる — scheme を外した app.json が原因の場合の確かめ方
expo-router のアプリで app.json に scheme がないと、Release の iOS だけ起動直後に落ちることがあります。Debug と Android が無事な理由、直し方の二択、scheme を外したい場合の考え方、提出前に見つける確認手順を書き残します。
⬡ 開発ツール2026-10-15
EXPO_PUBLIC_ の値が、開発では読めて本番だけ undefined になる書き方を、変換結果で見分けます
API の URL が開発中は読めるのに、ビルドした途端に undefined になる。原因は process.env の書き方でした。babel-preset-expo の変換結果を開発・本番で並べ、置き換わる書き方と残ってしまう書き方を表にしました。
⬡ 開発ツール2026-10-13
リリース前に、書き出した Expo バンドルの中から自分の鍵を探してみる
ソースに鍵がなくても、出来上がったアプリの中に鍵が残ることがあります。Expo SDK 57 で実際に expo export を実行し、バンドルの中身を grep と 60 行ほどの Node スクリプトで調べて、見つかる鍵と見つからない鍵の違いを確かめました。