Slide 1

Slide 1 text

© 2025 Wantedly, Inc. アプリ内課金入門 (サブスク編) バックエンドのための 2025-01-29 Masaki Hara @ Tech Lunch

Slide 2

Slide 2 text

© 2025 Wantedly, Inc. 本発表の目的 ● アプリ内課金は特殊なケースが色々ある ○ 返金された場合、同じApple IDで別のユーザーに課金した場合、 etc… ● アプリ内課金ではストアの特性を考慮する必要がある ○ AppleとGoogleでは課金のモデリングが異なる ○ 事業者ができること、ユーザーができることも異なる ○ バックエンドで統合されるマルチプラットフォームのアプリでは、これらをどう統合するか考 える必要がある ● これらの罠を知ってもらい、アプリ内課金を開発・保守する上 での参考にしてほしい

Slide 3

Slide 3 text

© 2025 Wantedly, Inc. アプリ内課金とは

Slide 4

Slide 4 text

© 2025 Wantedly, Inc. アプリ内課金とは ● アプリストアの機能を使って、ユーザーにお金を支払わせる方 法のこと ● 英語で In-App Purchase (IAP) ● アプリストアの重要な収入源、すなわち ○ 手数料率はそれなりに高い、またはかなり高い ○ ストア外での課金は禁止または制限されている

Slide 5

Slide 5 text

© 2025 Wantedly, Inc. アプリ内課金とは ● OSではなく、アプリストア側の機能として提供される ○ Android (AOSP) 系OSであっても、Google Play以外のストアを使うならば当該ストア の機能を使って開発することになる たとえばAmazon App Storeなど ○ ここではApp StoreとGoogle Playについて取り扱う

Slide 6

Slide 6 text

© 2025 Wantedly, Inc. サブスクリプション

Slide 7

Slide 7 text

© 2025 Wantedly, Inc. 課金方式 課金報酬の3形態 ● 消費可能アイテム ○ 回復アイテム、ゲーム内通貨など。使うと無くなり、また買う必要がある ● 消費できないアイテム ○ アイテムボックスを広げる特別なアイテムなど。一度買えば無くならない ● サブスクリプション ○ 広告なしプランなど。支払いをやめると機能は使えなくなる 形態により返金等の挙動が異なる

Slide 8

Slide 8 text

© 2025 Wantedly, Inc. サブスクリプションのモデル サブスクリプションは以下の3つの組み合わせと考える ● 一定期間のサービスを受ける権利に対する購入 ● 上記を継続する権利の購入 (複数) ● 上記を自動継続するための設定

Slide 9

Slide 9 text

© 2025 Wantedly, Inc. サブスクリプションの状態 最低でも3状態を考慮する必要がある ● アクティブ …… 自動継続が設定されている状態 ● キャンセル …… 自動継続が解除されている ○ ただしサービス自体は、満期が来るまで有効 ● 無効 …… 購入の満期が来てサービスが無効になった 月極めの契約一般では、中途解約で即時サービスが無効になるものや、その際に残り期間に応じて返金され るものもあると考えられる。 しかし、App StoreとGoogle Playではどちらも満期まで利用が可能なモデルとなっている。

Slide 10

Slide 10 text

© 2025 Wantedly, Inc. サブスクリプションの状態 最低でも3状態を考慮する必要がある ● アクティブ …… 自動継続が設定されている状態 ● キャンセル …… 自動継続が解除されている ○ ただしサービス自体は、満期が来るまで有効 ● 無効 …… 購入の満期が来てサービスが無効になった 月極めの契約一般では、中途解約で即時サービスが無効になるものや、その際に残り期間に応じて返金され るものもあると考えられる。 しかし、App StoreとGoogle Playではどちらも満期まで利用が可能なモデルとなっている。 購入 #1 購入 #2 4月17日 5月17日 6月17日 7月17日 利用開始 自動継続 購入 #3 自動継続 キャンセル サービス 停止

Slide 11

Slide 11 text

© 2025 Wantedly, Inc. サブスクリプションの状態 加えて以下のようなケースにも注意が必要 ● 支払い失敗時の挙動 ○ 基本的には、継続失敗とみなしてサービスを停止する。 ○ しかし、設定した猶予期間 (grace period) の間サービスを提供し続けることも可能。 ● 返金申請された場合 ○ 返金時はサービス即時停止 ● サービス障害等の補償 ○ 事業者の判断でサービス提供期間を延長することが可能

Slide 12

Slide 12 text

© 2025 Wantedly, Inc. ストアとの連携

Slide 13

Slide 13 text

© 2025 Wantedly, Inc. クライアント or サーバー 課金の検証形態には2種類ある ● クライアント側で検証を完結させる方法 ○ 特定のサーバー・サービスと紐付いていなければこちらのほうが話が早い ○ たとえばサードパーティー製のSNSクライアントアプリが広告無しオプションを導入するな らこの方式 ● サーバー側で検証し、クライアント側でそれを参照 ○ 特定のサーバー・サービスと紐付いているならこちらのほうが堅牢で柔軟 ○ たとえば対戦ゲームや動画視聴サービスであればこちらの方式が良いだろう

Slide 14

Slide 14 text

© 2025 Wantedly, Inc. クライアント or サーバー サーバーを含む情報の流れ ● 端末→ストア ● ストア→サーバー ここさえ抑えておけば何とかな る (サーバーは基本は受信する だけ) 事業者サーバー Google, Apple 利用者端末 通知 課 金 要 求

Slide 15

Slide 15 text

