in_app_purchase を用いたアプリ内課金について

アプリ内課金に使用しているパッケージ

https://pub.dev/packages/in_app_purchase

実装例

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

Skimieでは、課金のメソッドをNotifierProviderで管理しています。

アプリ内課金を実装するためには、Google Play Console とApp Store Connect(Apple)の設定が必要です。

それぞれのプラットフォームの設定は、

Google :

https://developer.android.com/google/play/billing/getting-ready?hl=ja

https://www.revenuecat.com/docs/android-products
Apple:

https://www.revenuecat.com/docs/ios-products

アプリ内課金の流れ


  1. アプリ内購入が利用可能な状態なのか確認する

  2. 購入品の製品IDのSetを用意する。

  3. それぞれのストアに問い合わせて製品情報を取得する

  4. 上記で取得した製品情報を使って購入処理を行う

  5. 購入処理進行を監視して、購入が完了した後にバックエンドでレシートの検証を行う

  6. 検証が成功した場合、ジェムをユーザーに付与する(課金の対価を付与する)

  7. 返ってきた検証結果に応じて、通知を行う

  8. 購入のトランザクションを完了させる

NotifierProviderで持つ状態と上記の順番に沿って説明を進めます。

全体のコードを確認するには、/skimie/lib/services/app_purchase/app_purchase_provider.dart

を参照してください。

Stateとしては、下記の状態を定義しました。

@freezed
class AppPurchaseState with _$AppPurchaseState {
  const AppPurchaseState._();

  const factory AppPurchaseState({
    // 課金利用の可否
    @Default(false) bool isAvailable,

    // バックエンドから取得した課金商品情報(productIdや購入品Image)
    Items? purchaseItems,

    // AppleやGoogleのプラットフォームから取得した課金商品の情報
    @Default([]) List<ProductDetails> products,

    // 課金処理の状態
    @Default(false) bool purchasePending,
  }) = _AppPurchaseState;

  ProductDetails? getProductDetail(Item item) {
    final productId = item.code;
    return products.firstWhereOrNull((e) => e.id == productId);
  }
}


purchasePending 以外は、全てAPIから取得した値を定義しています。

getProductDetailsのメソッドは、OpenAPIで定義されたItemの型の中にある

製品IDに該当するproductDetailsをリストの中から取得するメソッドです。

単回購入処理のときに使います。

1. アプリ内購入が利用可能な状態なのか確認する

    // アプリ内購入が利用できるかを確認
    final isAvailable = await _inAppPurchase.isAvailable();

    // アプリ内購入が利用できない場合
    if (!isAvailable) {
      Log.e('アプリ内課金が利用できません。');
      state = state.copyWith(
        isAvailable: isAvailable,
      );
      return;
    }


ストアにアクセスできない、デバイスがGoogleにログインしていないなど、

利用不可の場合は、この先の処理には進むことはないです。

2. 購入品の製品IDのSetを用意する。

    // 課金商品の情報を取得
    final purchaseItems = await _purchaseService.getProductIds();
    final productIds = purchaseItems.items.map((p0) => p0.code).toSet();


バックエンドから、購入品のItemsのデータを取得し、製品IDのSetを作成します。

3. それぞれのストアに問い合わせて製品情報を取得する

    // 課金処理に使用する商品一覧を取得
    final response = await _inAppPurchase
        .queryProductDetails({...productIds, 'android.test.purchased'});


このAPIは、1. で作成した製品IDに該当する購入品の情報を Google Play Console や App Store Connect から List<ProductDetails> という型で取得することができます。

ProductDetailsからは、購入品のid や title、description、price などの情報が取得できます。

4. 上記で取得した製品情報を使って購入処理を行う

