207 Tech Blog

207株式会社のテックブログです。

React Nativeのアプリ内課金でExpo InAppPurchasesを使う

こんにちは!207株式会社 ソフトウェアエンジニアの若月(@wktq)です。

React Nativeアプリ内課金の実装で、これまでreact-native-iapを利用していましたが、 ExpoのInAppPurchasesに移行しました。

最近になってExpoのInAppPurchasesに移行しているという話はよく聞きますが、 その理由と導入方法・注意点についてまとめました。

Expoのアプリ内課金はejectが必須

まず、Expoでアプリ内課金を実装する場合は、Managed Workflowからejectする必要があります。 Managed Workflowでのアプリ内課金機能は以前よりフォーラム内で最も要求されている機能ですが、 2021年8月現在、いまだに進行中パイプラインに乗らず次回のExpo SDKバージョンでも実装される気配はありません(たぶん)。 理由としては、課金機能の実装のために抜本的な変更が必要であるためだそうです。

アプリ内課金を実装したい場合はejectした上で、SDK41から利用できるexpo dev clientを利用することでexpoの開発者体験を維持することができます。

なぜExpo InAppPurchasesを選んだのか

React Nativeアプリで課金を実装するためには、以下の2つの選択肢があります。

  • react-native-iap
  • Expo InAppPurchases API

やっていることはどちらも同じく、iOSのStoreKitとGoogle Play Billingという各OSのストアのSDKのwrapです。(react-native-iapはAmazon AppStoreも一部対応) Expo InAppPurchasesの週間約1,500ダウンロードに対し、react-native-iapは16,000ダウンロードです(2021/8/6現在)が、いくつかのExpo InAppPurchasesを使うべき理由があります。

もともとreact-native-iapで実装を行っていましたが、イベントがうまく発火しないなどの問題があり、Expo InAppPurchasesへの移行を決めました。 以上の理由から、新規で導入を検討している方は、最初からExpoのInAppPurchasesを利用することをお勧めします。

大まかな課金のしくみ

f:id:wktq:20210813230534p:plain
react-native-iapのgithubから引用

  • こちらはreact-native-iapのドキュメントから引用していますが、Expo InAppPurchaseに置き換えても同様です。
  • 各ストアからの購入や更新は、都度取りに行くわけではなく、設置したリスナーによって通知されます。
  • ライブラリ側からは購入リクエストを送るだけで、購入ダイアログの表示やその中で起こるユーザーとのインタラクションは全てネイティブのSDK側でハンドリングされ、特定の結果のみが非同期的に返ってきます。
  • 購入処理後はにレシート検証や機能のアンロックが完了したら、購入の承認(消耗型の場合は消費)が必要です。Expo InAppPurchasesではfinishTransactionAsync() で処理します。iOSではダイアログの再表示を防ぎ、Androidではこれを行わないと返金されます。

Expo InAppPurchasesの導入(移行)

ライブラリのセットアップ

ejectを行い、bare workflowに移行している必要があります。

yarn add expo-in-app-purchases
npx pod-install // iOS向け

AndroidManifest.xmlに以下を追記します

<uses-permission android:name="android.permission.BILLING" />

課金アイテムの作成

AppStoreConnect および Google Play Consoleにて、課金アイテムを作成します。AppStoreの場合は課金のためのAppleの審査が必要になります。

詳細(英語):https://rossbulat.medium.com/react-native-subscriptions-with-in-app-purchases-setup-fdaf3863e07f

作成した課金アイテムのSKUを使用してストアから情報を取得していきます。

課金で使う用語・定義

  • consumable: 消耗品かどうか、falseなら定期購読
  • acknowledge: Androidにおける購読の承認。iOSではfinish
  • consume: Androidにおける消耗型アイテムの消費。iOSではfinish
  • purchaseToken: Androidのみ、購入検証用のトーク
  • transactionReceipt: iOSのみ、購入検証用のレシート
  • originalTransactionXXXXXX: iOSにおける最初の購読情報。途中で購読をキャンセルしても変わらない情報(receiptや日付など)

実装

ほぼ必ず使うであろうmethodsは以下の通りです。

  • connectAsync(): 必須。ストアに接続する(初期化)処理。
  • getProductsAsync([skus]): 課金アイテムを取得する
  • getPurchaseListener(cb(purchase)): 購入などの処理が走った際に発火するリスナー。起動時にも一度呼ばれる。
  • purchaseItemAsync(sku, currentSku?): アイテムの購入処理開始リクエスト。Androidのみ、すでに有効なサブスクリプションがある場合、currentSkuを指定する。
  • finishTransactionAsync(purchase, consume): 購入後の承認(iOSであればconsume、Androidであればacknowledge)

便利なmethods - getPurchaseHistoryAsync(): 購入履歴の取得 - getBillingResponseCodeAsync(): 最新の支払い成功可否を取得

サンプルコード

こちらは、Expo InAppPurchasesの処理をReact Contextで書いた例です。

gist.github.com

購入後の処理(サーバー側にレシート検証のリクエストを送ったり、アプリ内のreduxの状態を更新したり)は processNewPurchase() の中でOSごとに書きます。

このContextが読み込まれた時点で、のイベントリスナーが発行されます。 Context配下では、processingの状態とgetProducts(課金アイテムの取得)が利用できます。

おわりに

参考記事

  • 2021年、React Nativeアプリ内課金(購読)のウォークスルー / Ross Bulat氏 rossbulat.medium.com

  • Googleの課金の仕組みについて

developer.android.com developer.android.com

  • iOSの課金について

qiita.com

この記事では、React Nativeのアプリ課金でExpo InAppPurchasesを導入した話について紹介しました。 課金周りでは記事に書ききれないほど細かい設定があるため、上記参考記事の内容などを参照して実装することをお勧めします。

We're Hiring

207株式会社では、レガシーな物流業界の変革に挑む配達員向け効率化アプリ「TODOCUサポーター」を開発しています。 開発チームでは一緒に開発してくれるアプリエンジニア(React Native)やバックエンドエンジニアの仲間を大絶賛募集中です!

もし少しでもご興味がありましたら、以下のnotionをご覧ください!

www.notion.so

本サイトではGoogle Analyticsを利用しています。