個人開発アプリをApp Storeに出したらIAPでリジェクトされた!StripeとRevenueCatを共存させるまでの意思決定記録!!

はじめに:「あとでやろう」が通用しなかった
個人でウェルネスアプリ「Paleotribes(パレトラ)」を開発していて、ついに iOS 版を App Store に出そうとしたとき、いきなり壁にぶつかりました。
Apple の審査から、こういう趣旨のフィードバックが来たのです。
「デジタルコンテンツや機能への課金は App 内では In-App Purchase を使う必要があります」
パレトラは Web 側で Stripe を使った課金構成を組んでいました。iOS アプリにはまだ IAP を実装していなかった。「まずは最小構成で公開して、課金周りはあとで整備しよう」という判断だったのですが、Apple はそれを通してくれませんでした。
この記事は、そのリジェクトにどう向き合い、最終的にどういう設計に落ち着いたかの記録です。同じように「Web 側に Stripe がある状態で iOS アプリを出そうとしている」個人開発者の参考になれば幸いです。
なぜリジェクトされたのか、本質を理解するまで
最初はリジェクトの意味をちゃんと掴めていませんでした。「IAP を入れれば通るんでしょ」と軽く思っていたのです。
でも整理すると、Apple が問題にしていたのは単なる実装不備ではなく、課金チャネルと権限付与の整合性でした。
App Store Connect の履歴を見ると、リジェクトが2回入っています。

Apple から見た状況をまとめるとこうです。
- Web で購入できるプレミアムプランがある
- iOS アプリでも同じアカウントを使ってログインできる
- つまり iOS 上でも有料機能にアクセスできる可能性がある
この構図がある以上、「iOS アプリ内に IAP が提供されていない」という状態は、Apple のガイドラインに抵触するとみなされます。
Web で払った権利を iOS で使えるなら、iOS でも同じ機能を IAP で買える手段を用意しなければならない——これが本質でした。
最初に考えた逃げ道と、なぜやめたか
いくつかの「逃げ方」を考えました。正直に書いておきます。
逃げ道①:App Review に説明して押し切る
「iOS アプリでは有料機能を提供していない」と説明して審査を通す案です。実装コストはゼロ。でもこれは弱い。
Web で買ったアカウントと iOS アプリが同一なのに「iOS では有料機能を提供していない」という説明は、審査員から見ると矛盾に見えます。実際に突っ込まれたとき答えられません。
逃げ道②:iOS では常に free 扱いにする
サーバー側で「iOS クライアントには free の権限しか返さない」という制御を入れる案です。技術的には実装できます。短期的に審査を通る可能性もある。
ただし問題があります。
- Web でプレミアムを買ったユーザーが iOS を使うと権限がない、という不自然な体験になる
- 「同じアカウントなのに端末ごとに権限が違う」という設計は将来の負債になる
- 審査でさらに深掘りされたとき説明できない
審査を一度すり抜けても、長期的に安全な構成ではないと判断してやめました。
辿り着いた核心:「どこで払ったか」と「何を許可するか」は別の話
逃げ道を塞いでいくうちに、重要な設計上の整理ができました。決済手段と権限判定を分離するという考え方です。
- Stripe は Web 課金のイベントソース
- Apple IAP / RevenueCat は iOS 課金のイベントソース
- アプリで実際に使う権限は、サーバーが統合的に判断する
「どこで払ったか」はシステムの都合であって、ユーザーには関係ない話です。ユーザーから見れば「自分はプレミアムを買っている」という事実があるだけ。それを一元管理する責任をサーバーに持たせる——これが今回の設計の中心になりました。
最終的に採った構成
Web 課金:Stripe を継続、iOS 課金:RevenueCat 経由で Apple IAP を実装、権限判定:サーバーで統合という構成に落ち着きました。
[Web]
ユーザー → Stripe Checkout / Customer Portal
→ Webhook
→ DB にサブスク状態を反映
[iOS]
ユーザー → Apple In-App Purchase
→ RevenueCat
→ DB / サーバー連携に反映
[Server]
Stripe の状態 + RevenueCat の状態を統合
↓
getEntitlementsForUser()
↓
free / premium および機能ごとの entitlement を返す
[Client(iOS・Web 共通)]
サーバーが返した entitlements に従って UI・機能を制御クライアントは決済手段を直接信頼しない。サーバーが唯一の正とする設計です。
なぜ RevenueCat を選んだか
Apple のレシート検証やサブスク状態の同期を自前で厚く持つことは、個人開発のコストとしては割に合いません。RevenueCat を使うと次の利点があります。
- Apple のレシート検証を任せられる
- サブスク状態(アクティブ、期限切れ、復元済みなど)の管理がシンプルになる
- Webhook で自前 DB への反映も整理しやすい
- 将来 Android を追加する際も対応しやすい
Web は Stripe で動いている基盤をそのまま活かし、iOS だけ RevenueCat を使うという分業が自然でした。
両チャネル有効時のルール
Stripe と IAP の両方が有効になったとき(Web と iOS 両方で購入した場合など)の扱いも整理しました。
シンプルに、currentPeriodEnd がより先の方を優先するというルールにしました。ユーザー体験を壊さず、実装が複雑にならず、将来読んだときにも意図が明確。個人開発の段階では複雑な優先度ロジックを書くより、このくらいシンプルなルールで十分です。
App Review を通すために実際にやったこと
設計が整っても、審査員に伝わらなければ通りません。App Review Notes の整備が思ったより重要でした。
審査ノートに明記した内容:
- プランの名称と月額料金
- 設定画面のどこに購入導線があるか
- Restore Purchases ボタンの場所
- Terms / Privacy への導線がどこにあるか
Apple の審査では、機能が実装されているだけでなく「審査員がたどれること」が重要です。購入導線や復元ボタンを設定画面にまとめて常時表示することで、審査員が確認しやすくなります。
この対応を通じて整理できたこと
結果として、この問題に向き合ったことで設計がきれいになりました。逃げ道を選んでいたら、「iOS は always free」という不自然な構成を抱えたまま先に進んでいたはずです。
迷ったタイミングに正しく向き合うと、設計の質が上がるというのは、個人開発をやっていると繰り返し感じることです。
まとめ
- Web 課金(Stripe 等)がある状態で iOS アプリを出す場合、Apple IAP の要否は早めに確認する
- 「説明で押し切る」「iOS では free にする」は短期対応としては考えられるが、長期的に脆い
- 「決済手段」と「権限判定」を分離する設計にするとスッキリする
- RevenueCat は個人開発での iOS IAP 運用に向いている
- App Review Notes には購入・復元・利用規約の導線を明確に書く
小さな個人開発でも、設計の判断を記録として残しておくと、後で振り返ったときに役立ちます。今回はそれを記事にしてみました。
Paleotribes(パレトラ)は習慣・ウェルネス管理のモバイルアプリです。現在 iOS 版を公開中です。