© 2025 Wantedly, Inc. サーバー通信 事業者サーバーがストアから情報を得る方法は2つある ● Push型 ○ App Store: App Store Server Notification ○ Google Play: Real-Time Developer Notification ● Pull型 ○ App Store: App Store Server API ○ Google Play: Google Play Developer API 基本的に、両方を組み合わせるのが良い

Slide 16

Slide 16 text

© 2025 Wantedly, Inc. ユーザー サービスを「誰に対して」付与するのか? ● ストア側のユーザーIDに対して? ○ App Store なら Apple ID ○ Google Play なら Googleアカウント ● 自社サービスのユーザーIDに対して? ○ Wantedly なら Wantedlyユーザー この2つは、複数端末利用時の挙動が異なる

Slide 17

Slide 17 text

© 2025 Wantedly, Inc. ユーザー 自社サービスのユーザーIDに紐付ける場合 ● 購入にユーザーIDを紐付けることができる ○ App Store → appAccountToken ○ Google Play → obfuscatedExternalAccountId ● サーバー側でユーザーIDをデコードして紐付けを永続化

Slide 18

Slide 18 text

© 2025 Wantedly, Inc. ユーザー ストアユーザーと自社ユーザーは「多対多」の関係にある ● 1つの自社ユーザーに、iPhoneとAndroidの両方からログイ ンしている場合はどうする? ○ たとえば …… 片方でサブスクリプション契約していれば両方で有効 ● 1つのGoogleアカウントで複数の自社ユーザーにログインし ている場合はどうする? ○ たとえば …… 片方でサブスクリプション契約するともう一方では契約できない

Slide 19

Slide 19 text

© 2025 Wantedly, Inc. ストア別の仕様

Slide 20

Slide 20 text

© 2025 Wantedly, Inc. サブスクリプション階層 サブスクリプションの基本は2階層 ● サブスクリプション契約 …… 連続した購入の列 ● 1回分の購入

Slide 21

Slide 21 text

© 2025 Wantedly, Inc. サブスクリプション階層 App Store と Google Play のモデリング差異 階層 App Store Google Play (上位)※1 originalTransactionId - サブスクリプション (transactionReason = PURCHASE) SubscriptionPurchaseV2※2 1回分 Transaction (transactionId) Order※3 ※1 originalTransactionId は、同一Apple IDの同一商品の購入に対して 1つだけ発行される。そのため、一度解約して再度契約し ても同じIDが返ってくる。サブスクリプション単位を知るには transactionReason を参照する必要がある。 ※3 SubscriptionPurchaseV2のプライマリキーは (packageName, purchaseToken) ※3 SubscriptionPurchaseV2内にorderIdがあり、これで区別できる。

Slide 22

Slide 22 text

© 2025 Wantedly, Inc. 契約の承認 ● 全ユーザーに一律に購入権を与えたくないこともある ○ たとえばメールアドレス認証済みであることを要求するなど ● 一般論として、サービスのサブスクであればバックエンドで商 品情報を制御するのが望ましい ○ まずバックエンドがモバイルアプリに対して商品 IDの一覧を返す ○ モバイルアプリは商品IDを使ってストアに商品情報を問い合わせる

Slide 23

Slide 23 text

© 2025 Wantedly, Inc. 契約の承認 それでも意図しない商品購入があった場合……? ● App Store: 事業者からはどうしようもない ○ ユーザーが自主的にキャンセルないし返金申請するのを待つしかない ● Google Play: 「承認」しなければOK ○ 「承認」は事業者側から専用のAPIを叩くことで購入を確定させる処理 ○ Google Playでは、承認されなかった購入は自動的にキャンセルされる ○ サーバーサイドで承認を行うアーキテクチャであれば、意図しない購入を承認しないこと によって実質的に拒否できる

Slide 24

Slide 24 text

© 2025 Wantedly, Inc. キャンセル 契約をキャンセルする場合 ● App Store: Apple IDを使ってキャンセル申請 ○ 具体的にはApp Store側または、SDKを組み込んだアプリ内からキャンセルする ○ 事業者側でキャンセルすることはできない ● Google Play: 2つの方法がある ○ ユーザーがGoogle Playからキャンセル ○ 事業者がAPIを使ってキャンセル

Slide 25

Slide 25 text

© 2025 Wantedly, Inc. 返金 ユーザーが返金を申請した場合の挙動 ● App Store: ユーザーはAppleに返金を申請する ○ Appleは独自の基準で返金可否を判断する ○ 事業者はAppleに参考情報を提供できるが、最終的な判断は Appleが行う ● Google Play: 返金申請先は2つある ○ Googleに返金申請を行うと、決められた基準により返金可否が決定される (契約してか らの時間で決まる) ○ ユーザーが事業者に返金依頼を行い、事業者判断で返金を申請することも可能

Slide 26

Slide 26 text

© 2025 Wantedly, Inc. API ● App StoreのAPIはJWTで認証を行う ○ 特にApp Store側が発行するJWTの検証は大変なので実装者は覚悟すること ■ ルート証明書を取得して証明証チェインを検証し、さらに OCSPをサポートする必要がある ○ AppleのSDKがある言語であれば使ったほうがいい ● Google Playの通知はGoogle Cloud Pub/Subを利用 ○ Pub/Sub側の設定でWebhookのようにも運用できる

Slide 27

Slide 27 text

© 2025 Wantedly, Inc. まとめ

Slide 28

Slide 28 text

© 2025 Wantedly, Inc. まとめ まとめ ● サブスク = 購入 × n + 自動継続設定 ● 端末 → アプリストア → 事業者サーバーの流れが重要 ● App StoreとGoogle Playのモデリング差異や仕様差異に 気をつけよう