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
App Store/Google Play Store サブスクリプション連携の勘所
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
vilagia
January 29, 2026
Programming
26
0
Share
App Store/Google Play Store サブスクリプション連携の勘所
vilagia
January 29, 2026
Other Decks in Programming
See All in Programming
AI時代だからこそ「Bloc」を採用する価値があるのかもしれない
takuroabe
0
230
ローカルLLMでどこまでコードが書けるか / How much code can be written on a local LLM
kishida
2
410
Hive Metastoreを通して学ぶIceberg REST Catalog ― 仕様から実装まで
okumin
0
280
生成AI時代にこそ効くGo | Why Go Works in the Age of Generative AI
mom0tomo
1
500
Why Laravel apps break—Mastering the fundamentals to keep them maintainable
kentaroutakeda
1
180
開発体験を左右するライブラリの API 設計 - GraphQL スキーマ構築ライブラリから考える #tskaigi
izumin5210
2
520
Kubernetesを使わない環境にもCloud Nativeなデプロイを実現する / Enabling Cloud Native deployments without the complexity of Kubernetes
linyows
3
540
横断組織出身のQAEがインプロセスQAEでつまずいたこと・活かせたこと
ty89
0
180
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
110
AI駆動開発で崩れていくコードベースを立て直す
kyoko_nr_nr
1
360
Stage 3 Decorators でできること / できないこと / TSKaigi 2026
susisu
1
820
AI駆動開発勉強会 広島支部 第一回勉強会 AI駆動開発概要とワークショップ
hayatoshimiu
0
360
Featured
See All Featured
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
390
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
5.8k
Context Engineering - Making Every Token Count
addyosmani
9
900
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
200
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
1
3.6k
Site-Speed That Sticks
csswizardry
13
1.2k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.5k
WENDY [Excerpt]
tessaabrams
10
37k
How to Ace a Technical Interview
jacobian
281
24k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
55k
Building Adaptive Systems
keathley
44
3k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.7k
Transcript
App Store/Google Play Store サブスクリプション連携の勘所 〜リストア・割引施策における仕様理解のポイント〜 2025/12/09 YAPC::Fukuoka 非公式リジェクトコン 2025
株式会社はてな vilagia
自己紹介 • 名前: vilagia(う゛ぃらと呼んでください) • 所属: 株式会社はてな テクノロジーソリューション本部 第2グループ •
普段の仕事: GigaViewerの開発を担当するWebアプリケーションエンジニア • 趣味: 小説を書くこと
GigaViewerについて • 17社・26サービス(2025年12月時点)に採用いただいている出版社向けマンガ ビューワ • Webブラウザで閲覧するGigaViewer for Webと、iOS/Androidアプリで閲覧する GigaViewer for
Appsがある • サブスクリプション型課金機能あり
サブスクリプション型課金 • いわゆる「サブスク」。 • 一定期間(通常は一ヶ月)ごとにお金を頂いて、その代わりにサービス提供を行うタ イプの課金 => 今回はこのタイプの課金の注意点 & 向き合い方について話したい
今回伝えたいこと: 定期購読の開発時の注意点を紹介 1. リストアにまつわるもの 2. フルマネージドなロジックにまつわるもの
リストアにまつわる注意点たち • アプリストアでの購入物を、機種変更後などに再適用する機能 • App StoreもGoogle Play Storeも、アプリ内課金で購入したサブスクリプションの 「リストア」機能を要求している •
当然、GigaViewerもリストア機能を搭載 出典: https://developer.apple.com/jp/documentation/storekit/original_api_for_in-app_purchase/
こんなことは経験ありませんか • 機種を変更したけど、やっていたスマホゲームの引継コードを発行し忘れた • ゲーム開始時に作成される「ゲストアカウント」は端末ごとに発行されるから、失わ れてしまう • メールアドレスなどでアカウント登録をしていればこんなことには…… • このとき揃えたキャラなどは、「◦◦石」という「消耗型プロダクト」を使って得た権利な
ので、アプリストアが要求するリストア対象にはならない • しかし、「買い切り500円で広告を非表示にする」や、「毎月500円払う代わりに◦◦石 を受け取れる権利」などは、「非消耗型プロダクト」や「サブスクリプション」なので、 リストアが必要
リストアで権利が往復しうる
対応の考え方 • 権利は一つなので、増殖しないような工夫が必要 • 往復で済まない「渡り歩く」ケースも考えられる • アプリストアからやってくる購入履歴は絶対なので、最優先される • この「往復」が後から追えるようなデータ構造にしないと、ユーザーサポートで苦労 する可能性あり
◦ 具体的には、UPDATEで持ち主を移して終わり、という方法は避けたい ◦ ということはINSERTとなるが、うかつなユニーク制約を掛けていると ……
データモデルのミスマッチ • アプリストアのAPIは、「今の状態」にフォーカスした設計 • よくあるアプリのサブスクを想定すると、自然なこと
サブスクリプションには違う形もある • サブスクリプション中にコンテンツの権利が付与され、解約後もアクセス可能 ◦ 物理的な雑誌の定期購読に近い形 • GigaViewerでは両方のパターンをサポートしている • 今回問題になるのは後者のパターンで、チーム内では「定期購入型定期購読」と呼 んでいる
定期購入型定期購読とのギャップ • 「サブスクリプションが一時的に無効だった期間」内に配られたコンテンツを渡して はいけない • そのユーザーさんが過去に購入した履歴を全て把握できないと、正しい復元ができ ない
App Storeの場合 • 情報量は多め • そのユーザーさんの全購入履歴が取得できる • ただし、「サブスクリプションが一時的に無効だった期間」などはわからない
Google Playの場合 • 情報量は少なめ • 現在有効なサブスクリプションと、直近に有効だったサブスクリプションの購入履歴 が取得できる
対応の考え方 • 定期購入型定期購読に適したデータ構造でGigaViewer側が保存する • アプリストア側とのギャップが出たら、アプリストア側を正として補正する • ただでさえ複雑なライフサイクルを持つ各アプリストアのサブスクリプションを表現 するのはかなり大変なので、歯を食いしばる https://developer.android.com/google/play/billing/lifecycle/subscriptions?hl=ja
リストアにまつわる注意点 • 定期購読の権利はユーザーを渡り歩く ◦ 増殖したり消えてしまったりしないようにしよう ◦ 後から追跡できるようにしよう • アプリストアのデータモデルは、アプリの要件と必ずしも整合しない ◦
整合しなければ、サーバサイドで別途管理しよう ◦ アプリストアのサブスクリプションライフサイクルは複雑。マッピングは頑張ろう
フルマネージドなロジックとの付き合い • アプリストアには、フルマネージドなロジックを提供してくれる機能がある • 代表的なのは、割引施策などに関するもの • 便利なのだが、文化的バイアスなどもあり、思わぬ仕様が隠れていることも
我々のやりたかったこと • サブスクリプションの割引施策を行いたかった ◦ 例: 1ヶ月1000円のところ、初月だけ 500円にする • 2025/04/01 00:00(JST)〜2025/04/30
23:59(JST)までの申し込みが対象 ※ 金額や日付などは一例です
Google Play Storeは「デベロッパー指定」が可能 • サーバサイドのロジックに基づいて任意に割引を行える https://support.google.com/googleplay/android-developer/answer/12154973?hl=ja
お試しオファー • 新規加入者に割引を訴求するためのApp Storeにある機能 • 設定しておくと、指定期間内に申し込んだ新規加入ユーザーに対して自動で割り引 いてくれる
その他のオファー • オファーコードは適用フローの都合上、要件に合わず検討から外した • プロモーションオファーは「デベロッパー指定」に近いが、獲得の目的では使えない https://developer.apple.com/jp/app-store/subscriptions/#providing-subscription-offers
予想外のお試しオファー適用 • お試しオファーを適用するかどうかはApp Store側の制御下 • 適用期間の設定は日単位であることは事前に把握しており、ユーザーのタイム ゾーンに依存することもドキュメントで確認していた • 実挙動を事前確認すると、終了日前日(4/29)の23:59(JST)時点でお試しオファー が不適用になっていることに気付く。一体なぜ???
突然のサマータイム https://developer.apple.com/jp/help/app-store-connect/reference/pricing-and-availability/app-store-pricing-and-availability-start-times-by-country-or-region/
何が起こっていたのか • お試しオファーの適用期間は、アメリカにおいてサマータイム期間内だった • このとき、ユーザーのタイムゾーンにかかわらず、サマータイムに基づく価格反映 の前倒しが行われていた • また、お試しオファーの設定期間において、「2025/04/30まで」とは、2025/04/30 00:00:00まで、であった •
上記処理の結果、2025/04/30 23:59:59までのつもりで設定していた施策は、 2025/04/29 22:59:59までの施策になってしまっていた(※) ※ 事前の動作確認での知見なので、実際の設定値は異なる
• 事前の動作確認は確実に行う ◦ 当たり前ではあるが、現物を実際に動かして、その挙動を確かめることは重要 • 技術的な困難を迂回することも一案 ◦ 今回の場合、0時ちょうどに手動でオファーを無効化する運用回避を行った 対応の考え方
• ストアサーバーに実装は任せられても、動作確認は任せきれない ◦ 設定ミスは大いにありうる • グローバルなサービスを使っていることを自覚する ◦ 「日本での常識」は通用しない ◦ 仕様に文句を言うのは簡単だが、それより見落としを避ける動きを取りたい
フルマネージドなロジックにまつわる注意点
教訓 • 地道なことを大切に ◦ 動作確認、ドキュメント精査など • 関係者間の関係性構築とコミュニケーション ◦ 運用回避などでご協力頂くからには、日頃からの信頼関係が大切 ◦
それに甘えず、なぜ運用回避が必要なのか、などのコミュニケーションも丁寧に • 暗黙の前提を疑う ◦ 文化的バイアス ◦ 確認しづらいエッジケース