(2025年版)サブスクリプション課金アプリをApp Storeで公開する手順まとめ
はじめに
サブスクリプション方式の課金が実装されたアプリをApp Storeで公開する手順をまとめました。慣れてしまえば難しくないのですが、最初は何から着手すればよいのか迷うはず。本記事がお役に立てれば幸いです。
想定読者は本業が別にある個人のアプリ開発者です。無料アプリの公開経験があり、腕試しに有料アプリを公開してみようとお考えの方を想定しています。
法人を設立して大規模なアプリを開発する予定の方は本記事の対象外です。記事中で説明する作業はすべて手動で行います。CI / CDパイプラインの構築やメタデータ提出の自動化などは行いません。
前提
- Androidなどクロスプラットフォーム対応は行いません。
- RevenueCatなどサードパーティーの課金管理サービスは利用しません。
- アプリの配信先は日本のApp Storeに限定します。
- アプリはSwiftUIとStoreKit 2で実装します。
- App Store Connectの言語設定は英語を前提に説明します。(一部日本語のローカライズが漏れていて紛らわしいため)
本記事は情報の提供のみを目的とします。本記事に従って生じた損害について著者はいかなる責任も負いません。
記事の構成
本記事は以下8つの章で構成されています。ランダムに読み進めても構いませんが、第1章から順番に読み進めることをおすすめします。
- 第1章 アプリ開発をはじめる前に
- 第2章 サブスクリプション関連用語を理解する
- 第3章 App Store Connectでサブスクリプションの設定を練習する
- 第4章 アプリを公開するための事前準備を行う
- 第5章 課金処理を実装する
- 第6章 Xcodeのローカル環境で課金をテストする
- 第7章 App Store Connectのサンドボックス環境で課金をテストする
- 第8章 アプリを提出する
第1章 アプリ開発をはじめる前に
この章ではサブスクリプション方式を含めた有料アプリの開発にあたって考慮するべき事項をまとめます。一般的な話題ではありますが、一度確認されることをお勧めします。
1.1. StoreKitとStoreKit 2の違い
StoreKit 2は公式な名前ではなく、iOS 15で導入された新しいAPIを指す俗称です。Apple DeveloperではAPIの新旧を区別せずStoreKitフレームワークとしてドキュメントが整備されています。
なお、課金に関連する旧StoreKit APIはiOS 18で非推奨となりました。[1]互換性のために残されているだけであり、今から新規にアプリを開発する場合はStoreKit 2 APIを利用することになります。
1.2. 有料アプリの提出に追加の費用は必要?
不要です。読者の皆様はアプリを公開するためにApple Developer Programの年会費[2]として$99をすでに支払っているかと思いますが、それ以外の費用は一切発生しません。
1.3. 個人事業の開業は必要?
不要です。開業していなくてもApp Store Connectでアプリを公開できます。ただし収益が雑所得の範囲を上回った場合、開業しているか関係なく確定申告が必要になりますのでご注意ください。
なお、本業が会社員の方が開業すると、例えば無職になったとき失業手当を受給できない恐れがあります。[3]事業の廃止や休止を証明することで受給可能になりますが、手続きが面倒です。
悲しいことに個人が開発したアプリで、いきなり莫大な収益を得られる可能性は、ほぼゼロです。雑所得の範囲を大きく上回り、収益が安定して継続できると見込まれる状況になるまで、開業は先送りすればよいかと思います。
1.4. StoreKit 2がサポートする課金の方式
StoreKit 2では課金の方式として以下の4種類をサポートしています。[4]
- 消耗型(Consumable): 一度使用すると消費され、再購入が必要になる商品。例: 魚釣りゲームの餌
- 非消耗型(Non-consumable): 一度購入すれば期限も使用回数の制限もない商品。例: レーシングゲームの追加コース
- 自動更新サブスクリプション(Auto-renewable subscription): 一定期間ごとに自動更新される購読型商品。ユーザが解約しない限り更新され続ける。例: 音楽聴き放題サービスの月額サブスクリプション
- 非更新サブスクリプション(Non-renewing subscription): 期間限定で利用でき、期間終了後に自動更新されない購読型商品。例: 音楽ライブの映像を1年視聴できるサブスクリプション
本記事では自動更新サブスクリプション方式の課金を実装します。サブスクリプションに関連する用語の詳細については第2章を参照ください。
1.5. どのような課金の方式を選ぶべき?
個人でアプリを開発する場合、課金の方式は自動更新サブスクリプションをおすすめします。主な理由は以下3点です。
- サブスクリプションの加入状態だけに気を配れば良いので実装が単純です。また、Basic / Standard / Proのようなレベルを用意して、排他的に選択させることがStoreKit 2標準機能だけで実現できます。
- 自前のサーバーを構築せず課金処理が実現できます。App StoreサーバーがSSoT(Single Source of Truth:信頼できる唯一の情報源)となるため、課金の履歴と特典付与の関連づけを管理するデータベースやユーザー認証基盤を構築する手間を省けます。
- 消耗型の課金でよくあるトラブルを回避できます。
消耗型の課金でよくあるトラブルについては次項で解説します。なお、サブスクリプション方式は比較的単純な方式ではありますが、お試し無料期間・値上げ通知・価格据え置き値上げ拒否処理などを実装するのには、それなりの手間がかかります。本記事では無料期間は設けず、アプリ公開後の大幅な値段変更はしないものと想定しています。
1.5.1. 消耗型課金をおすすめしない理由
例えば、ゲームで使い捨ての体力回復アイテムを購入したのに、効果がなかったと苦情が届いたとします。考えられる原因は以下2つです。
- ユーザーの勘違い: 例えばアイテムを購入して体力回復した直後に攻撃を受けて相殺された結果、体力が回復していないように見えた。
- ロジックの不具合: 例えば戦闘システムに欠陥があり、本当に体力が回復していなかった。
証拠となる情報がなければ原因の切り分けができません。従って、ユーザーの行動を証跡として記録するためのシステム構築が必要になります。
さらに、そのシステムには高い可用性と信頼性が求められます。システムが停止したり不具合を起こしたりすると、情報源として信頼できなくなるためです。
加えて、消耗型の課金で発生したトラブルを解決するには時系列を正確に把握する必要があります。調査のための聞き取り作業はユーザーと開発者双方に大きな負担となります。
ゲームのように商品の単価を下げて大量に課金させる想定であれば書き込みの高スループットを維持しつつ、増え続ける証跡データを妥当なコストで管理するアーキテクチャの考慮も必要です。その次は課金の傾向を分析するための基盤構築も必要になるでしょう。システム全体を監視して、不備があれば早期に発見できるようトレーシングの仕組みも必要になりそうです。
このような作業が苦にならなければ問題ありませんが、ちょっとした課金機能の開発が大規模なインフラ構築作業に発展するのは避けたいはずです。特に個人でアプリを開発する場合、消耗型の課金を採用するべきか慎重に検討してください。
1.5.2. 非消耗型と非更新サブスクリプションについて
消耗型の課金方式と比べると、非消耗型と非更新サブスクリプションは開発時の考慮事項が少ないのが特徴です。非消耗型であれば効力が失われる可能性に気を配る必要がなく、非更新サブスクリプションであれば加入期間中かどうかを判定するだけです。
難点を挙げるとすれば、アプリの内容によっては定期的なコンテンツの補充が必要になる点です。レーシングゲームの追加コースのように、何度か遊ぶと飽きるコンテンツであれば収益を維持するために新しいコースを作る必要があるかもしれません。技術的な難しさではなく、ビジネス面での難しさがある印象です。
それならば、遊び放題プランを自動更新サブスクリプションとして提供するほうが良いかもしれません。ユーザーは都度追加コンテンツを購入する手間を省けます。開発者は総ユーザーに占める課金ユーザーの割合から、将来の収益をある程度予測でき、新しいコンテンツ補充の方針が立てやすくなります。
1.5.3. アプリ内の独自通貨について
サブスクリプションと類似した手段として、非消耗型の課金でアプリの独自通貨(コインや宝石など)を購入させて、その通貨でアプリ内のコンテンツや機能を制限解除させる方式もあります。ご想像のとおり大きく自由度が増す反面、実装と運用の負担が大きくなります。
さらに、この方式は日本国内における資金決済法の対応が必要になるのが難点です。[5]個人でアプリを開発する場合の方式としてはおすすめしません。
1.5.4. 買い切りの有料アプリについて
買い切りの有料アプリであれば、最も簡単にアプリを収益化できます。面倒な課金処理の実装は必要ありません。App Store Connectでアプリの値段を設定して公開するだけです。
この方式が適しているのはオフラインで動作が完結し、一度リリースしたら更新することが滅多にないアプリです。辞書アプリや楽器アプリなどが該当します。
ただし、想定されるユーザー数とアプリの単価で収益の規模が決まってしまうのが難点です。値段が高くても一定数の購入が見込まれる市場に狙いを定める必要があります。
第2章 サブスクリプション関連用語を理解する
サブスクリプションは身近な課金方式です。しかし、App Store Connectにおける用語の理解があいまいな状態で開発を進めると後になって手戻りが発生する恐れがあります。この章では開発する上で知っておくべき用語を整理します。
2.1. Transactionとは
Transactionとは支払いが発生した取引の情報を意味します。トランザクションという用語は消耗型と非消耗型の課金に共通して使われます。本記事では取引の情報を指す場合は「取引」または「トランザクション」を使い、StoreKit 2のTransaction構造体を指す場合はTransactionとアルファベットで表記します。
2.2. Subscription Groupとは
Subscription Groupとはサブスクリプションのプランを束ねる単位です。通常は1つのアプリに1つのSubscription Groupを作成します。
ユーザーが契約できるプランはSubscription Groupの中で1つだけです。同時に複数のプランを契約することはできません。
ChatGPTアプリを例に説明します。ChatGPTという名前のSubscription Groupがあり、その中に選択肢として以下3つのプランがあります。
- Plus月払い: 3,000円 / 1ヶ月
- Pro月払い: 30,000円 / 1ヶ月
- Plus年払い: 30,000円 / 1年
プランの中で契約できるのは1つだけです。例えばPlus月払いとPro年払いを同時に契約することはできません。
なお、1つのアプリに複数のSubscription Groupを作成することは可能ですが、一般的ではありません。原則として1つのアプリに1つのSubscription Groupを作成します。
2.3. Subscription Group IDとは
Subscription Group IDとはSubscription Groupを識別するためのIDです。App Store ConnectおよびSwiftコード上でSubscription Groupを識別するために使います。IDの値がユーザーに公開されることはありません。
なお、Subscription Groupを作成すると自動的にIDが生成されます。また、一度生成されたIDの値が変化することはありません。手作業でIDの値を変更することもできません。
2.4. Auto-Renewable Subscriptionとは
月額や年額など定期的に料金を支払うことで加入期間中はサービスが利用可能になる方式を一般に「サブスクリプション」とよびます。この方式をApp Store ConnectではAuto-Renewable Subscription(自動更新サブスクリプション)とよびます。
StoreKit 2は期間終了とともに効力が失われるNon-renewing Subscription(非更新サブスクリプション)方式もサポートしており、自動更新サブスクリプション方式と区別しています。本記事では自動更新サブスクリプション方式を「サブスクリプション」として扱います。
2.4.1. 期間の選択肢一覧
自動更新サブスクリプションはユーザーが自らキャンセルするまで自動的に更新され続ける課金方式です。期間として選べるのは、次の6種類です。
- 1週間
- 1ヶ月
- 2ヶ月
- 3ヶ月
- 6ヶ月
- 1年
なお、1.5ヶ月や3年といった独自の期間を設定することはできません。
2.5. Productとは
Productとは課金対象の商品を意味します。Productという用語は消耗型と非消耗型で共通して使われます。サブスクリプション方式の場合、各プランが1つの商品として扱われます。
本記事では商品について指す場合は「商品」または「プロダクト」を使い、StoreKit 2のProduct構造体について指す場合はProductとアルファベットで表記します。
なお、アプリごとに登録できる商品数の上限は10,000件です。サブスクリプションのプランは数種類、多くても数十種類が一般的ですから、上限を心配する必要はありません。
2.6. Product IDとは
Product IDとはApp Store ConnectおよびSwiftコード上で商品を識別するためのIDです。ユーザーに公開されることはありません。
Product IDは消耗型の課金と非消耗型の課金どちらの場合も開発者が手動で設定する必要があります。サブスクリプション課金の場合、サブスクリプションのプランごとにProduct IDを割り振ります。
2.6.1. 使用できる文字と長さ
Product IDには以下の制限があります。
- 長さ: 最大100文字
- 使用できる文字
- 英数字: A-Z, a-z, 0-9
- 記号:
.
,-
,_
2.6.2. 命名の注意点
Product IDの命名は自由です。しかし、以下の点に注意が必要です。
- 一度保存すると変更も再利用もできません。削除しても同じProduct IDを別の課金アイテムへ流用できません。
- Product IDは大文字と小文字が区別されます。入力ミスによる事故を防ぐため、小文字に統一するか大文字を利用する際のルールを決めておくと安全です。
- 長さは最大100文字です。sourceをsrcと省略するような、無理に短い名前をつける必要はありません。読みやすさ重視で30〜60文字程度に収めるのが一般的です。
2.6.3. 推奨される命名の形式
Appleは以下のような逆ドメイン(reverse-DNS)形式を推奨しています。
- <Bundle ID>.<カテゴリ>.<詳細>
例:
- com.openai.chatgpt.plus.monthly
- com.openai.chatgpt.pro.monthly
- com.openai.chatgpt.plus.yearly
※あくまで例です。実際のChatGPTプランのProduct IDが上記のように設定されているとは限りません。
2.6.4. 命名のポイント
Product ID命名のポイントは以下3点です。
- Bundle ID を先頭に付ける: 衝突を防ぎ、どのアプリの課金か一目で分かるようにします。
- 階層をドットで区切る: カテゴリやプランをツリー構造で整理でき、ログや分析時にフィルタしやすくなります。
- 変動する可能性がある情報を含めない: 例えば価格変更を行なってもProduct IDの名前が一貫していれば混乱を防ぐことができます。
2.7. Levelとは
Levelとは同じSubscription Group内の相対的なプラン階層を示す整数値です。レベルは1以上の整数値で表されます。
レベルの値が小さいほど上位プラン、大きいほど下位プランと判断されます。Appleはこの情報を使って、ユーザーがプランを変更する際の移行ルールを自動的に制御します。
なお、App Store Connectのプラン一覧画面で並び替えを行うと、自動的にレベルの数値が割り振られます。手作業で数値を編集する必要はありません。
また、任意のタイミングでレベルを変更できます。将来の拡張に備えて、プランを余分に確保する必要はありません。(既存プランに契約者がいる場合は編集が制限される場合があります)
2.7.1. レベルの設定例
例として4つのプラン構成を考えます。基本機能が利用可能なBasicプランと、すべての機能が利用可能なPremiumプランの2種類があり、それぞれに月払いと年払いのプランが用意されている、という想定です。
- ベーシック月払い: 500円 / 1ヶ月
- ベーシック年払い: 5,000円 / 1年
- プレミアム月払い: 1,000円 / 1ヶ月
- プレミアム年払い: 10,000円 / 1年
Basicが下位プラン、Premiumが上位プランです。この場合、レベルの数値は以下のように設定します。
- ベーシック月払い: 2
- ベーシック年払い: 2
- プレミアム月払い: 1
- プレミアム年払い: 1
2.7.2. プラン変更時の挙動
プラン変更は以下の3パターンがあります。
- アップグレード: ユーザーが現在より上位のプランを購入すると、直ちにアップグレードされ、元のプランの残存期間に応じた日割り分が払い戻されます。
- ダウングレード: ユーザーが現在より下位のプランを選択した場合、現在のプランは次の更新日まで継続し、その後に下位プランで更新されます。
- クロスグレード: レベルが同じプランへ変更する場合、期間が同じであれば新しいプランは即時開始され、期間が異なる場合は次回の更新日から適用されます。
先ほどの4つのプランを例にすると変更時の挙動は以下のようになります。
- ベーシック月払いからベーシック年払いに変更: 同じレベル間の変更です。次回の更新タイミング(翌月)にプラン変更が適用されます。
- ベーシック月払いからプレミアム月払いに変更: 下位プランから上位プランへのアップグレードです。変更は即座に反映され、残存期間に応じて日割りで返金されます。
- プレミアム年払いからベーシック月払いに変更: 上位 → 下位へのダウングレードです。次回更新タイミング(翌年)にプラン変更が適用されます。それまでは現在のプラン(プレミアムプラン)が継続します。
このように月額プランと年額プランの違いはレベルではなく期間として別に管理されます。レベルで区別する必要はありません。
第3章 App Store Connectでサブスクリプションの設定を練習する
サブスクリプションの設定は頻繁に行う作業ではありません。ミスを防ぐため、事前にApp Store Connectでの操作に慣れておくと安全です。
そこで、仮のアプリを作成してApp Store Connectの操作を練習します。他のアプリとは独立しているため、操作を間違えても影響が及ぶことはありません。
あくまでApp Store Connectでの操作を練習するのが目的なので、Xcodeで新規プロジェクトを作成する、といった作業は不要です。気軽に試せるので、一度練習することをお勧めします。
3.1. 練習で設定するプランの概要
今回の練習で設定するプランは次の4種類とします。ベーシックとプレミアムの2種類があり、それぞれに月額と年額プランがある想定です。
- ベーシック月払い: 500円 / 月
- ベーシック年払い: 5,000円 / 年
- プレミアム月払い: 1,000円 / 月
- プレミアム年払い: 10,000円 / 年
レベルについては以下の設定とします。Basicが下位プラン、Premiumが上位プランです。
- ベーシック: 2
- プレミアム: 1
それでは作業を進めましょう。
3.2. アプリを作成する
- Apple DeveloperのIdentifiersページを開きます。
- App IDを新規作成します。
- Bundle IDは適当で構いません。混乱を避けるため、練習用であることがわかる名前をおすすめします。
- 以降の説明は
com.example.myapp
を設定したものと想定して説明を進めます。このBundle IDが登場したら、各自で設定した値に読み替えてください。 - Capabilitiesについては、In-App Purchaseにチェックをつけてください。
- App Store ConnectのAppsページから新規アプリを追加します。
- アプリ作成時は前の手順で作成したBundle IDを指定してください。
- アプリの名前は適当で構いません。「サブスク設定練習」など。
以上でアプリの作成は完了です。アプリ情報の編集画面に切り替わりますので、次の手順へ進んでください。
3.3. Subscription Groupを作成する
- MonetizationメニューのSubscriptionsをクリックします。
- 見出し「Subscription Groups」の近くにあるCreateボタンを押します。
- Reference Nameを設定します。ここでは
Sample Subscription Group
と入力します。
- Subscription GroupのReference Nameに命名規則や一般的な形式はありません。
MyAppSubscriptionGroup
やmy_app_subscription_group
など何でも構いません。 - Subscription GroupのReference NameはApp Store Connect内で重複して構いません。そのため
PaidPlan
のような普遍的な名前をつけることも技術的には可能です。 - ただし、普遍的な名前をつけると混乱の原因になります。
アプリ名+SubscriptionGroup
のような判別しやすい命名をお勧めします。
- Createボタンを押します。
以上でSubscription Groupが作成されました。
3.4. Subscription Groupのローカライズを行う
- 見出し「Localization」の近くにあるCreateボタンを押します。
- Localizationボタンを押してJapaneseを選択します。
- Display Nameを設定します。
- Display Nameはユーザーに表示される公開情報です。
- ここでは
サンプルのサブスクリプション
と入力します。
- Display Optionsを選択します。ここではUse App Nameを選択します。
- Createボタンを押します。
以上でSubscription Groupのローカライズは完了です。
3.5. プランを追加する
- 見出し「Subscriptions」の近くにあるCreate Subscriptionボタンを押します。ダイアログが表示されるので次のように入力します。
- Reference Name:
Basic Monthly Plan
- Product ID:
com.example.myapp.subscription.basic.monthly
- 入力したらCreateボタンを押します。
- Subscription Durationラベルの近くにあるChooseボタンを押します。選択肢の中から1 monthを選択します。
- Set Up Availabilityボタンを押します。JapanだけにチェックをつけてDoneボタンを押します。
- Add Subscription Priceボタンを押して値段を設定します。ダイアログが表示されるので次のように入力します。
- Priceボタンを押して選択肢の中から500 JPYを選びます。
- Nextボタンを押します。参考情報として日本円を基準とした各国の値段一覧が表示されます。
- 日本円の設定が正しければConfirmボタンを押します。
- Add Localizationボタンを押してローカライズを行います。ダイアログが表示されるので次のように入力します。
- Display Name:
ベーシック月払い
- Description:
基本機能をご利用いただけます
- Localizeの選択肢はJapaneseを選択してください。
- すべて入力したらAddボタンを押します。
- Saveボタンを押します。
以上でベーシック月払いプランを登録できました。残りのプランも同じ手順で登録します。参考までに設定例をまとめます。
プランの設定例一覧
- ベーシック月払い: 500円 / 月
- Reference Name:
Basic Monthly Plan
- Product ID:
com.example.myapp.subscription.basic.monthly
- Price: 500 JPY
- Display Name:
ベーシック月払い
- Description:
基本機能をご利用いただけます
- Reference Name:
- ベーシック年払い: 5,000円 / 年
- Reference Name:
Basic Yearly Plan
- Product ID:
com.example.myapp.subscription.basic.yearly
- Price: 5,000 JPY
- Display Name:
ベーシック年払い
- Description:
基本機能をご利用いただけます
- Reference Name:
- プレミアム月払い: 1,000円 / 月
- Reference Name:
Premium Monthly Plan
- Product ID:
com.example.myapp.subscription.premium.monthly
- Price: 1,000 JPY
- Display Name:
プレミアム月払い
- Description:
すべての機能をご利用いただけます
- Reference Name:
- プレミアム年払い: 10,000円 / 年
- Reference Name:
Premium Yearly Plan
- Product ID:
com.example.myapp.subscription.premium.yearly
- Price: 10,000 JPY
- Display Name:
プレミアム年払い
- Description:
すべての機能をご利用いただけます
- Reference Name:
3.6. 各プランのレベルを設定する
- Subscriptionsメニューに戻り、4つのプランが表示されていることを確認します。
- Edit Orderボタンを押します。
- プランの名前の隣にあるアイコンをドラッグ&ドロップして順番を入れ替えます。
- プランを横方向にドラッグすると同じレベルになります。
- プランを縦方向にドラッグするとレベルを上下できます。
- 編集が完了したらSaveボタンを押します。
以上でレベルの設定作業は完了です。
3.7. まとめ
お疲れ様でした。以上がサブスクリプションのプラン設定を行う操作の全体像となります。
作業中に気がつかれたかもしれませんが、Billing Grace PeriodやFamily Sharingの設定は行っていません。これらの設定は行わなくてもApp Storeの審査に提出できるため、意図的に飛ばしました。
なお、本番のプラン設定では購入画面スクリーンショットの添付とレビュー担当者へのコメントの記入が必須です。また、プランを識別するためのアイコン画像の設定も行うとユーザーに親切です。
第4章 アプリを公開するための事前準備を行う
この章では有料アプリを公開するための事前準備を行います。必ずしもアプリの開発前に行う必要はありませんが、事前に済ませておくと時短になります。
4.1. 銀行口座と納税フォームを設定する
以下の記事を参考に、App Store Connectで銀行口座と納税フォームの設定を行なってください。この作業が未完了の場合、アプリを提出できません。
なお、銀行口座と納税フォームが入力途中の状態でも無料アプリを新規に公開したり、既存の無料アプリをアップデートしたり問題なく行えます。影響を受けるのは有料アプリだけです。
4.2. Small Business Program(SBP)を申請する
Small Business Program(SBP)を申請すると、App Storeの手数料が半額になります。具体的には、通常の手数料が30%のところ15%になります。
条件はアプリの年間収益が100万 USD「記事投稿時の為替レートでおよそ1億5000万円」未満に該当する場合です。ほぼ例外なく、個人のアプリ開発者は該当するはずです。
なお、SBPは自動適用されず、自己申告する必要があります。また、SBPが適用されるのは申請後の収益に限られます。申請前に収益があったとしても、過去に遡って適用されることはありません。
せっかくアプリを収益化できたのに手数料を余計に取られては非常にもったいないです。有料アプリを開発すると決めたら忘れず申請しましょう。
4.2.1. 申請方法
SBPの申請は以下のページから行います。
Enroll - App Store Small Business Program - Apple Developer
4.2.2. 申請項目の一覧
申請の際に回答する項目は次の5つです。
Have you reviewed and accepted the latest Paid Applications Agreement (Schedule 2 to the Apple Developer Program License Agreement) in App Store Connect?
App Store Connect で最新の Paid Applications Agreement(Apple Developer Program License Agreement の Schedule 2)を確認し、承諾しましたか?
Yesを選択しなければ申請できないため、実質的に選択肢はYesだけです。
Do you have majority (over 50%) corporate, individual, or partnership interest in the ownership or shares of another Apple Developer Program member account?
あなたは、他の Apple Developer Program メンバーアカウントの所有権または株式について、企業・個人・パートナーシップとして過半数(50%超)の持分を有していますか?
筆者はNoを選択しました。
Does another Apple Developer Program member have majority (over 50%) corporate, individual, or partnership interest in the ownership or shares of your account?
他の Apple Developer Program メンバーは、あなたのアカウントの所有権または株式について、企業・個人・パートナーシップとして過半数(50%超)の持分を有していますか?
筆者はNoを選択しました。
Do you have ultimate decision-making authority over another Apple Developer Program member account?
あなたは、他の Apple Developer Program メンバーアカウントに対して最終的な意思決定権限を有していますか?
筆者はNoを選択しました。
Does another Apple Developer Program member have ultimate decision-making authority over your account?
他の Apple Developer Program メンバーは、あなたのアカウントに対して最終的な意思決定権限を有していますか?
筆者はNoを選択しました。おそらく、読者の方も大半は2から4の回答がNoになるはずです。
最後に、以下のチェックボックスにチェックをつけます。
To the best of your knowledge, you and your Associated Developer Accounts earned no more than 1,000,000 USD in total proceeds (sales net of Apple’s commission and certain taxes and adjustments) during the twelve (12) fiscal months occurring in the prior calendar year. You further acknowledge that the information provided above is accurate, and providing false or inaccurate information is grounds for disqualification, termination, and forfeiture of payment.
あなたの知る限りにおいて、あなたおよびあなたの関連開発者アカウントは、前暦年に該当する連続する 12 会計月の期間において、総収益(Apple の手数料および一部の税金・調整控除後)が合計 1,000,000 米ドルを超えていないことをここに表明します。また、上記の情報が正確であることを確認し、虚偽または不正確な情報を提供した場合は、失格・契約解除および支払いの没収の対象となることに同意します。
各項目の設定を確認したら、Submitボタンを押してください。申請作業は以上で完了です。
申請を行うと数分後にAppleから「We’ve received your request to join the App Store Small Business Program」という件名のメール通知が届きます。届かない場合は各設定項目を見直してみてください。
4.2.3. 申請結果はいつ届く?
筆者の場合、2025年6月16日に申請して、2025年7月12日にSBPが適用されたとのメール通知が届きました。インターネットで事例を探してみると、申請に不備がなければ1ヶ月程度で審査が完了するとの報告が多くあります。審査にかかる期間は最短で1ヶ月程度を見込んでおくと良さそうです。
4.3. 利用規約とプライバシーポリシーを作成する
サブスクリプション課金アプリ特有の作業ではありませんが、利用規約とプライバシーポリシーの策定は必須です。Webページを作るのが面倒な場合、例えばNotionに記入してページを公開する方法でも問題ありません。
4.4. 特定商取引法に基づく表示を作成する
アプリ内に表示する義務はありませんが、特定商取引法に基づく表示(以下、特商法表記)は必要です。例えばアプリのWebサイトに表示することで対応します。
App Store Connectのレビューでは各国の法律に遵守しているかまではチェックされません。そのためアプリ内に特商法表記や、そのページへのリンクがなくてもAppleのガイドラインさえ満たしていればアプリの審査は通過します。
ここでは記入例は示しません。特商法表記の記入は以下の資料を参考に各自の判断で作成してください。
第5章 課金処理を実装する
本記事のために最小構成のサンプルアプリを用意しました。この章では以下のサンプルアプリの実装を解説します。
5.1. サンプルアプリの概要
サンプルアプリはSwiftUIとStoreKit 2を利用して実装しました。サブスクリプション購入画面はiOS 17で導入されたSubscriptionStoreViewを利用することで実装の手間を省いています。
処理はすべてアプリ内で完結し、自前のサーバーを利用しない形で実装しています。クロスプラットフォーム対応は行わず課金の方式をサブスクリプションに限定しているため、App Store ConnectをSSoTとみなす設計です。
アプリを起動すると、直後にバックグラウンドで課金に関連するイベントの監視を開始します。受信したイベントをもとに現在のプランを判定し、その都度画面を更新します。
5.2. イベントの監視
課金に関連したイベントはいくつか定義されています。サンプルアプリでは以下3つの非同期シーケンスをアプリ起動直後からバックグラウンドタスクで監視します。
- Transaction.currentEntitlements: 実行時点で有効なトランザクションを返します。
- Transaction.updates: 起動中のアプリ外で発生したトランザクションも含めて、新しく発生したトランザクションすべてを返します。
- Product.SubscriptionInfo.Status.updates: サブスクリプション固有の情報、例えば有効期間中に発生したキャンセル情報を含むトランザクションを返します。
Transaction.updatesは消耗型の課金を含むすべてのトランザクションを返します。Product.SubscriptionInfo.Status.updatesと機能的には重複しています。
それならばサブスクリプションに関連するトランザクションだけを監視すれば無駄がないと思われるかもしれませんが、未完了のトランザクションを監視できるのはTransaction.updatesだけです。そのため、アプリの課金方式がサブスクリプションに限定される場合もTransaction.updatesの監視が必要になります。
一方で、Transaction.updatesが監視できるのは大雑把にいえばお金が動くイベントに限られます。例えばサブスクリプションの有効期間中に自動更新をキャンセルしたイベントは拾えません。
5.3. 商品一覧の取得
サンプルアプリではenum Plan
としてサブスクリプションのプランを定義しています。さらに、Computed Propertyとして各ケースに紐づくProduct IDをハードコードしています。
StoreKit 2にはSubscription Group IDに紐づくProduct IDを一括で取得するメソッドが用意されているため、ハードコードは必須ではありません。ただし、App Store ConnectからProduct IDを取得できない場合、Product IDと各プロダクトの関連を知る手段がなくなりプラン判定ができなくなります。
都合よく(悪く?)プロダクトの情報だけが取得できない状況に陥ることは滅多にないと思われます。とはいえ、後で説明するXcodeを利用したローカル環境でのテストでは、設定項目としてProduct IDが取得できないというフラグが用意されています。滅多にないが、あり得るパターンとして気に留める必要はあります。
5.4. トランザクションの検証
各非同期シーケンスが返すTransaction
はVerificationResult
型に包まれた状態で返されます。サンプルアプリでは.verified
ケースにマッチさせることで不正なトランザクションを弾いています。
さて、このVerificationResultによる検証は具体的にトランザクションの何を検証しているのでしょうか。答えはトランザクションの実態であるJWS(JSON Web Signature)の署名です。
従って、VerificationResultの.verified
ケースで検証されるのは、そのトランザクションが間違いなくAppleが発行したものであるかだけです。トランザクションの有効期限が切れていないか、支払いが取り消しされていないか、といったビジネス観点の検証は開発者の責任で行う必要があります。
例えばキャンセル済みのサブスクリプションがある場合、Product.SubscriptionInfo.Status.updatesは更新日に到達したタイミングで有効期限が切れたトランザクションを返します。また、サブスクリプション有効期間中にプランをアップグレードした場合、Transaction.currentEntitlementsは変更前のトランザクションに"isUpgraded": true
フラグを設定した状態で、プラン変更前後の2つのトランザクションを返します。
なお、自前サーバーでトランザクションを検証する場合も手順は同じです。トランザクションのJWSを検証し、その後に有効期限や取り消し状態を確認します。
5.5. その他の留意点
- 未完了のトランザクションはTransaction.updatesで監視できるため、Transaction.unfinishedの監視は不要です。Transaction.unfinishedは互換性の観点で残されているだけのようです。
- StoreKitのAPIが呼び出せるのはホストアプリに限定されています。Action Extensionなど拡張機能からStoreKitのAPIを呼び出すと機能しません。例えばTransaction.currentEntitlementsは何も値を返しません。
- Xcodeを利用したローカル環境でのテストでは、課金関連イベントは即座に発生します。一方、サンドボックス環境やプロダクション環境ではApp Store Connectサーバーとの通信が発生するため、処理に数秒かかります。サンプルアプリでは実装を単純にするため処理待機中のUIは特に実装せず、変更イベントが届いたタイミングで画面を更新するように実装しています。
第6章 Xcodeのローカル環境で課金をテストする
この章ではXcodeを利用してローカル環境で課金をテストする方法を説明します。この方法はApp Storeとの通信が発生せず、オフラインでテストできるのが特徴です。
6.1. StoreKit Configurationを作成する
第5章で紹介したサンプルアプリを利用してテストを行う場合、この項目は読み飛ばしてください。
- XcodeのメニューからNew→File from Templateを選びます。
- OtherセクションのStoreKit Configuration Fileを選択してNextボタンを押します。
- Nameに適当な名前を入力します。
SubscriptionTest
など。 - Sync this file with an app in App Store Connectのチェックは外すことをおすすめします。
- App Store Connectと同期させてもかまわないのですが、それならば第7章で説明するサンドボックス環境でのテストをおすすめします。
- ローカル環境でのテストはパラメータを変更しながらさまざまな状況をテストしたいはずですので、通常はチェックを外して作成するのがおすすめです。
- Createボタンを押します。
その後は必要に応じて課金の内容を編集してください。各項目の意味は第2章で説明していますので、参考にしてください。
なお、Xcodeローカル環境テスト固有の設定項目もあります。例えば以下の項目です。
- Default Storefront: どの国または地域のApp Storeを表示させるか制御します。サンプルアプリではJapan (JPY)を選択しています。
- Default Localization: デフォルトの言語設定です。サンプルアプリではJapaneseを選択しています。
- Subscription Renewal Rate: サブスクリプションの更新を実時間で行うか、倍速で行うか制御します。例えば、以下のような選択肢があります。
- Monthly Renewal Every 5 Minutes: 1ヶ月を5分に短縮します。サブスクリプションの期間が一年の場合、
5 * 12
となるため1時間ごとにイベントが発火します。 - Any Renewal Every 5 Minutes: サブスクリプションの期間が1ヶ月か1年か、長さに関係なく5分ごとにイベントを発火させます。
- Monthly Renewal Every 5 Minutes: 1ヶ月を5分に短縮します。サブスクリプションの期間が一年の場合、
6.2. SchemeにStoreKit Configurationを適用する
StoreKit Configurationを作成しただけではローカル環境でのテストは行えません。以下の操作が必要になります。
- XcodeのメニューからProduct→Scheme→Edit Schemeを選びます。
- ダイアログが開いたら、タブのメニューとしてOptionsが選択されていることを確認します。
- StoreKit ConfigurationのNoneボタンをクリックします。選択肢が表示されたら、先ほど作成したStoreKit Configurationを選びます。
- Closeボタンを押します。
以上でローカル環境でのテスト準備が整いました。
6.3. 実機またはシミュレーターを起動してテストを行う
普段どおり実機またはシミュレーターを起動します。あとは各自の目的に応じて動作を試してください。
- アプリ起動中にXcodeのメニューからDebug→StoreKit→Manage Transactionsを開くと、トランザクションを管理することができます。トランザクションの払い戻しなどは、このメニューから行います。変更はその場で即座に反映されます。
- StoreKit Configurationの設定はアプリ起動中は固定されます。変更してもリアルタイムには反映されません。反映させるにはアプリを終了して、もう一度起動してください。
- アプリを終了しても過去のトランザクション情報は残り続けます。初期状態に戻すには、Manage Transactionsメニューからすべてのトランザクションを選択して右クリックメニューからDeleteを選んでください。
6.4. 本番環境との違い
本番環境とXcodeのローカル環境テストでは以下のような違いがあります。いずれも外観に関する違いだけで、StoreKit 2の挙動に違いはありません。
- 購入確認の方法が異なります。本番環境では例えばiPhoneの場合、電源ボタンをダブルクリックで承認するところ、Xcodeローカル環境テストでは「サブスクリプションに登録」ボタンを押すだけで支払いが完了したことになります。
- 購入完了後のダイアログは英語で表示されます。StoreKit ConfigurationでストアをJapanに設定していても、英語表記となります。
- SwiftUIのSubscriptionStoreViewは日本語で表示されますが、
.manageSubscriptionsSheet()
は英語で表示されます。不具合なのか仕様なのかは不明です。
なお、トランザクションのJWSに含まれるenvironmentの値はテスト環境と本番環境で異なります。その他にも本番環境とテスト環境では格納される値に違いが生じる場合があります。本記事はアプリ内で処理を完結させる方式のため説明は省きますが、自前のサーバーでトランザクションの検証を行う場合は考慮が必要になります。
第7章 App Store Connectのサンドボックス環境で課金をテストする
この章ではApp Store Connectのサンドボックス環境で課金をテストする方法を説明します。早期に不具合を発見するため、アプリの公開前には必ずサンドボックス環境でのテストを行いましょう。
7.1. テストユーザーを作成する
- App Store ConnectのUsers and Accessページを開きます。
- Sandboxをクリックし、Add Test Accountボタンを押します。
- テストユーザーの情報を入力し、Createボタンを押します。
- First NameとLast Nameは適当で構いません。
- パスワードは複雑なものを設定してください。
Password-20250724
のような推測されやすい単語や文字列を含んでいると拒否されます。 - メールアドレスはApple IDと紐づけられていないものが必要になります。テスト用に新しくGoogleアカウントを作り、Gmailのメールアドレスを指定するのがお勧めです。
- メールアドレスは
yamada+testuser@example.com
のようにプラス記号つきのエイリアスも指定可能です。 - Country or RegionはJapanを選択してください。
7.2. 実機にテストユーザーでサインインする
以下の操作手順は記事を投稿した時点で最新のiOS 18.5で確認したものです。iOS 17以前は操作手順が異なる可能性がありますので、ご注意ください。
- 設定アプリ→デベロッパ→SANDBOX Appleアカウントへ進み、サインインボタンを押します。
- 先ほど作成したテストユーザーのメールアドレスとパスワードを入力します。
- 2ファクタ認証画面が表示されたら「その他のオプション」ボタンを押します。選択肢の中から「アップグレードしない」を選びます。
- SANDBOX Appleアカウントにメールアドレスが表示されたら設定完了です。
7.3. App Store Connectに本番環境で利用するサブスクリプション情報を登録する
第3章の手順を参考に、App Store Connectに本番で利用するサブスクリプション情報を登録してください。この時点では、レビューに提出するスクリーンショットと担当者へのコメントは空欄のままで構いません。
サンドボックス環境では本番で提出するサブスクリプション情報を参照します。実際の支払いが発生しない点を除けば、サンドボックス環境での挙動は本番と同じです。
従って、本番で公開予定のサブスクリプション情報をサンドボックス環境テストの段階で設定すれば、後の作業が減らせて時短になります。あるいは順番を逆にして、本番公開予定の情報を登録し終えたら、サンドボックス環境でのテストを行う流れでも良いかと思います。
7.4. テストを行う
この時点でサンドボックス環境でのテストが可能になっています。以下の手順で操作します。
- Xcodeのメニューを開き、Product→Scheme→Edit Schemeを選びます。
- StoreKit ConfigurationをNoneに設定します。
- 実機でアプリを起動し、課金をテストしてください。
StoreKit Configurationが設定されていなければサンドボックス環境が参照されます。サブスクリプションの情報が取得できない場合、アプリのBundle IDとApp Store Connectに登録されているBundle IDが一致しているかご確認ください。
サンドボックス環境ではXcodeを利用したローカル環境テストと同様に、課金テストのパラメータ変更や、テストユーザーのトランザクションの編集が行えます。App Store ConnectのUsers and Accessページを開き、操作対象のテストユーザーを選択するとメニューが表示されます。
第8章 アプリを提出する
最後の章です。アプリの公開まであと一歩です。
8.1. App Store Connectでサブスクリプション情報の登録を行う
まずはアプリの一般情報を入力します。その後、第3章を参考にサブスクリプションの情報を登録してください。
8.1.1. スクリーンショットと担当者へのコメントを設定する
第7章から順番に読み進めている場合、レビューに提出するスクリーンショットと担当者へのコメントが空欄になっているかと思います。どちらもアプリの公開に必須ですので入力してください。
8.1.2. サブスクリプションのプラン詳細画面のステータス表記について
記事を投稿した時点では、自動更新サブスクリプションのプラン設定画面についてローカライズの情報が正しく入力されていてもSTATUS Prepare for Submissionと表示されます。おそらく不具合です。一方、Subscription Groupの画面では、それぞれのプランにSTATUS Ready to Submitと表示されます。
8.2. アプリバージョン登録画面のIn-App Purchases and Subscriptionsを設定する
サブスクリプション情報の登録が完了すると、アプリバージョンの設定画面に以下の項目が出現します。ここで、どのサブスクリプションを公開するか選びます。
Select the in-app purchases or subscriptions you want to add to this app version. (Optional)
上記の項目はアプリのビルド情報を設定するセクションの近くに表示されます。表示されない場合、サブスクリプション情報の設定に漏れや不備があるため再確認してください。
この設定を忘れると、アプリには課金機能が実装されているのにサブスクリプションのプランが表示されず課金できない状態となります。レビュー担当者は、現在のバージョンでは意図的に課金の導線を用意していないのだと判断するため、アプリ本体に不備がなければ審査をパスしてしまうのです。
筆者はこの設定を忘れた状態でアプリを公開してしまい、ユーザーから課金できないとの苦情をいただく失敗を経験しています。そんな失敗があり得るのかと思われそうですが、アプリが完成して良い気分になっていると操作を間違える可能性は十分にあり得ます。
8.3. アプリを提出する
最後にSubmitボタンを押して、アプリを提出しましょう。アプリ本体の審査に加えてサブスクリプション課金の審査も行われるため、無料のアプリと比べると多少レビューにかかる時間は長くなります。
筆者の場合、1プランだけのシンプルなサブスクリプション課金アプリを提出したところ、24時間ほどで審査をパスしました。はじめてのサブスクリプション課金アプリでしたので、普段より審査が長引いたものと思われます。
おわりに
プラットフォームをAppleデバイスに限定するなら、StoreKit 2標準機能に寄せることでサブスクリプション課金がシンプルに実装できます。シンプル != 簡単
ですので、手間はかかりますが挑戦する価値はあると思います。
AI花盛りの時代、思い描いたアプリを具現化するコストは日々小さくなっています。アプリが完成したら課金機能も組み込んでみてはいかがでしょうか。
(余談)ドイツ銀行から謎の振込が!?
アプリの収益は銀行振込で受け取ります。その際、Appleはドイツ銀行を利用して振り込みを行います。
入出金明細には「*ドイツギンコウAPPLEブン」と表記されます。怪しげな振込かと警戒しますが、正式なAppleからの振り込みですのでご安心ください。
参考資料
- Overview for configuring in-app purchases - Apple Developer
- Auto-renewable Subscriptions - Apple Developer
- StoreKit - Apple Developer
-
旧StoreKit APIはSKPaymentQueueのようにSKプレフィックスがつけられています。ただし非推奨になったのは課金関連のAPIだけであり、SKOverlayなどは今も現役です。SKプレフィックスがつけられたAPIを見つけたら、対応しているOSに注意してください。 ↩︎
-
筆者が2024年に支払った年会費は日本円で12,980円でした。しばらく改定されていないので、そろそろ値上げされるのではないかと怯えています。 ↩︎
-
Overview for configuring in-app purchases - Configure in-app purchase settings - App Store Connect - Help - Apple Developer ↩︎
-
アプリ・ゲーム・ウェブサービスでポイントを発行したい ゲーム内通貨に関する資金決済法の対応 前払式支払手段とは?〜 | 弁護士法人 LEON ↩︎
Discussion
githubへのリンクが間違っているようです。
が正しいアドレスかと思われますが合っていますか?
URLを修正しました。ご指摘ありがとうございます!