Tech Blog - mixross

react-native-iapを使ってアプリ内課金(非消耗型)

このエントリーをはてなブックマークに追加
LINE

環境

react-native 0.61.4
react-native-iap 4.3.0

iOS

App Store Connect

対象のアプリのページの「機能」 -> 「App内課金」に行き、課金アイテムを新規作成する。

AppStoreConnect1
AppStoreConnect2

「審査に関する情報」のスクリーンショットを追加しないとステータスが「メタデータが不足」となるので注意。
購入ページのスクショでいいが、ヘルプには「iOS では、少なくとも 640 x 920 ピクセルが必要です。」とあるので、横長のアプリの場合はスクショを90度回転させる必要がある。

AppStoreConnect3

ここまで登録すると課金アイテムのステータスが「送信準備完了」となる。

XCode

Capabilities の In-App Purchase をON

Xcode

Android

Play Console

対象のアプリを選択後、左側のサイドメニューの「ストアでの表示」 -> 「アプリ内サービス」 -> 「管理対象のアイテム」を選択。
ここで設定するIDはiOSのものと同様のアイテムであれば同じIDにしておくと後々ReactNative側の処理で両OS分のIDを持たなくていいので簡単になる。

React Native

react-native-iapをRNプロジェクトに追加

yarn add react-native-iap

実装サンプル

実際のアプリでは最低限以下の3つの機能を使ってリリースしました。

課金アイテムの情報を取得

課金アイテムの名称や金額をストア側とアプリ側で2重管理するのであれば、このAPIは使用する必要はなく、アプリ側で定数で持っておけばいいです。

import * as RNIap from 'react-native-iap'

async componentWillMount() {
  let products = []
  try {
    products = await RNIap.getProducts(['id-a', 'id-b', ・・・])
  } catch(err) {
    console.log(err)
  }
  console.log('products', products)
}

課金アイテムの購入情報を取得

import * as RNIap from 'react-native-iap'

async getPurchasedData() {
  let purchases = []
  try {
    purchases = await RNIap.getAvailablePurchases()

  } catch(err) {
    console.log(err)
  }
  return purchases
}

課金アイテムの購入

import { Platform } from 'react-native'
import * as RNIap from 'react-native-iap'

componentDidMount() {
  // 購入成功時のコールバック
  this.purchaseUpdateSubscription = RNIap.purchaseUpdatedListener(async (purchase) => {
    console.log('purchase success', purchase)
    const receipt = purchase.transactionReceipt
    if (receipt) {
      if (Platform.OS === 'ios') {
        await RNIap.finishTransactionIOS(purchase.transactionId)
      } else if (Platform.OS === 'android') {
        await RNIap.acknowledgePurchaseAndroid(purchase.purchaseToken)
      }
    }
  })
  // 購入失敗時のコールバック
  this.purchaseErrorSubscription = RNIap.purchaseErrorListener((err) => {
    console.log('purchase error', err)
  })
}

componentWillUnmount() {
  // コールバックの削除
  if (this.purchaseUpdateSubscription) {
    this.purchaseUpdateSubscription.remove()
    this.purchaseUpdateSubscription = null
  }
  if (this.purchaseErrorSubscription) {
    this.purchaseErrorSubscription.remove()
    this.purchaseErrorSubscription = null
  }
}

// 購入処理
// アイテム購入ボタンのクリック等で呼び出す
async purchaseItem(productId) {
  try {
    await RNIap.requestPurchase(productId, false);
  } catch (err) {
    console.log(err.code, err.message);
  }
}
RSS