この処理はUI側で行われる処理です。

    // アプリ内購入品のリスト
    List<Widget> purchaseItemList = [];
    final purchaseItems = ref.watch(appPurchaseServiceProvider
            .select((s) => s.purchaseItems?.items.toList())) ??
        [];
    final isAvailable =
        ref.watch(appPurchaseServiceProvider.select((s) => s.isAvailable));

    for (final item in purchaseItems) {
      final product =
          ref.watch(appPurchaseServiceProvider).getProductDetail(item);
      if (product == null) continue;

      purchaseItemList.add(
        ActionBtn(
          label: "",
          disabled: !isAvailable,
          onPressed: () async {
            await ref.read(appPurchaseServiceProvider.notifier).buyProduct(
                  context,
                  product: product,
                );
          },
          color: Colors.amber,
          width: (width - marginValue * 2 - 80) / 3,
          height: (width - marginValue * 2 - 80) / 3,
          image: ProductImage(
            item: item,
            displayCount: product.title.getNumInTarget,
          ),
        ),
      );
    }


appPurchaseServiceProviderのStateから、purchaseItemsを取得し、for in で回します。

各item に該当するProductDetailsを取得して、notifierのbuyProductに渡します。

AppPurchaseServiceクラス内の単回購入のメソッド、

  // 単回購入
  Future<void> buyProduct(
    BuildContext context, {
    required ProductDetails product,
  }) async {
    _context = context;
    state = state.copyWith(purchasePending: true);

    final purchaseParam = PurchaseParam(productDetails: product);
    await _inAppPurchase.buyConsumable(
      purchaseParam: purchaseParam,
      autoConsume: _kAutoConsume,
    );
  }


各プラットフォームのアプリ内購入が実行されます。

※ autoConsume について、デフォルトがtrueになっており、

消耗型の購入は、購入と同時にトランザクションが閉じられます。

https://techblog.booklista.co.jp/entry/2023/09/29/130633#%E8%B3%BC%E5%85%A5%E5%87%A6%E7%90%86%E3%81%AE%E6%9B%B4%E6%96%B0%E3%82%92listen%E3%81%99%E3%82%8B:~:text=%E3%81%99%E3%81%B9%E3%81%8D%E7%82%B9-,Android,-%E3%81%A7%E3%81%AF%E8%B3%BC%E5%85%A5%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0

5. 購入処理進行を監視して、購入が完了した後にバックエンドでレシートの検証を行う

  /// 購入の処理を監視する
  Future<void> listenPurchaseUpdated() async {
    final Stream<List<PurchaseDetails>> purchaseUpdated =
        _inAppPurchase.purchaseStream;
    _subs = purchaseUpdated.listen(
      (List<PurchaseDetails> purchaseDetailsList) {
        // 購入などが行われた場合の処理
        _listenToPurchaseUpdated(purchaseDetailsList);
      },
      onError: (Object error) {
        Log.e('Error: ${error.toString()}');
        errorNotification(msg: _l10n.errorPurchase);
      },
    );
  }


Streamにて、購入が行われるとpurchaseDetailsList が自動で返される処理になります。

返されたpurchaseDetailsListを_listenToPurchaseUpdatedに渡して、検証する流れになります。

  // 購入処理が行われた場合にそのトランザクションを処理する
  void _listenToPurchaseUpdated(
    List<PurchaseDetails> purchaseDetailsList,
  ) {
    purchaseDetailsList.forEach(
      (PurchaseDetails purchaseDetails) async {
        if (purchaseDetails.status == PurchaseStatus.pending) {
          state = state.copyWith(purchasePending: true);
        } else {
          // 購入処理がエラーの場合
          if (purchaseDetails.status == PurchaseStatus.error) {
            Log.e('Error: ${purchaseDetails.error}');
            errorNotification(msg: _l10n.errorPurchase);
          }

          // 購入がキャンセルされた場合
          if (purchaseDetails.status == PurchaseStatus.canceled) {
            successNotification(msg: _l10n.canceledPurchase);
          }

          // 購入処理が完了、または復元された場合
          if (purchaseDetails.status == PurchaseStatus.purchased ||
              purchaseDetails.status == PurchaseStatus.restored) {
            // レシートの検証をする
            final receipt =
                purchaseDetails.verificationData.localVerificationData;

            final valid = await _purchaseService.postPurchaseReceipt(
              receipt: base64Encode(utf8.encode(json.encode(receipt))),
              itemMasterCode: purchaseDetails.productID,
            );

            if (valid) {
              // レシートの検証が成功時の処理
              successNotification();

              // 購入完了の処理
              if (purchaseDetails.pendingCompletePurchase) {
                await _inAppPurchase.completePurchase(purchaseDetails);
              }
            } else {
              // レシートの検証が失敗時の処理
              errorNotification(msg: _l10n.errorPurchase);
            }

            state = state.copyWith(purchasePending: false);
          }
        }
      },
    );
  }


