RORK LABEN
MAX — Rork MaxはiPhone・iPad・Apple Watch・Apple TV・Vision Pro向けにネイティブSwiftを生成し、2クリックでApp Store公開でき、Xcodeを必要としませんSTACK — 通常のRorkはReact Native(Expo)でクロスプラットフォームのモバイルアプリを作る位置づけ。用途に応じた使い分けが鍵ですFOCUS — BoltやLovableのようなWeb中心ツールと違い、RorkはiOS/Androidのネイティブアプリ生成に特化していますBUGS — 実利用レビューでは遭遇したバグの約70%を手動介入なしで解決、残り3割はエクスポート済みコードでの手修正が必要と報告されていますFUNDING — Rorkはa16z(Andreessen Horowitz)から$2.8Mを調達しましたPRICING — 無料で開始でき、有料プランは$25/月からです。まず触ってから判断できますMAX — Rork MaxはiPhone・iPad・Apple Watch・Apple TV・Vision Pro向けにネイティブSwiftを生成し、2クリックでApp Store公開でき、Xcodeを必要としませんSTACK — 通常のRorkはReact Native(Expo)でクロスプラットフォームのモバイルアプリを作る位置づけ。用途に応じた使い分けが鍵ですFOCUS — BoltやLovableのようなWeb中心ツールと違い、RorkはiOS/Androidのネイティブアプリ生成に特化していますBUGS — 実利用レビューでは遭遇したバグの約70%を手動介入なしで解決、残り3割はエクスポート済みコードでの手修正が必要と報告されていますFUNDING — Rorkはa16z(Andreessen Horowitz)から$2.8Mを調達しましたPRICING — 無料で開始でき、有料プランは$25/月からです。まず触ってから判断できます
記事一覧/開発ツール
開発ツール/2026-06-15上級

解約だと思って権限を切ったユーザーは、まだ払う気だった — 支払い猶予期間と権限維持の実装

購読の支払いが失敗しても、それは解約ではありません。猶予期間中に権限を切ると復帰可能な売上を自分で捨てます。Rork(Expo)アプリで猶予・課金リトライ・アカウントホールドを取り違えず、権限維持と復帰導線を出す実装をまとめます。

サブスクリプション59RevenueCat24課金7Expo84収益化60

プレミアム記事

購読アプリを運用していて、ある月の解約数が妙に多い、と感じたことがあります。調べてみると、その「解約」の半分以上は本人が解約ボタンを押したわけではなく、クレジットカードの期限切れや限度額超過で自動更新の決済が失敗していただけでした。問題は、私のアプリがその状態を「解約」と同じ扱いにして、即座に有料機能を閉じていたことです。

支払いが一度失敗しただけのユーザーは、まだ払う気があります。Apple も Google も、決済が失敗した購読をすぐには切らず、数日から数週間かけて自動でリトライする仕組みを用意しています。この期間に権限を維持して「カードを更新してください」と促せるかどうかで、回収できる売上が変わります。私の場合、ここを直しただけで失敗課金からの復帰がはっきりと増えました。

ここからは Rork で生成した Expo アプリを前提に、支払い失敗の4つの状態を取り違えずに扱い、猶予期間は権限を維持し、本当に回収不能になったタイミングで初めて権限を切る実装をまとめます。決済の状態管理そのものに関心がある方は、先にサブスク権限のエンタイトルメント状態機械の設計を読んでおくと、本記事の state machine が頭に入りやすいはずです。

「支払い失敗」は1つの状態ではない

失敗から失効までに挟まる4つの中間状態

最初の落とし穴は、isActive === false を見て「解約」と判断してしまうことです。実際には、自動更新の決済が失敗してから購読が完全に消えるまでに、いくつもの中間状態があります。

  • 課金リトライ(Billing Retry): 決済が失敗し、ストアが自動でリトライしている期間。この間も多くの場合は権限を維持すべきです。
  • 支払い猶予期間(Billing Grace Period): 開発者が明示的に有効化する猶予。期間中はユーザーが有料機能を使い続けられます。Apple は購読周期に応じて最大16日、Google は週次で最大7日・月次以上で最大30日が目安です。
  • アカウントホールド(Account Hold): 猶予もリトライも尽きた状態。ここで初めて権限を停止します。ただしユーザーがカードを直せば購読は復活します。
  • 解約・失効(Cancelled / Expired): 本人が解約した、またはリトライ期間も終わって購読が完全に終了した状態。

重要なのは、リトライと猶予の段階では「まだ顧客である」と見なすことです。ここで権限を切ると、ユーザーは「金は払ったのに使えなくなった」と感じ、復帰どころか低評価レビューに直行します。

ストア固有の状態を1つの列挙型に正規化する

私はこの4状態を、アプリ全体で1つの列挙型に正規化してから扱うようにしています。ストアごとの細かい通知タイプを画面側まで持ち込まないことが、バグを減らす最大のコツです。

