Flutter アプリ内課金（サブスクリプリプション）のつまずき集


##追記
2021/3/31 つまずきその7追加

2021/5/29 in_app_purchaseを1.0.1に更新した際、import package:・・・・の書き方が変更していたので追加

#内容
今回初めて、アプリ内課金（サブスクリプション、以下サブスク）の実装をしたので、忘却録として書きます。
2021/3/12 時点のサンプルコードを使用。
サブスク導入でつまずいた部分のみを記載（2021/03/26時点ではiOSのつまずき集のみ）。実装は気が向いたら書く予定。

テスト中に何回も購入処理をして、トランザクションが残っていると色々と大変らしいので、[YuKiOさんの記事](https://qiita.com/YuKiO-OO/items/a0fe8e0a256afbb69fc7#%E9%87%8D%E8%A6%81%E8%AA%B2%E9%87%91%E6%A9%9F%E8%83%BD%E3%81%A7%E3%83%8F%E3%83%9E%E3%81%A3%E3%81%9F%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88)を先に読んでおくことをおすすめします。

追記 SKPaymentQueueWrapper()を使用するのにstore_kit_wrappers.dartをインポートしているが、
バージョンを1.0.1に更新したら、インポートの書き方が少し変わっていた

変更前：import 'package:in_app_purchase/store_kit_wrappers.dart';

変更後：import 'package:in_app_purchase_ios/store_kit_wrappers.dart';


#前提条件
●App Store, Google Play側の準備ができている（サブスクアイテム作成）
●今回はサブスクのみなので、consumable(消耗品)関係は未対応

以下のような感じに複数のアイテムをセットしています

```
Set<String> _kProductIds = {
 Platform.isIOS
     ? 'ios.subscription01'
     : 'android.subscription01',
 Platform.isIOS
     ? 'ios.subscription02'
     : 'android.subscription02',
 Platform.isIOS
     ? 'ios.subscription03'
     : 'android.subscription03',
};
```

#環境

PC:MacOS BigSur
エディター:Visual Studio Code

Flutter:1.22.6 -> 2.2.0(2021/5/29変更)

in_app_purchase:0.3.5+1 -> 1.0.1(2021/5/29変更)


#つまずき集
サンプルコードを修正しながら実行してみて動きを見ていた時のつまずき集です
サンプルコードはこちら

https://github.com/flutter/plugins/blob/master/packages/in_app_purchase/in_app_purchase/example/lib/main.dart

###つまずきその1
　シミュレータでデバッグしたら、return されてしまう

```
    productDetailResponse = await _connection.queryProductDetails(_kProductIds);
    if (productDetailResponse.error != null) {
      setState(
        () {
          _queryProductError = productDetailResponse.error.message;
          _isAvailable = isAvailable;
          _products = productDetailResponse.productDetails;
          _purchases = [];
          _notFoundIds = productDetailResponse.notFoundIDs;
          _consumables = [];
          _purchasePending = false;
          _loading = false;
        },
      );
      return;
    }
```

解決方法：シミュレータで実行していることが原因らしく、実機をつないで、デバッグしたらここで、returnしないらしいです（iPhone11で確認しました）
[こちら](https://developer.apple.com/forums/thread/651858)を参考にしました


###つまずきその2
　サブスク用に各プラットフォームで作成したアイテムを_kProductIdsにセットしたが、見つからないと画面に表示されてしまう
画像のように[ios.subscription01,・・・] not foundと表示される

<img width="50%" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631339/e8d4426b-63c5-73d7-2d4f-8d8b73c1a647.png">

```
    productDetailResponse = await _connection.queryProductDetails(_kProductIds);
    if (productDetailResponse.productDetails.isEmpty) {
      setState(
        () {
          _queryProductError = null;
          _isAvailable = isAvailable;
          _products = productDetailResponse.productDetails;
          _purchases = [];
          _notFoundIds = productDetailResponse.notFoundIDs;
          _consumables = [];
          _purchasePending = false;
          _loading = false;
        },
      );
      return;
    }
```
解決方法：こちらもシミュレータではproductDetailResponse.productDetails.isEmptyがtrueになってしまうので、実機に接続することで、解決できました
プラグインの公式ページにも以下のように記載がありました

```
Use flutter run to install the app and test it.
Note that you need to test it on a real device instead of a simulator, and signing into any production service (including iTunes!)
with the test account will permanently invalidate it.
Sign in to the test account in the example app following the steps in the In-App Purchase Programming Guide.
 ```

###つまずきその3
　SANDBOXアカウントの変更ができない
サブスク購入ボタンをクリック時にアカウントを入力するダイアログが表示され、一度、入力したユーザーが変更できなかった
解決方法：[こちら](https://spinners.work/posts/ios-sandbox/)の記事を参考にすると、SANDBOXアカウントの変更ができる

###つまずきその4
　SANDBOXアカウントの変更をしてもアイテムの購入ができない
　購入しても、purchaseDetails.status が purchased にならず、購入後の処理が実行されない

解決方法：SANDBOXアカウントのアドレスを存在するアドレスにする
       (たまたまそうなっただけで、正しいかどうかは未確認)

###つまずきその5
　購入フォームが2回表示される

解決方法：つまずき4が解決したら表示されなくなった
[github](https://github.com/flutter/flutter/issues/75745)にも上がってたけど、解決はしていない？
原因は不明のまま

###つまずきその6
　保護者へのリクエストが表示され、購入が進まない

解決方法：[github](https://github.com/flutter/flutter/issues/68626)に書いてありますが、PurchaseParamメソッドの引数のsandboxTestingをfalseにする。

```
 PurchaseParam purchaseParam = PurchaseParam(
                    sandboxTesting: false,
                    productDetails: productDetails);
```

###つまずきその7
　Androidでアイテム購入ボタンを押すと、「アプリケーションのこのバージョンは請求サービスに対応していません。」と表示されて、購入処理が完了しない

解決方法：[stackoverflow](https://stackoverflow.com/questions/11068686/this-version-of-the-application-is-not-configured-for-billing-through-google-pla)を参考にした。

```
flutter build apk
```

上記コマンドでapkを作成。アプリをアルファ/ベータ版でgoogle playから申請。

あとは、apk作成前に、pubspec.yamlのversionを上げた。

```
version: 1.0.0+1
```

[公式](https://flutter.dev/docs/deployment/android)を参考に
リリース済のアプリのバージョンが1.1.0+ 10なら2.0.0+11などに上げた。


##参考

[公式](https://pub.dev/packages/in_app_purchase/versions/0.3.5+1)

[YuKiOさんのQiita](https://qiita.com/YuKiO-OO/items/a0fe8e0a256afbb69fc7)

[スタディプラスのブログ](https://tech.studyplus.co.jp/entry/2020/04/13/102204)

[mediumの記事](https://medium.com/bosc-tech-labs-private-limited/how-to-implement-subscriptions-in-app-purchase-in-flutter-7ce8906e608a)

[Fulutter Community](https://medium.com/flutter-community/flutter-in-app-purchase-step-by-step-e5611501f723)


●各プラットフォームのアイテムの作成方法
・iOS
[Qiita](https://qiita.com/Masataka-n/items/6f98a5a9fee7b28ccd1f)


・Android
[Qiita](https://qiita.com/koichi-ozaki/items/7ac8c8135705867e8b14)