受け取った、purchaseDetailsListは、forEachで回され、個別の状態に応じて処理をする。

purchaseDetails.status == PurchaseStatus.purchased


購入済みの状態であれば、

  // レシートの検証をする
  final receipt = purchaseDetails.verificationData.localVerificationData;


レシートを取得することができるので、バックエンドに渡して、レシート検証をしてもらう。

6. 検証が成功した場合、ジェムをユーザーに付与する(課金の対価を付与する)

バックエンドの処理。

7. 返ってきた検証結果に応じて、通知を行う

レシート検証から返ってきた値(valid)によって、成功、失敗の通知を行う。

8. 購入のトランザクションを完了させる

  // 購入完了の処理
    if (purchaseDetails.pendingCompletePurchase) {
       await _inAppPurchase.completePurchase(purchaseDetails);
      }


トランザクションを完了していないpurchaseDetailsは、完了の処理を行う。

バックエンドの処理が失敗した時の対策


https://techblog.booklista.co.jp/entry/2023/09/29/130633

こちらの記事で紹介されている方法で実装。

  // 未完了のレシートを検出して再検証する
  Future<void> reCheckPendingTransaction() async {
    // 未完了のレシートを格納するリスト
    final List<PurchaseDetails> pendingReceiptList = [];

    // iOSの場合
    if (Platform.isIOS) {
      final paymentQueueWrapper = SKPaymentQueueWrapper();
      final transactions = await paymentQueueWrapper.transactions();
      for (final transaction in transactions) {
        // TODO:transactionから未完了のPurchaseDetailsを取得する
      }
    }

    // Androidの場合
    if (Platform.isAndroid) {
      final androidAddition = _inAppPurchase
          .getPlatformAddition<InAppPurchaseAndroidPlatformAddition>();
      // 過去の購入履歴を取得
      final response = await androidAddition.queryPastPurchases();
      for (final purchaseDetails in response.pastPurchases) {
        if (purchaseDetails.pendingCompletePurchase) {
          pendingReceiptList.add(purchaseDetails);
        }
      }
    }

    // 未完了の購入トランザクションがあったときにはレシートを再検証する
    if (pendingReceiptList.isNotEmpty) {
      pendingReceiptList.forEach(
        (PurchaseDetails purchaseDetails) async {
          // 購入処理が完了、または復元された場合
          if (purchaseDetails.status == PurchaseStatus.purchased ||
              purchaseDetails.status == PurchaseStatus.restored) {
            // レシートの検証をする
            final receipt =
                purchaseDetails.verificationData.localVerificationData;

            final valid = await _purchaseService.postPurchaseReceipt(
              receipt: base64Encode(utf8.encode(json.encode(receipt))),
              itemMasterCode: purchaseDetails.productID,
            );

            if (valid) {
              // 購入完了の処理
              if (purchaseDetails.pendingCompletePurchase) {
                await _inAppPurchase.completePurchase(purchaseDetails);
              }
            }
          }
        },
      );
    }
  }

リチェックの処理で参考になるレポジトリ:

https://github.com/erase2004/InstantCoffee/blob/9278973eb13f70e04653076a2cf651dc13b0cce5/lib/helpers/iap_subscription_helper.dart#L33

0

Game Up Your Life.

Skimieアプリをダウンロードして、タスク管理をゲーム化しよう

Skimieは、仕事や勉強にゲーミフィケーションを取り入れ、モチベーションアップをサポートする情報管理サービスです。

新着記事

複数の写真を1つにする方法が知りたい

Xで投稿する際に、やりたいと思ったので調べてみました。意外と簡単な方法が見つからなかったので、もう少し調べたり、周りに聞こうと思います。とりあえずは、Androidユーザーの私が一番簡単にできそうな方法をご紹介します。参考サイトhttps://support.google.com/photos/answer/12637115?hl=ja&co=GENIE.Platform%3DAndroidツール「Googleフォト」アプリ機能「コラージュ」という機能を利用します。手順は参考サイトに記載ありますが、とても簡単です。テンプレートは無料なのは4つほどです。私のスマホにあるラーメン画像だとこんな感じ。

イオンクレジットカードの付帯保険「ショッピングセーフティ保険」が気になる

最近、イオンクレジットカードの広告を見て、気になる内容があったのでピックアップします。それは、イオンクレジットカードの付帯保険で「ショッピングセーフティ保険」のサービスです。とてもお得なサービスなので、他のクレジットカードなどにも同様のサービスがないか調べたいです。また、スマホやパソコンなどWeb購入する機会もあるので、ぜひ利用したいと思います。▶公式サイトhttps://www.aeon.co.jp/service/safety/イオンクレジットカードの「ショッピングセーフティ保険」はどんなサービスこちらの記事の最後に公式サイトのスクショも添付します。約半年間になりますが、偶然による事故の補償するサービスです。【対象】イオン銀行が発行したクレジットカードでクレジット決済する5,000円以上の商品【対象範囲】偶然による事故破損事故火災事故盗難事故【保証期間】購入日から180日以内

複数のPDFファイルを結合したい

最近、複数のPDFの資料を確認する作業があり、PDFを結合するサービスを探していました。検索すると、Webサービスを利用してオンラインでやる方法とツールをインストールする方法の2つがあるようです。今回は、ツールをインストールしてみました。見つけたツールCubePDF Pagehttps://www.cube-soft.jp/cubepdfpage/評判以下のサイトを参考にしました。ざっと読んでみると評価は良さそうな感じでした。https://www.itreview.jp/products/cubepdf-page/reviews感想利用方法がとてもシンプルで、特にマニュアルことなく利用ができました。注意点こちらのソフトをインストールすると、一緒に「CubeWidet」というソフトもインストールされるようです。こちらは、最小化してニュースが表示するようになり気になったのでアンインストールしました。補足こちら、pdfファイルの他にpngファイルでも同様の事ができないか試してみたところ対応できました!

ウェブサイトのパスワードをスマートに管理したい

色々なWebサービスを利用していると、パスワードの管理は困りませんか?最近、Googleで「Google パスワード マネージャー」というサービスがあることを知りました。パスワードを考えたり、メモしたり、管理したり、思い出したりするストレスから解放されるので、とても便利なサービスなので、ぜひ活用しましょう。公式サイトhttps://support.google.com/chrome/answer/95606?hl=JA&ref_topic=7438325サービス概要Chromeを利用して様々なサイトのパスワードを保存することができますパスワードの保存方法Chromeにて、ウェブサイトで新しいパスワードを入力すると、Chromeでパスワードを保存するかダイアログが表示されますので、こちらで [保存] をクリックするだけです。保存したパスワードの利用方法パスワード保存したウェブサイトのログインフォームを表示すると、自動的に入力されます。便利な点パスワードを覚える必要がないGoogleアカウントに連携しているため、スマホ・パソコンのどちらでもパスワード管理ができる操作がとても簡単

利用したECTの料金をすぐに確認する方法

利用したETCの料金をすぐに確認する方法を知っていますか?ETCのカードにもよるかもしれませんが、私の持っているETCカードではすぐに利用料金を確認することができません。そこで検索してみたところ、こんなサービスがありました。ETC利用照会サービスhttps://www.etc-meisai.jp/【サービス内容】こちらはETCカードで利用した走行の利用明細をネット上で確認することができます。【対象】ETCクレジットカード、ETCパーソナルカード及びETCコーポレートカードを持っている方サービス登録に必要な情報ETCカード番号メールアドレス過去のご利用年月日車両番号車載器管理番号【注意点】私の場合、「車載器管理番号」という物に馴染みがありませんでした。こちら、車検証と一緒に「ETC車載器セットアップ証明書」という紙を保管していて、そちらに記載がありました。

東京都のQRコード決済で10%還元キャンペーンが3/23(土)で早期終了

東京都のQRコード決済で10%還元キャンペーンが3/23(土)で早期終了のニュースがありました。この記事を書いているのが「3/21」なので、今日を入れて残り3日になります。3/31(日)までの予定だったので、8日早い終了になります。公式サイトhttps://kurashisupport.metro.tokyo.lg.jp/私は「PayPay」「auPAY」「d払い」「楽天Pay」の中で、「PayPay」がほとんどでしたが、どんな利用割合だったかきになりますね。10%のポイント還元でしたけど、利用店舗が多かったので結構利用した感じはありました。このようなキャンペーンを通して、キャッシュレス化がまた加速しますね。

大阪のお土産って何があるのだろう?

3/23と3/24で大阪に行く予定です。そこで、大阪のお土産について、調べてみました。『りくろーおじさんの店』の「チーズケーキ」と『551』の「豚まん」は、マストですね!楽しみです。私の周りからのお勧めりくろーおじさんの店 焼きたてチーズケーキhttp://www.rikuro.co.jp/551の豚まんhttps://www.551horai.co.jp/カールチーズあじhttps://qa.meiji.co.jp/faq/show/4301?category_id=1&site_domain=default辻利抹茶ラスクhttps://www.otabe.jp/view/category/ct223Web検索1/2(じゃらん.net)https://www.jalan.net/news/article/93846/喜ばれる大阪府のお土産19選!地元民「人気ランキング」&編集部おすすめを紹介01位 豚まん【551蓬莱】02位 焼きたてチーズケーキ【りくろーおじさんの店】03位 堂島ロール【モンシェール】04位 じゃがりこ たこ焼き味ソースマヨ風味【カルビー】05位 GRAND Calbee【カルビー】06位 みるく饅頭 月化粧【青木松風庵】07位 みたらし小餅【千鳥屋宗家】08位 茜丸 五色どらやき【茜丸本舗】09位 本千鳥さぶれ【千鳥屋宗家】10位 ひとくち餃子【点天】編集部おすすめお土産情報岩おこし・粟おこし【あみだ池大黒】大阪花ラング【あみだ池大黒】ポテトチップス 関西だししょうゆ【カルビー】手焼・しょう油味たこ焼【たこ昌】ふる里の味 とん蝶【御菓子司 絹笠】ええもんちぃ【五感】本千鳥饅頭【千鳥屋宗家】お好み焼せんべえ【大阪の味本舗】肉桂餅【八百源来弘堂】Web検索1/2(JR東海ツアーズ)https://travel.jr-central.co.jp/plan/area/osaka/omiyage/絶対外さない!大阪の定番お土産6選1.生乳の香りとコクを包み込んだケーキ 「堂島ロール」2.大阪土産の定番中の定番 「551蓬莱 豚まん」3.30年愛される大阪産(もん) 「千鳥屋宗家 みたらし小餅」4.このかたさが癖になる 「あみだ池大黒 岩おこし」5.行列も納得の美味しさ 「りくろーおじさんの店 焼きたてチーズケーキ」6.黒豆入りのマドレーヌ 「ええもんちぃ」ワンランク上の見た目と味!大阪のおしゃれなお土産6選1.プレミアムなポテトチップス 「グランカルビー」2.パティシエが考えた新しい大阪土産 「瓢月堂 たこパティエ」3.お花のカタチのラングドシャ 「あみだ池大黒 大阪花ラング」4.健康や美容を気遣う方へ 「KAGOME GREENS Catch the Rainbow 食べるスムージー」5.日本で作ったフランス伝統のお菓子 「カヌレ堂CANELÉ du JAPON カヌレ」6.大阪発の新しいチーズスイーツ 「ウメダチーズラボ クッキー」

TポイントがVポイントに変わる?

Tポイントを持っている人が多いので、気になっている人は多いのではないですかね?ただ、実際どうなるのか調べていない人が大半なのではないでしょうか。そんな中の一人の私が、調べてみました。参考サイトhttps://web.tsite.jp/vpoint/質問形式いつから変更されるの?→2024年4月22日Tカードは使えなくなるの?→そのまま利用ができる何かしないといけないの?→必要なしVポイントに変わると何がある?→利用できる店舗が増える(すき屋、なか卯、LOTTELIAなど)変更点は?→「Tポイント」アプリをアップデートすると、「VポイントPay」アプリに変更される(予定)感想Tポイントは、今までポイントカードだったが、「VポイントPay」としてQRコード決済できるようになると利用の仕方が変わるかも。後、TポイントからVポイントへの手続きが一番気になるポイントだったが、カードはそのまま、アプリはアップデートする事で、そのまま利用できるのであれば、とてもスムーズで良いと思いました。気になる点としては、TポイントはPayPayなどのアプリで連携しているので、そこは再設定など必要になるかが少し気になりました。

私の近所にある複数のスーパーのメリット・デメリットを書き出してみた

2024年3月12日に書いています。最近、色々なスーパーに足を運ぶことが多いので足で稼いだ情報を整理しています。私の通っているスーパーなので、他の店舗とサービスが違うかもしれません。最初に現状の推しを言っておくと「マルエツ」です。理由は、セルフレジや、Scan&Goなど、スーパーで一番不満になる「待ち時間」について、新しいサービスを早く導入して緩和をしようとしているからです。ただ利用頻度で言うと、「サミット」、「ハナマサ」を多く利用しています。候補5つ①マルエツ②ピーコック③サミット④まいばすけっと⑤ハナマサ①マルエツ【メリット】セルフレジが充実Scan&Goが導入されていて、スマホで商品バーコードをスキャンしてすぐに決済ができるヨーグルトの種類が多く比較的安いTポイントが付く ※Scan&Goのポイントと併用は不可冷凍食品が充実しているパンが充実している【デメリット】2階建て ※生鮮食品などが2階にあり上らなくてはならない買い物カートが利用しにくい ※たくさんの買い物がしにくい②ピーコック【メリット】100円ショップが一緒にあるイオンペイが使える基本は1フロアで欲しい物が揃う ※文房具や日用品は地下時々、アナログの割引券を配っている ※分かりやすく行く動機になる冷凍食品が充実している【デメリット】イオンペイ以外のQRコード決済が使えないセルフレジがないイオン系列の安価な商品が置いていない③サミット【メリット】駐車場がある1フロアで全ての買い物ができる買い物カートを利用しやすい試食をやっていて、かつ気軽に食べやすい季節に合ったサービスを積極的に行っているヨーグルトの種類が多く比較的安いパンが充実している【デメリット】セルフレジがない独自の決済カードのチャージが現金でしかできない④まいばすけっと【メリット】良い意味でコンパクト ※ピンポイントの買い物は利用しやすいイオン系列の安価な商品を購入できるイオンペイが使えるセルフレジがある【デメリット】商品が少ないイオンペイ以外のQRコード決済が使えない⑤ハナマサ【メリット】肉、牛乳、卵、トマトなど、必需品で他に比べて安い商品が多いクレジットのタッチ決済がある冬は比較的高い確率で、みかんの箱売りをやっている【デメリット】店が狭い、階段を下りるのが大変商品カートが使えないセルフレジがない

令和6年4月から"プラスチック"ゴミの出し方が変わる(墨田区版)

墨田区のチラシで、ゴミ捨て方法の変更の案内がきていたので確認してみます。ゴミ捨てについては、市区町村ごとにルールが違います。今回は、墨田区について確認していきます。内容令和6年4月から"プラスチック"ゴミの出し方が変わる詳細素材が全てプラスチックでできている製品を「プラスチック資源」として出す【プラスチックの例】食品トレー"プラマーク"の付いた包装・容器プラスチック製品(概ね30cm以内)【"プラマーク"の付いた包装や容器】※安全でキレイなプラスチック100%素材容器・キャップ類カップ・パック類チューブ類トレー(皿型容器)類食料品や日用品の袋類発砲スチロールなど【プラスチック製品(概ね30cm以内)】バケツ、じょうろストロー、スプーン歯ブラシハンガーなど【プラスチックとして回収できない物】軽くすすいでも汚れが付着しているもの刃物類・発火の危険があるもの在宅医療で使用したものプラスチック以外のものが付着しているものリサイクル整備の故障になるものペットボトル【二重袋に注意!】回収したプラスチックは、全て袋を破いて中身を確認しているため、家庭から出す際は袋などには入れず、そのまま出して欲しいとのこと。確認した感想試みは理解できるが、"プラスチック"の見分け方が複雑に感じるプラスチック資源の分別方法(見分け方)は、対処まで記載されていてGood!個別にルールを決めるのではなく、国で統一できないのだろうか?チラシ(PDF)https://www.city.sumida.lg.jp/kurashi/gomi_recycle/oshirase/pura_start.files/honnkakutirasi.pdf