// subscription/types.ts
// ストア固有の状態を、アプリ内では必ずこの5値に正規化する
export type EntitlementState =
  | 'active'        // 正常に課金中
  | 'grace'         // 決済失敗だが猶予/リトライ中(=権限は維持する)
  | 'hold'          // アカウントホールド(=権限は停止するが復帰可能)
  | 'expired'       // 完全に失効(解約・期限切れ)
  | 'never';        // 一度も購読していない
 
export interface EntitlementSnapshot {
  state: EntitlementState;
  // grace のときに「いつまで使えるか」を画面に出すために保持する
  graceUntil: string | null;   // ISO8601、なければ null
  willRenew: boolean;          // 次回自動更新が予定されているか
  productId: string | null;
}

RevenueCat の customerInfo から状態を読む

RevenueCat を使っている場合、customerInfo に支払い失敗の手がかりが入っています。多くの実装が entitlements.active の有無だけを見ていますが、それだと猶予期間を取りこぼします。猶予中は isActivetrue のまま willRenewfalse になり、billingIssueDetectedAt に失敗検知時刻が入る、という組み合わせを読む必要があります。

// subscription/fromRevenueCat.ts
import Purchases, { CustomerInfo } from 'react-native-purchases';
import { EntitlementSnapshot } from './types';
 
const ENTITLEMENT_ID = 'premium'; // RevenueCat で設定した Entitlement 識別子
 
export function toSnapshot(info: CustomerInfo): EntitlementSnapshot {
  const ent = info.entitlements.active[ENTITLEMENT_ID]
    ?? info.entitlements.all[ENTITLEMENT_ID];
 
  if (!ent) {
    return { state: 'never', graceUntil: null, willRenew: false, productId: null };
  }
 
  const hasBillingIssue = ent.billingIssueDetectedAt != null;
 
  // isActive が true なら、まだ使わせてよい期間(正常 or 猶予)
  if (ent.isActive) {
    if (hasBillingIssue && !ent.willRenew) {
      // 決済は失敗したが、有効期限までは猶予として使える
      return {
        state: 'grace',
        graceUntil: ent.expirationDate ?? null,
        willRenew: false,
        productId: ent.productIdentifier,
      };
    }
    return {
      state: 'active',
      graceUntil: null,
      willRenew: ent.willRenew,
      productId: ent.productIdentifier,
    };
  }
 
  // isActive が false。ここで hold と expired を分けるのが肝心
  // RevenueCat 単体では両者の区別が曖昧なため、サーバー側の通知で補強する(後述)
  return {
    state: hasBillingIssue ? 'hold' : 'expired',
    graceUntil: null,
    willRenew: false,
    productId: ent.productIdentifier,
  };
}

ここで billingIssueDetectedAtnull でないかどうかが、解約とホールドを分ける最初の判断材料になります。本人が解約した場合は課金問題が立っていないため expired、決済失敗起因で失効した場合は課金問題が立っているため hold に倒せます。

ただしクライアントの customerInfo だけでは取りこぼしが出ます。アプリが起動していない間に状態が変わるからです。そこでサーバー側の通知を一次情報として併用します。

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

この記事の続きを読む

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

この記事で得られること
支払い失敗ユーザーを『解約済み』として権限を切り、復帰できたはずの売上を取りこぼしていた人が、猶予期間を維持する実装を今日手に入れられる
RevenueCat の customerInfo と App Store Server Notifications V2 / Google RTDN を突き合わせ、猶予・リトライ・ホールド・解約を区別する動くコードを習得できる
猶予中はソフトに、ホールド突入で明確に、という二段階の復帰導線を実装し、失敗課金からの回収率を計測できる状態になれる
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

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

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

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

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

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

関連記事

開発ツール2026-06-14
Rork のサブスクを RevenueCat の Entitlement で組む — アクセス判定・Offering 駆動のペイウォール・復元の実装メモ
Rork(Expo)アプリの課金を RevenueCat で実装する際の設計メモです。Entitlement を唯一の真実とするアクセス判定、Offering 駆動でハードコードを避けるペイウォール、購入復元とリスナーの実装、サンドボックスで詰まりやすい点まで動くコードで整理します。
開発ツール2026-06-15
「広告を消す理由」が3つに増えた日 — Rork アプリの広告非表示判定を1か所に畳む設計
課金していないはずの画面だけ広告が出る、あるいは課金済みの読者に広告が出てしまう。原因の多くは「広告を消す条件」がコードのあちこちに散らばっていることでした。サブスク・買い切り・リワード時限解除の3つを1つの状態に畳み、すべての広告表示を同じフックに通す設計を、個人開発で6本のアプリを運用してきた実装メモとしてまとめます。
開発ツール2026-04-15
Rork + Superwall でペイウォールをA/Bテストして課金率を高める実装ガイド
Rorkで開発したサブスクリプションアプリにSuperwallを導入し、ペイウォールのA/Bテストで課金転換率を高める方法を解説。SDK導入からキャンペーン設定・分析まで実装コード付きで紹介します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →