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
ISUCON14公式反省会LT: 社内ISUCONの話
astj
PRO
0
130
[Fin-JAWS 第38回 ~re:Invent 2024 金融re:Cap~]FaultInjectionServiceアップデート@pre:Invent2024
shintaro_fukatsu
0
310
PicoRubyと暮らす、シェアハウスハック
ryosk7
0
250
AWS re:Invent 2024個人的まとめ
satoshi256kbyte
0
140
AWS Lambda functions with C# 用の Dev Container Template を作ってみた件
mappie_kochi
0
200
Alba: Why, How and What's So Interesting
okuramasafumi
0
230
chibiccをCILに移植した結果 (NGK2025S版)
kekyo
PRO
0
180
Оптимизируем производительность блока Казначейство
lamodatech
0
980
Запуск 1С:УХ в крупном энтерпрайзе: мечта и реальность ПМа
lamodatech
0
970
watsonx.ai Dojo #6 継続的なAIアプリ開発と展開
oniak3ibm
PRO
0
250
Vue.jsでiOSアプリを作る方法
hal_spidernight
0
110
PHPUnitしか使ってこなかった 一般PHPerがPestに乗り換えた実録
mashirou1234
0
450
Featured
See All Featured
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
127
18k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
39
1.9k
Side Projects
sachag
452
42k
Optimizing for Happiness
mojombo
376
70k
RailsConf 2023
tenderlove
29
980
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
A Modern Web Designer's Workflow
chriscoyier
693
190k
The Straight Up "How To Draw Better" Workshop
denniskardys
232
140k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
49k
Unsuck your backbone
ammeep
669
57k
Into the Great Unknown - MozCon
thekraken
34
1.6k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
113
50k
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のモデリング差異や仕様差異に 気をつけよう