ナレッジ?ここって何をするんですか?

説明が無いので良く分かんないのです…。 ちなみに画像は私の落描きです適当に貼りました…。

サッカーなどで利用できる都立公園のスポーツ施設がWeb予約可能になったのを知っていますか?

団体でスポーツする際、場所に困っていませんか?私は中野区でサッカーチームに参加しているのですが、場所の確保にはとても苦労しています。区などが管理しているグラウンドは、区に在住か在勤のメンバーを集めての団体登録が必要になるため、チームの中心になっている地域以外の場所を借りるのは、とても難しい状況だと思います。そんな私のような人に朗報で、2024/3/1から都立公園のスポーツ施設がWebから予約可能になりました。都立公園のスポーツ施設は、個人で利用者登録ができ、予約することが可能です。※以前は、施設に行かないと利用者登録ができませんでした、、都立公園スポーツレクリエーション予約システムhttps://kouen.sports.metro.tokyo.lg.jp/web/場所は3つ①代々木公園②高井戸公園③府中の森公園私も3月になって、早速登録して予約をしました。ただ、とても便利になったので、ライバルは多そうです...。なるべく確立の高そうな場所、時間を狙って予約しようと思いますー

「お好み鯛焼き」って知ってますか?

昨日、「お好み鯛焼き」を食べました初めて食べたのだけど、メチャ旨い!また食べたいので、場所をチェック。錦糸町駅のそば、丸井の中にありました。こちらは本店ですね!https://www.0101.co.jp/054/restaurant-menu/omedetaiyaki.htmlつぶあん、カスタードも気になるところ、、

東京都で3/11(月)よりQRコード決済で10%還元のキャンペーンが開始

来週の月曜から、東京都でQRコード決済のポイント還元キャンペーンが始まるので要チェックですね。ポイントを書き出していきます。公式ページhttps://kurashisupport.metro.tokyo.lg.jp/概要キャンペーン期間中に、都内の対象店舗において、対象のQRコード決済を行うと、後日、決済額の最大10%(上限3,000円相当)のポイントを還元!詳細キャンペーン期間2024/3/11(月)~2024/3/31(日) ※早期終了の可能性あり内容対象店舗での決済で10%のポイントが還元されるポイントの付与は後日ポイント付与の上限は、3,000円相当のポイント対象のQRコード(4つ)①PayPay②楽天Pay③au Pay④d払い対象者対象のQRコード決済を行った利用者(都民でなくてもOK)対象店舗キャンペーン開始日から、各QRコード決済のアプリ・HPで確認できるとのこと※以下の対象外を見ると、飲食以外も対象になりそうです!対象外の店舗国、地方公共団体、公共法人が管理運営する施設金融商品取引業者の店舗銀行、信託会社、保険会社等保険医療機関及び保険薬局等風俗営業等の規制及び業務の適正化等に関する法律第2条に該当する施設その他、本事業の目的・趣旨から適切でないと都が判断するもの間違いそうな点対象のQRコード決済ごとに上限3,000円相当のポイントが付与される※4つのQRコード決済を全て利用すると最大12,000円相当のポイントが受け取れる。

GKの"6秒ルール"が変わる?サッカーのルール改善案が下部カテゴリで試験導入の記事

