【Flutter/iOS】アプリ内課金の実装方法!in_app_purchase + StoreKit
この記事からわかること
- Flutter/Dartでアプリ内課金を実装する方法
- iOSアプリでの実装
- in_app_purchase・in_app_purchase_storekitパッケージの使い方
index
[open]
\ アプリをリリースしました /
- Android Studio:Narwhal Feature Drop
- Xcode:26.0.1
- iOS:26
- Flutter:3.38.3
- Dart:3.10.1
- in_app_purchase:3.2.3
- macOS(M1):Tahoe 26.0.1
iOSのアプリ内課金
iOSアプリでアプリ内課金を実装する大まかな流れは以下の通りになります。Flutterで実装する場合でも以下の流れは必要になるので先に対応しておいてください。
大まかな実装の流れ
- 有料App契約(App Store Connect)
- 課金アイテムの設定(App Store Connect)
- Capabilitiesの追加(Xcode)
- 課金処理の実装(本記事)
詳細な設定手順などは以下の記事を参考にしてください。
またiOSアプリではアプリ内課金で購入できるアイテムとして以下の4種類があります。今回は広告削除などを想定して「非消耗型」の実装で進めてみたいと思います。
| 種類 | 概要 |
|---|---|
| 消耗型 | 一度購入すると使い切り(例: ゲーム内アイテム) |
| 非消耗型 | 一度購入すると永久に利用可能(例: 広告の削除、特定の機能のアンロック) |
| 自動更新サブスクリプション | 月額や年額で継続的に課金(例: サブスクリプション型サービス) |
| 非更新サブスクリプション | 定期購読だが自動更新なし(例: 一定期間限定のサービス) |
in_app_purchaseパッケージ
Flutterでアプリ内課金を実装する場合はin_app_purchaseパッケージを使用します。in_app_purchaseはiOS / Androidでのアプリ内課金機能をラップしてFlutterで使えるようにしてくれているパッケージになります。
インストールコマンド
$ flutter pub add in_app_purchase
pubspec.yamlの中身
dependencies:
in_app_purchase: ^3.2.3
動作確認用のテスト環境を整える
アプリ内課金のテスト環境は.storekitファイルを用意することで簡単に構築することが可能です。設定方法は以下の記事で紹介しているので参考にしてください。
作業としては.storekitファイルを作成してスキームに設定するだけです。私はAndroid StudioでFlutterアプリを開発しておりそこからiOSシミュレーターをビルドした時に何故かアイテムが取得できませんでした。ですがXcode(Runner)から起動してビルドしたら無事アイテムを取得することができました。そのあとにAndroid Studioでビルドするとこちらでも正常に取得できるようになっていたので一度Xcode経由でビルドする必要があるようです。
実装の手順
- アイテム情報を取得する:queryProductDetails
- 購入処理を開始する:buyConsumable / buyNonConsumable
- 購入ストリームの観測:purchaseStream
- 復元処理の実装
1.アイテム情報を取得する:queryProductDetails
課金アイテムを取得するにはqueryProductDetailsメソッドを使用します。引数には取得対象となる課金アイテムIDを渡します。レスポンスはProductDetailsResponse型で取得できます。
final Set<String> _productIds = { `com.test.removeAds` };
final _iap = InAppPurchase.instance;
final response = await _iap.queryProductDetails(_productIds);
// 取得に失敗したIDが格納される
print('notFound: ${response.notFoundIDs}');
// 取得に成功したアイテム情報が格納される
print('products: ${response.productDetails}');
ProductDetailsResponse型のnotFoundIDsに 取得に失敗したIDが格納され、productDetailsに取得に成功したアイテム情報が格納されます。List<ProductDetails>型で取得できるのでこれを使用して購入処理や実際にUIに金額などを表示させます。
ProductDetailsのプロパティ
| フィールド名 | 説明 | 例 |
|---|---|---|
| id | 製品ID | com.test.removeAds |
| title | 表示用タイトル | 広告削除 |
| description | 商品説明 | アプリ内に表示されている広告を削除することができます。 |
| price | 価格(通貨記号付き) | ¥300 |
| rawPrice | 価格 | 300.0 |
| currencyCode | 通貨コード(ISO 4217形式) | JPY |
| currencySymbol | 通貨記号(ロケールに準拠) | ¥ |
2.購入処理を開始する:buyConsumable / buyNonConsumable
課金アイテムを購入するにはbuyNonConsumableメソッドを使用します。購入メソッドを呼び出すとOS標準のストアの購入シートが表示され決済フローを開始されます。
Future<void> buy(ProductDetails product) async {
final PurchaseParam param = PurchaseParam(productDetails: product);
await InAppPurchase.instance.buyNonConsumable(purchaseParam: param);
}
購入するアイテムはPurchaseParam型でラップして渡します。
今回は非消耗型なのでbuyNonConsumableですが、消耗型であればbuyConsumableを使用する必要がありアイテムによって呼び出すメソッドが異なってくるので注意してください。またbuyConsumableを使用する際はautoConsume: trueの設定をしておかないと正しく動作しないことがあるので注意してください。
await InAppPurchase.instance.buyConsumable(
purchaseParam: PurchaseParam(productDetails: product),
autoConsume: true,
);
3.購入ストリームの観測:purchaseStream
購入処理の開始後には処理中・成功・失敗などのステータスが変化します。in_app_purchaseではストリームとして提供されておりpurchaseStreamから取得することができます。引数にはステータス更新周りのコールバック・完了処理・エラー処理を渡すことができます。トランザクションが無事完了したら明示的にcompletePurchaseを呼び出して処理は完了です。
/// 購入ストリームの監視開始
final purchaseUpdated = _iap.purchaseStream;
_subscription = purchaseUpdated.listen(
_onPurchaseUpdate,
onDone: () => _subscription?.cancel(),
onError: (error) {
logger('purchaseStream Error: $error');
},
);
/// 購入更新コールバック
void _onPurchaseUpdate(List<PurchaseDetails> purchaseDetailsList) {
for (final purchaseDetails in purchaseDetailsList) {
switch (purchaseDetails.status) {
case PurchaseStatus.pending:
logger('購入処理中...');
break;
case PurchaseStatus.purchased:
logger('購入成功!');
// 購入成功処理
_deliverProduct(purchaseDetails);
break;
case PurchaseStatus.error:
logger('購入エラー: ${purchaseDetails.error}');
break;
case PurchaseStatus.restored:
logger('購入復元!');
// 復元成功処理
_deliverProduct(purchaseDetails);
break;
default:
break;
}
// 完了処理
if (purchaseDetails.pendingCompletePurchase) {
_iap.completePurchase(purchaseDetails);
}
}
}
購入に成功したらユーザーに対してアイテムの付与等をしてあげればOKです。
4.復元処理の実装
アプリ内課金の実装をする際には「復元処理の実装」が必須になります。これを実装しないとリジェクトされてしまうので注意してください。復元処理は簡単でrestorePurchasesを実行するだけです。これで再度観測していたストリームに復元成功ステータスが流れるのでハンドリングしてあげてください。
/// 購入復元処理
Future<void> _restore() async {
await InAppPurchase.instance.restorePurchases();
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。





