Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
バックエンドのためのアプリ内課金入門 (サブスク編)
Search
Masaki Hara
January 30, 2025
Programming
1
160
バックエンドのためのアプリ内課金入門 (サブスク編)
App StoreやGoogle Playの機能を使ってサブスクを提供するにあたって、バックエンドエンジニアとして理解しておくとよいことをまとめました。
Masaki Hara
January 30, 2025
Tweet
Share
More Decks by Masaki Hara
See All by Masaki Hara
Arm移行タイムアタック
qnighy
1
460
Quine, Polyglot, 良いコード
qnighy
5
710
Prolog入門
qnighy
5
1.5k
Rubyのobject_id
qnighy
6
1.6k
Getting along with YAML comments with Psych
qnighy
2
2.3k
状態設計から「なんとなく」を無くそう
qnighy
85
28k
日付時刻A to Z
qnighy
1
620
Hands-on Native ESM @ JSConf JP 2022
qnighy
0
5.8k
computed_modelの紹介 / Introducing computed_model (2)
qnighy
0
620
Other Decks in Programming
See All in Programming
Scaling your build logic
antalmonori
1
130
DevinとCursorから学ぶAIエージェントメモリーの設計とMoatの考え方
itarutomy
1
450
Fibonacci Function Gallery - Part 2
philipschwarz
PRO
0
220
functionalなアプローチで動的要素を排除する
ryopeko
1
740
[Fin-JAWS 第38回 ~re:Invent 2024 金融re:Cap~]FaultInjectionServiceアップデート@pre:Invent2024
shintaro_fukatsu
0
310
Alba: Why, How and What's So Interesting
okuramasafumi
0
230
Linux && Docker 研修/Linux && Docker training
forrep
16
3k
ISUCON14感想戦で85万点まで頑張ってみた
ponyo877
1
770
SRE、開発、QAが協業して挑んだリリースプロセス改革@SRE Kaigi 2025
nealle
1
2.8k
知られざるDMMデータエンジニアの生態 〜かつてツチノコと呼ばれし者〜
takaha4k
3
940
自分ひとりから始められる生産性向上の取り組み #でぃーぷらすオオサカ
irof
8
2k
“あなた” の開発を支援する AI エージェント Bedrock Engineer / introducing-bedrock-engineer
gawa
9
1k
Featured
See All Featured
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
28
4.5k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
8
1.3k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Docker and Python
trallard
43
3.2k
Measuring & Analyzing Core Web Vitals
bluesmoon
5
210
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
VelocityConf: Rendering Performance Case Studies
addyosmani
327
24k
Being A Developer After 40
akosma
89
590k
How to Ace a Technical Interview
jacobian
276
23k
Why Our Code Smells
bkeepers
PRO
335
57k
Fontdeck: Realign not Redesign
paulrobertlloyd
82
5.3k
Testing 201, or: Great Expectations
jmmastey
41
7.2k
Transcript
© 2025 Wantedly, Inc. アプリ内課金入門 (サブスク編) バックエンドのための 2025-01-29 Masaki Hara
@ Tech Lunch
© 2025 Wantedly, Inc. 本発表の目的 • アプリ内課金は特殊なケースが色々ある ◦ 返金された場合、同じApple IDで別のユーザーに課金した場合、
etc… • アプリ内課金ではストアの特性を考慮する必要がある ◦ AppleとGoogleでは課金のモデリングが異なる ◦ 事業者ができること、ユーザーができることも異なる ◦ バックエンドで統合されるマルチプラットフォームのアプリでは、これらをどう統合するか考 える必要がある • これらの罠を知ってもらい、アプリ内課金を開発・保守する上 での参考にしてほしい
© 2025 Wantedly, Inc. アプリ内課金とは
© 2025 Wantedly, Inc. アプリ内課金とは • アプリストアの機能を使って、ユーザーにお金を支払わせる方 法のこと • 英語で
In-App Purchase (IAP) • アプリストアの重要な収入源、すなわち ◦ 手数料率はそれなりに高い、またはかなり高い ◦ ストア外での課金は禁止または制限されている
© 2025 Wantedly, Inc. アプリ内課金とは • OSではなく、アプリストア側の機能として提供される ◦ Android (AOSP)
系OSであっても、Google Play以外のストアを使うならば当該ストア の機能を使って開発することになる たとえばAmazon App Storeなど ◦ ここではApp StoreとGoogle Playについて取り扱う
© 2025 Wantedly, Inc. サブスクリプション
© 2025 Wantedly, Inc. 課金方式 課金報酬の3形態 • 消費可能アイテム ◦ 回復アイテム、ゲーム内通貨など。使うと無くなり、また買う必要がある
• 消費できないアイテム ◦ アイテムボックスを広げる特別なアイテムなど。一度買えば無くならない • サブスクリプション ◦ 広告なしプランなど。支払いをやめると機能は使えなくなる 形態により返金等の挙動が異なる
© 2025 Wantedly, Inc. サブスクリプションのモデル サブスクリプションは以下の3つの組み合わせと考える • 一定期間のサービスを受ける権利に対する購入 • 上記を継続する権利の購入
(複数) • 上記を自動継続するための設定
© 2025 Wantedly, Inc. サブスクリプションの状態 最低でも3状態を考慮する必要がある • アクティブ …… 自動継続が設定されている状態
• キャンセル …… 自動継続が解除されている ◦ ただしサービス自体は、満期が来るまで有効 • 無効 …… 購入の満期が来てサービスが無効になった 月極めの契約一般では、中途解約で即時サービスが無効になるものや、その際に残り期間に応じて返金され るものもあると考えられる。 しかし、App StoreとGoogle Playではどちらも満期まで利用が可能なモデルとなっている。
© 2025 Wantedly, Inc. サブスクリプションの状態 最低でも3状態を考慮する必要がある • アクティブ …… 自動継続が設定されている状態
• キャンセル …… 自動継続が解除されている ◦ ただしサービス自体は、満期が来るまで有効 • 無効 …… 購入の満期が来てサービスが無効になった 月極めの契約一般では、中途解約で即時サービスが無効になるものや、その際に残り期間に応じて返金され るものもあると考えられる。 しかし、App StoreとGoogle Playではどちらも満期まで利用が可能なモデルとなっている。 購入 #1 購入 #2 4月17日 5月17日 6月17日 7月17日 利用開始 自動継続 購入 #3 自動継続 キャンセル サービス 停止
© 2025 Wantedly, Inc. サブスクリプションの状態 加えて以下のようなケースにも注意が必要 • 支払い失敗時の挙動 ◦ 基本的には、継続失敗とみなしてサービスを停止する。
◦ しかし、設定した猶予期間 (grace period) の間サービスを提供し続けることも可能。 • 返金申請された場合 ◦ 返金時はサービス即時停止 • サービス障害等の補償 ◦ 事業者の判断でサービス提供期間を延長することが可能
© 2025 Wantedly, Inc. ストアとの連携
© 2025 Wantedly, Inc. クライアント or サーバー 課金の検証形態には2種類ある • クライアント側で検証を完結させる方法
◦ 特定のサーバー・サービスと紐付いていなければこちらのほうが話が早い ◦ たとえばサードパーティー製のSNSクライアントアプリが広告無しオプションを導入するな らこの方式 • サーバー側で検証し、クライアント側でそれを参照 ◦ 特定のサーバー・サービスと紐付いているならこちらのほうが堅牢で柔軟 ◦ たとえば対戦ゲームや動画視聴サービスであればこちらの方式が良いだろう
© 2025 Wantedly, Inc. クライアント or サーバー サーバーを含む情報の流れ • 端末→ストア
• ストア→サーバー ここさえ抑えておけば何とかな る (サーバーは基本は受信する だけ) 事業者サーバー Google, Apple 利用者端末 通知 課 金 要 求
© 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 基本的に、両方を組み合わせるのが良い
© 2025 Wantedly, Inc. ユーザー サービスを「誰に対して」付与するのか? • ストア側のユーザーIDに対して? ◦ App
Store なら Apple ID ◦ Google Play なら Googleアカウント • 自社サービスのユーザーIDに対して? ◦ Wantedly なら Wantedlyユーザー この2つは、複数端末利用時の挙動が異なる
© 2025 Wantedly, Inc. ユーザー 自社サービスのユーザーIDに紐付ける場合 • 購入にユーザーIDを紐付けることができる ◦ App
Store → appAccountToken ◦ Google Play → obfuscatedExternalAccountId • サーバー側でユーザーIDをデコードして紐付けを永続化
© 2025 Wantedly, Inc. ユーザー ストアユーザーと自社ユーザーは「多対多」の関係にある • 1つの自社ユーザーに、iPhoneとAndroidの両方からログイ ンしている場合はどうする? ◦
たとえば …… 片方でサブスクリプション契約していれば両方で有効 • 1つのGoogleアカウントで複数の自社ユーザーにログインし ている場合はどうする? ◦ たとえば …… 片方でサブスクリプション契約するともう一方では契約できない
© 2025 Wantedly, Inc. ストア別の仕様
© 2025 Wantedly, Inc. サブスクリプション階層 サブスクリプションの基本は2階層 • サブスクリプション契約 …… 連続した購入の列
• 1回分の購入
© 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があり、これで区別できる。
© 2025 Wantedly, Inc. 契約の承認 • 全ユーザーに一律に購入権を与えたくないこともある ◦ たとえばメールアドレス認証済みであることを要求するなど •
一般論として、サービスのサブスクであればバックエンドで商 品情報を制御するのが望ましい ◦ まずバックエンドがモバイルアプリに対して商品 IDの一覧を返す ◦ モバイルアプリは商品IDを使ってストアに商品情報を問い合わせる
© 2025 Wantedly, Inc. 契約の承認 それでも意図しない商品購入があった場合……? • App Store: 事業者からはどうしようもない
◦ ユーザーが自主的にキャンセルないし返金申請するのを待つしかない • Google Play: 「承認」しなければOK ◦ 「承認」は事業者側から専用のAPIを叩くことで購入を確定させる処理 ◦ Google Playでは、承認されなかった購入は自動的にキャンセルされる ◦ サーバーサイドで承認を行うアーキテクチャであれば、意図しない購入を承認しないこと によって実質的に拒否できる
© 2025 Wantedly, Inc. キャンセル 契約をキャンセルする場合 • App Store: Apple
IDを使ってキャンセル申請 ◦ 具体的にはApp Store側または、SDKを組み込んだアプリ内からキャンセルする ◦ 事業者側でキャンセルすることはできない • Google Play: 2つの方法がある ◦ ユーザーがGoogle Playからキャンセル ◦ 事業者がAPIを使ってキャンセル
© 2025 Wantedly, Inc. 返金 ユーザーが返金を申請した場合の挙動 • App Store: ユーザーはAppleに返金を申請する
◦ Appleは独自の基準で返金可否を判断する ◦ 事業者はAppleに参考情報を提供できるが、最終的な判断は Appleが行う • Google Play: 返金申請先は2つある ◦ Googleに返金申請を行うと、決められた基準により返金可否が決定される (契約してか らの時間で決まる) ◦ ユーザーが事業者に返金依頼を行い、事業者判断で返金を申請することも可能
© 2025 Wantedly, Inc. API • App StoreのAPIはJWTで認証を行う ◦ 特にApp
Store側が発行するJWTの検証は大変なので実装者は覚悟すること ▪ ルート証明書を取得して証明証チェインを検証し、さらに OCSPをサポートする必要がある ◦ AppleのSDKがある言語であれば使ったほうがいい • Google Playの通知はGoogle Cloud Pub/Subを利用 ◦ Pub/Sub側の設定でWebhookのようにも運用できる
© 2025 Wantedly, Inc. まとめ
© 2025 Wantedly, Inc. まとめ まとめ • サブスク = 購入
× n + 自動継続設定 • 端末 → アプリストア → 事業者サーバーの流れが重要 • App StoreとGoogle Playのモデリング差異や仕様差異に 気をつけよう