3/4のYahoo記事からピックアップしました。GKの“6秒ルール”は8秒に増加して厳格化、違反時の再開方法も変更へ…IFABが下部カテゴリでの試験導入を承認https://news.yahoo.co.jp/articles/b4bba588cfe02e7dbf5c9261ce2495ec101f1c5dGKの"6秒ルール"とは?GKは、ボールをキャッチしてから6秒以内でボールを手や腕からはなさなければならない変更内容秒数を6秒→8秒に増加する主審は残り5秒から片手を挙げてカウントダウンする再開を「間接FK」→「CK」or「スローイン」に変更する変更の理由ゴール前からの間接FKとする現在の規定が主審に計測の迷いを生じさせているため。感想現在、私は40歳以上(シニア)のカテゴリーでサッカーをやっています。都リーグを始め、区のリーグ、その他の独自リーグなどにも参加しているが、このGKの「6秒ルール」でファールになっているのは見たことがないです。勝つための時間稼ぎは多発するので、導入は進んでいくと思いました。主審のカウントダウンも分かりやすくて良いので、現状のルールでも取り入れて良さそう。秒数は確かにきっちり計測してないので、6秒だと短いのか8秒が適切なのかは気になるところです。

Widgetテストにおける認証系のテスト

Widgetテストでは、エミュレーターや実機を使えないので、Firebaseに問い合わせるユーザー認証の機能が使えません。なので、モックのFirebaseAuthやGoogleSignInのパッケージを使って認証系のテストをします。https://pub.dev/packages/firebase_auth_mockshttps://pub.dev/packages/google_sign_in_mocksテストの流れFirebaseAuthとGoogleSignInのインスタンスのProviderをoverrideして、それぞれのモックのインスタンスに置き換えるAuthenticator のメソッドがモック化されたので、そのメソッドを使ってテストをするpackages/skimie/test/presentation/pages/login_page/components/signup_buttons_test.dartGoogleログインのWidgetテストを解説します。まず始めに、setUpにて、mockUserやmockGoogleSignIn、mockFirebaseAuthのインスタンスを用意します。このときに、mockFirebaseAuthには、mockUserのインスタンスを渡しておきます。 setUp(() async { final MockUser mockUser = MockUser( initialDisplayName: 'skimie_test', ); mockGoogleSignIn = MockGoogleSignIn(); mockFirebaseAuth = MockFirebaseAuth(initCurrentUser: mockUser); await Firebase.initializeApp(); });Widgetテストを開始するときに、対象のProviderをoverrideしてモック化します。ここでは、Googleログインのテストをしているので、そのメソッドがあるGoogleAuthenticatorをモック化しています。 testWidgets('Googleログイン', (WidgetTester tester) async { await tester.pumpWidget( ProviderScope( overrides: [ googleAuthenticatorProvider.overrideWithValue( GoogleAuthenticator( auth: mockFirebaseAuth, googleSignIn: mockGoogleSignIn, ), ), ], child: const MaterialApp( home: Material( child: SignUpButtons(animating: false), ), ), ), );SignUpButtonsをタップしたときには、FirebaseAuthのsignInWithCredentialを使っているので、モッククラス内でこのメソッドを修正します。 @override Future<UserCredential> signInWithCredential( AuthCredential credential, ) async { // ログインしたプラットフォームを確認 Log.i('ログイン : ${credential.signInMethod}'); final userCredential = MockUserCredential(); // currentUserを更新 currentUser = userCredential.user; return userCredential; } setUp内でcurrentUserにMockUserをセットしていましたが、このメソッドを走らせると、別のユーザーにcurrentUserが置き換わる様に修正しました。メソッドが走ったことをユーザー名が変更されていることで確認します。 // signInWithGoogleが呼ばれたことを確認 expect(loggedInCurrentUser.displayName, 'test_login_success');参考:https://qiita.com/mogmet/items/ade07bd842495192922dhttps://github.com/atn832/firebase_auth_mocks/blob/master/test/firebase_auth_mocks_test.darthttps://riverpod.dev/ja/docs/cookbooks/testing#%E3%83%97%E3%83%AD%E3%83%90%E3%82%A4%E3%83%80%E3%81%AE%E6%8C%99%E5%8B%95%E3%82%92%E3%82%AA%E3%83%BC%E3%83%90%E3%83%BC%E3%83%A9%E3%82%A4%E3%83%89%E3%81%99%E3%82%8B