firebase functions でアプリ内課金されたら Slack に通知が来るようにしてテンションを上げる

弊社で作っている feather for Twitter(以下 feather) には最近サポータープランという課金機能を追加しました。

課金されたときに Slack に通知が来たら楽しいなと思い実装してみました。

課金されると以下のような形で Slack に投稿されます。

f:id:mironal:20220111162809p:plain

構成と処理の流れ

feather には Google Analytics が組み込んでありますので課金アイテムが購入されたときには in_app_purchase のイベントが発生するのでそれをトリガにして firebase functions を実行します。

Slack に通知されるまでの処理の流れは以下のとおりです。

  1. feather (iOS App) で購入が行われる
  2. Google Analytics に in_app_purchase イベントが記録される
    • 特に何も実装しなくても自動的に記録されます(便利)
  3. firebase functions で in_app_purchase をトリガにしているコードが実行される
    • コードは後述
  4. Slack に通知される

firebase functions のコード

functions.analytics.event("in_app_purchase").onLog() を使用すると Analytics の購入イベントをトリガにした functions を作れます。

export const inAppPurchase = functions.analytics
  .event("in_app_purchase")
  .onLog(async (event) => {
    const { WebClient } = await import("@slack/web-api")
    const client = new WebClient(slackToken)
    // Slack に通知する処理
    return onInAppPurchase(event, client.chat)
  })

参考: https://firebase.google.com/docs/analytics/extend-with-functions

event の中身

onLog ので得られる event の中身はかなりざっくりとした型定義しか無いので実際に何が入ってくるかはよくわかりません。

実際に来たイベントを log してみると以下のようなものが得られます。

    • event.valueInUSD は USD ですが event.params["price"] を使うとローカライズされた金額(currencyが JPY なら円)が入ってきます。
    • event.params["product_name"] にはローカライズされた商品名が入ってきます

全体(値は適当に伏せています):

onInAppPurchase.event AnalyticsEvent = {
  params: {
    currency: 'JPY',
    engaged_session_event: 1,
    firebase_conversion: 1,
    firebase_event_origin: '',
    firebase_screen_class: '',
    firebase_screen_id: number,
    ga_dedupe_id: number,
    ga_session_id: number,
    ga_session_number: number,
    price: number,
    product_id: 'product_id',
    product_name: '商品名',
    quantity: number,
    session_duration: number,
    subscription: number,
    validated: number,
    value: number
  },
  name: 'in_app_purchase',
  valueInUSD: number,
  reportingDate: '20211129',
  logTime: '2021-11-29T01:33:00',
  user: UserDimensions {
    deviceInfo: {
      deviceCategory: 'mobile',
      deviceId: 'string',
      deviceModel: 'iPhone13,2',
      deviceTimeZoneOffsetSeconds: 0,
      limitedAdTracking: true,
      mobileBrandName: 'Apple',
      mobileMarketingName: 'iPhone',
      mobileModelName: 'iPhone',
      platformVersion: '15.1.1',
      userDefaultLanguage: 'ja-jp'
    },
    geoInfo: {
      city: 'HogeHoge City',
      continent: '000',
      country: 'Japan',
      region: 'Tokyo'
    },
    appInfo: {
      appId: 'appId',
      appInstanceId: '',
      appPlatform: 'IOS',
      appStore: 'iTunes',
      appVersion: '4.0.0'
    },
    firstOpenTime: 'date',
    userProperties: {
         // アプリで指定した userProperties
         // https://firebase.google.com/docs/analytics/user-properties?platform=ios
    },
    bundleInfo: ExportBundleInfo {
      bundleSequenceId: number,
      serverTimestampOffset: number
    }
  }
} 

"in_app_purchase" の注意点

in_app_purchase は本番環境のアプリ(AppStoreからのもの)の場合にのみイベントが発火します。 TestFlight などの SandBox 環境から課金された場合にはイベントは発生しません。

firebase analytics 的にはイベントが発生しているため Dashboard に値が反映されるのですが、 functions は発火しないので注意しましょう。

このため、先に述べた event の中身にどういったものが入っているのかはアプリをリリースして課金が発生するまでわかりませんでした。

Slack に投稿している内容

以下のような内容で Slack に post しています。 feather のサポータープランには高額のマハラジャという概念があるのでマハラジャの人が購入した際には見分けやすいように色を変えています。

  const options: ChatPostMessageArguments = {
    channel: featherNotifySlackChannel,
    as_user: true,
    text: isMaharaja ? "マハラジャが購入しました" : "購入されました👏",
    attachments: [
      {
        color: isMaharaja ? "good" : undefined,
        fields: [
          { title: "商品", value: product },
          { title: "価格", value: `${price} [${currency}]` },
          {
            title: "言語",
            value: userDefaultLanguage,
          },
          { title: "OS", value: osVer, short: true },
          { title: "App Ver", value: featherVer, short: true },
        ],
      },
    ],
  }

  await client.postMessage(options)

まとめ

iOS アプリでアプリ内課金が発生した際に Google Analytics と firebase functions を使ってSlack に投稿する方法について紹介しました。

この通知は大量に購入される商品に対して行うと Slack への投稿が大量になり大変なことになってしまうと思うので、適用できる範囲は限られると思いますが、それほど頻度が高くない場合にはテンションを上げる効果があると思うのでおすすめです。