Share
Embed
[DroidKaigi 2022] 詳解Google Playの新しい定期購入 ~オファーの活用や実装例を添えて~
DroidKaigi 2022で発表した「詳解Google Playの新しい定期購入 ~オファーの活用や実装例を添えて~」の発表資料です。
https://droidkaigi.jp/2022/timetable/363682
syarihu
October 05, 2022
Transcript
149 slides
-
Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行
Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 2 -
Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行
Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 3 -
従来の定期購入とその課題について 5 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間:
月別 • 価格: 500円 オファー • 無料期間: 30日 -
従来の定期購入とその課題について 6 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間:
月別 • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 -
従来の定期購入とその課題について 7 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間:
月別 • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 -
従来の定期購入とその課題について 8 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間:
月別 • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 期間限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日、お試し価格: 5,000円 -
従来の定期購入とその課題について 9 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間:
月別 • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 限定オファー • 請求対象期間: 年間 • 価格: 5,000円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額プレミアムプラン 初回限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日、お試し価格: 4,500円 -
従来の定期購入とその課題について 10 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間:
月別 • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 限定オファー • 請求対象期間: 年間 • 価格: 5,000円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額プレミアムプラン 初回限定 • 請求対象期間: 年間 • 価格: 5,000円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 学生限定 • 請求対象期間: 年間 • 価格: 4,000円 オファー • 無料期間: 30日 -
Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行
Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 12 -
19 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別
• 価格: 500円 オファー • 無料期間: 30日 従来の定期購入 -
20 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別
• 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 従来の定期購入 -
21 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別
• 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 従来の定期購入 -
22 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別
• 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 Subscription • 商品名: PROプラン 従来の定期購入 新しい定期購入に再構築した例 -
23 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別
• 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 Subscription • 商品名: PROプラン Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 従来の定期購入 新しい定期購入に再構築した例 -
24 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別
• 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 Subscription • 商品名: PROプラン オファー 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 オファー 提供の条件: 新規ユーザー 段階: • 無料試用 60日間 従来の定期購入 新しい定期購入に再構築した例 -
25 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間
• 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 期間限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額プレミアムプラン 初回限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 学生限定 • 請求対象期間: 年間 • 価格: 4,000円 オファー • 無料期間: 30日 従来の定期購入 -
26 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間
• 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 期間限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額プレミアムプラン 初回限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 学生限定 • 請求対象期間: 年間 • 価格: 4,000円 オファー • 無料期間: 30日 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 期間限定 提供の条件: デベロッパー指定 段階: • 1年間 500円 固定割引 オファー - 無料試用 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円 固定割引 従来の定期購入 新しい定期購入に再構築した例 Subscription • 商品名: PROプラン オファー - 学生限定 提供の条件: デベロッパー指定 段階: • 無料試用 30日間 • 3年間 1,500円 固定割引 -
27 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間
• 価格: 5,500円 オファー • 無料期間: 30日 Subscription • 商品名: PROプラン Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 無料試用 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 従来の定期購入 新しい定期購入に再構築した例 -
28 • Subscription (SKU) • 商品名: 年額PROプラン 期間限定 • 請求対象期間:
年間 • 価格: 5,500円 オファー • お試し価格: 5,000円 Subscription • 商品名: PROプラン Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 期間限定 提供の条件: デベロッパー指定 段階: • 1年間 500円 固定割引 従来の定期購入 新しい定期購入に再構築した例 -
29 • Subscription (SKU) • 商品名: 年額PROプラン 初回限定 • 請求対象期間:
年間 • 価格: 5,500円 オファー • 無料期間: 30日、お試し価格: 4,500円 Subscription • 商品名: PROプラン Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円 固定割引 従来の定期購入 新しい定期購入に再構築した例 -
30 • Subscription (SKU) • 商品名: 年額PROプラン 学生限定 • 請求対象期間:
年間 • 価格: 4,000円 オファー • 無料期間: 30日 Subscription • 商品名: PROプラン Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 従来の定期購入 新しい定期購入に再構築した例 定期購入を解約するまで 割引価格で利用できてしまう 定期的な支払いで割引価格が提供で きるようになったので、 今回は例として3年間固定割引にして いる オファー - 学生限定 提供の条件: デベロッパー指定 段階: • 無料試用 30日間 • 3年間 1,500円 固定割引 -
31 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間
• 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 期間限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額プレミアムプラン 初回限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 学生限定 • 請求対象期間: 年間 • 価格: 4,000円 オファー • 無料期間: 30日 Base plan - 通常 • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 期間限定 提供の条件: デベロッパー指定 段階: • 1年間 500円 固定割引 オファー - 無料試用 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円 固定割引 従来の定期購入 新しい定期購入に再構築した例 Subscription • 商品名: PROプラン オファー - 学生限定 提供の条件: デベロッパー指定 段階: • 無料試用 30日間 • 3年間 1,500円 固定割引 -
Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行
Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 32 -
新しい定期購入の作り方 1. 定期購入を作成する 2. 定期購入の下に基本プランを追加する ◦ 更新期間や地域別の価格などを設定する 3. 基本プランの下にオファー(特典)を追加する ◦
無料試用や特定の価格から割引きたい場合はオファーと して追加する 33 -
BenefitsとOffer • 定期購入の詳細で編集できる「特典」 とこれから説明する基本プランに追 加する「特典」は別物なので注意 ◦ 定期購入の詳細では、定期購入を 購入することにより得られるメリット を記載する •
定期購入の詳細の「特典」は英語で はBenefits、基本プランに追加する 「特典」はOfferとなっているため、本 発表では基本プランに追加する「特 典」はオファーとして扱う 34 -
1. 定期購入を作成する アイテムID • 定期購入を識別する一意なID • 実装ではProduct Id(旧SKU) 名前 •
ユーザーにも表示される定期購入 名 35 -
2. 基本プランを追加する 基本プランID • 定期購入ごとに一意なID 更新の種類 • 自動更新 or 前払い
• 種類ごとに、1つの定期購入に対し て同じ請求対象期間の基本プラン を複数作ることはできない ◦ 月額の基本プランを2つ追加 するなどはできない ◦ 無効にしても同じ期間の基本 プランは追加できないので注 意 36 -
2. 基本プランを追加する タグ • 実装時に基本プランやオファーを識別するため のタグ • アプリの実装では基本プランやオファーをタグ でしか判別できないため、必ず設定しておく •
基本プランやオファーごとに最大 20個設定でき る • 基本プランに設定したタグは、オファーのタグ にも継承される ◦ Play Console上からは見えないが、 アプリのAPIからオファーのタグを取 得するときに基本プランに設定したタ グも含まれている 37 -
3. オファーを追加する 特典ID • オファーを識別する一意な ID 基本プランと公開設定 • 基本プランは追加する対象の基本プラン が表示される
• 公開設定はオファーを提供する提供地域 を選択する(デフォルトでは基本プランと 同じ提供地域になる) タグ • アプリでオファーを識別するタグ • 用途は基本プランのタグと同じ 39 -
3. オファーを追加する 購入フェーズ(段階) タイプ • 1回限りのお支払い 1回だけ固定額または割引した価格を支払 い、指定した期間だけ利用できる たとえば1年間だけ特別に割引したい場合 は12ヶ月に設定すると、12ヶ月間はその価
格で利用できる その後、基本プランの価格での支払いが 始まる 47 -
3. オファーを追加する 購入フェーズ(段階) 1回限りのお支払い - 価格のオーバーライド • 固定額 ◦ ここで設定した価格に上書きする
◦ 通常価格が5,000円で、固定額が 4,000円なら4,000円になる • 割引率 ◦ 通常価格からの割引率( ~ %) ◦ 通常価格が5,000円で10%に設定し たら4,500円 • 割引額 ◦ 通常価格からの割引額( ~ 円 ) ◦ 通常価格が5,000円で500円に設定 したら4,500円 48 -
3. オファーを追加する 購入フェーズ(段階) タイプ • 定期的なお支払い 2 ~ 52回のいずれかの請求対象期間、固定 額または割引した価格を支払うことで利用で
きる たとえば年間払いで請求対象期間を 2回に すると、2回目の支払いまでは固定額または 割引した価格で利用できる その後、基本プランの価格での支払いが始 まる 49 -
3. オファーを追加する 50 購入フェーズ(段階) 価格のオーバーライド • 固定額 ◦ ここで設定した価格に上書きする •
割引率 ◦ 既存価格からの割引率( ~ %) ◦ 既存価格が5,000円で10%に設定し たら4,500円 • 割引額 ◦ 既存価格からの割引額( ~ 円 ) ◦ 既存価格が5,000円で500円に設定 したら4,500円 -
52 Subscription • 商品名: PROプラン Base plan - 初回限定 •
更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引 新しい定期購入の作り方 -
53 Subscription • 商品名: PROプラン Base plan - 初回限定 •
更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引 新しい定期購入の作り方 -
54 Subscription • 商品名: PROプラン Base plan - 初回限定 •
更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引 新しい定期購入の作り方 -
55 Subscription • 商品名: PROプラン Base plan - 初回限定 •
更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引 新しい定期購入の作り方 -
56 新しい定期購入の作り方 Subscription • 商品名: PROプラン Base plan - 初回限定
• 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引 -
57 新しい定期購入の作り方 Subscription • 商品名: PROプラン Base plan - 初回限定
• 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引 -
Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行
Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 59 -
61 自動移行された既存の定期購入 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間:
月別 • 価格: 500円 オファー • 無料期間: 30日 -
62 自動移行された既存の定期購入 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間:
月別 • 価格: 500円 オファー • 無料期間: 30日 月別のプランはp1mの基本プランとして追加されている(半年なら p6m、年間ならp1yになる) -
63 自動移行された既存の定期購入 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間:
月別 • 価格: 500円 オファー • 無料期間: 30日 無料期間は freetrial のオファーとして追加されている -
64 自動移行された既存の定期購入 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間:
月別 • 価格: 500円 オファー • 無料期間: 30日 自動移行されたプランは下位互換性のあるプランとしてマークされている 下位互換性のある基本プランは、 PBLv5で非推奨になっている querySkuDetailsAsync() メソッドに 返されるため、PBLv4以前の既存の実装でも問題なく購入できるようになっている -
65 Subscription • 商品名: PROプラン Offer 提供の条件: 新規ユーザー 段階: •
無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 Offer 提供の条件: 新規ユーザー 段階: • 無料試用 60日間 理想の定期購入の構成 - 月額 -
66 Subscription • 商品名: PROプラン Offer 提供の条件: 新規ユーザー 段階: •
無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 Offer 提供の条件: 新規ユーザー 段階: • 無料試用 60日間 実際に自動移行された定期購入の構成 - 月額 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 Subscription • 商品名: PROプラン 2ヶ月無料 -
67 理想の定期購入の構成 - 年額 • Subscription (SKU) • 商品名: 年額PROプラン
• 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 期間限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額プレミアムプラン 初回限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 学生限定 • 請求対象期間: 年間 • 価格: 4,000円 オファー • 無料期間: 30日 Base plan - 通常 • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 Offer - 期間限定 提供の条件: 新規ユーザー 段階: • 1年間 500円 固定割引 Offer - 無料試用 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 Offer - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円 固定割引 Subscription • 商品名: PROプラン Offer - 学生限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 3年間 1,500円 固定割引 -
68 実際に自動移行された定期購入の構成 - 年額 Base plan • 更新の種類: 自動更新 •
請求対象期間 : 年間 • 価格: 5,500円 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 Subscription • 商品名: 年額PROプラン 期間限定 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 Subscription • 商品名: 年額PROプラン Subscription • 商品名: 年額PROプラン 初回限定 Subscription • 商品名: 年額PROプラン 学生限定 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 4,000円 Offer - 期間限定 提供の条件: 新規ユーザー 段階: • 1年間 500円 固定割引 Offer - 無料試用 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 Offer - 無料試用 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 Offer - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円 固定割引 -
既存の定期購入を新しい 定期購入にしようとすると … 既存の定期購入に基本プランを増やす こともできるが、一つの定期購入商品 に対して一つの請求対象期間のみだっ たのが、 71 Base plan
• 請求対象期間 : 月間 Subscription 定期購入1 - 月額 Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年額 既存の定期購入の構成 -
既存の定期購入を新しい 定期購入にしようとすると … 新しく作成する一部の定期購入だけ一 つの定期購入に複数の請求対象期間 が含まれることになる 実装上も、定期購入に紐づく基本プラ ンをタグを元に探すというステップが必 要になり、既存の定期購入と実装方法 が異なるため、そういった点も考慮が必
要になる 72 Base plan • 請求対象期間 : 月間 Base plan • 請求対象期間 : 年間 Subscription 定期購入1 新しい定期購入の形 -
新しく作成する定期購入だけ新しい構成に則る 74 Base plan • 請求対象期間 : 月間 Subscription 定期購入1
- 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い 自動移行された既存の定期購入 -
新しく作成する定期購入だけ新しい構成に則る 75 Base plan • 請求対象期間 : 月間 Subscription 定期購入1
- 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い Base plan • 請求対象期間 : 月間 Subscription 新しい定期購入 Base plan • 請求対象期間 : 年間 自動移行された既存の定期購入 新しい定期購入だけ定期購入の下に 複数の期間を追加する -
新しく作成する定期購入だけ新しい構成に則る 76 Base plan • 請求対象期間 : 月間 Subscription 定期購入1
- 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い Base plan • 請求対象期間 : 月間 Subscription 新しい定期購入 Base plan • 請求対象期間 : 年間 新しい定期購入だけ定期購入の下に 複数の期間を追加する 自動移行された既存の定期購入 • 自動移行された既存の定期購入は定期購入と 期間がイコールなのでシンプルだったが、一つ の定期購入に複数の基本プランが紐づく • 特定の期間(基本プラン)を購入しようとしたと き、定期購入に紐づく基本プランを探さなけれ ばならないので、既存と新しい定期購入で異な る実装をしなければならないため、実装が少し 複雑になる • 一部だけ違う構成になるので、構成が統一さ れておらず管理上も少しややこしくなる -
新しく作成する定期購入だけ新しい構成に則る 77 Base plan • 請求対象期間 : 月間 Subscription 定期購入1
- 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い Base plan • 請求対象期間 : 半年 自動移行された既存の定期購入 • 既存の定期購入は定期購入と期間が 同じのため、商品名を変えないとおかし くなってしまう • 既存の定期購入に別の期間を追加す ることはできない -
新しく作成する定期購入だけ新しい構成に則る 78 Base plan • 請求対象期間 : 月間 Subscription 定期購入1
- 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い Subscription 定期購入1 - 半年払い Base plan • 請求対象期間 : 半年 1つの定期購入に対して 1つの期間のみが 紐づくように作成する 自動移行された既存の定期購入 -
新しい定期購入の構成に則るのを諦める 79 Base plan • 請求対象期間 : 月間 Subscription 定期購入1
- 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い 自動移行された既存の定期購入 Subscription 定期購入2 - 月払い Base plan • 請求対象期間 : 月間 Subscription 定期購入2 - 年払い Base plan • 請求対象期間 : 年間 いままでどおり、一つの定期購入に対して一つの 期間(基本プラン)のみを追加する 商品の扱いが変わらないのでオファー以外の実装 は既存の実装がそのまま使える(はず) -
全部新しい定期購入の構成に則るように頑張る ※これをやるのはおすすめしません 80 Base plan • 請求対象期間 : 月間 Subscription
定期購入1 - 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い 自動移行された既存の定期購入 Base plan • 請求対象期間 : 月間 Subscription 新しい定期購入1 Base plan • 請求対象期間 : 年間 案1. 新しい定期購入を作る 新規ユーザーはこちらを購入させ、 既存ユーザーはそのままにするか、切り替えを促す(なぜ 移行しないといけないのか、ユーザーへの説明が難しい) -
全部新しい定期購入の構成に則るように頑張る ※これをやるのはおすすめしません 81 Base plan • 請求対象期間 : 月間 Subscription
定期購入1 - 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い 自動移行された既存の定期購入 案2. 既存の定期購入に基本プランを増やす 新規ユーザーは新しい基本プランを購入させ、 既存ユーザーはそのままにするか、別の定期購入を購入 しているユーザーには移行を促す 既存の定期購入の商品名も変える必要がある Base plan • 請求対象期間 : 月間 -
Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行
Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 83 -
85 Subscription • 商品名: 月額PROプラン Offer 提供の条件: 新規ユーザー 段階: •
無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 既存の定期購入における新しいオファーの活用方法 通常の無料試用 -
86 Subscription • 商品名: 月額PROプラン Offer 提供の条件: 新規ユーザー 段階: •
無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 既存の定期購入における新しいオファーの活用方法 Offer 提供の条件: デベロッパー指定 段階: • 1ヶ月 20%割引 通常の無料試用 季節限定セール -
87 Subscription • 商品名: 月額PROプラン Offer 提供の条件: 新規ユーザー 段階: •
無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 既存の定期購入における新しいオファーの活用方法 Offer 提供の条件: デベロッパー指定 段階: • 1ヶ月 20%割引 通常の無料試用 季節限定セール Offer 提供の条件: 新規ユーザー 段階: • 3ヶ月 価格を300円に固定 期間限定 新規ユーザー向けセール -
88 Subscription • 商品名: 月額PROプラン Offer 提供の条件: 新規ユーザー 段階: •
無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 既存の定期購入における新しいオファーの活用方法 Offer 提供の条件: デベロッパー指定 段階: • 1ヶ月 20%割引 通常の無料試用 季節限定セール Offer 提供の条件: デベロッパー指定 段階: • 1ヶ月 100円割引 Offer 提供の条件: 新規ユーザー 段階: • 3ヶ月 価格を300円に固定 期間限定 新規ユーザー向けセール 過去に購読してくれていた ユーザーに対しての おかえりなさいオファー -
Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行
Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 91 -
Play Billing Library 5.0への移行 92 • 新しい定期購入の仕組みの再構築に合わせて、Play Billing Library 5.0(PBLv5)もリリースされた
• 新しい定期購入の仕組みや新しいオファーを利用するには PBLv5へのアップデートが必須になる dependencies { def billingVersion = "5.0.0" implementation "com.android.billingclient:billing:$billingVersion" } -
2020年 2021年 2022年 2023年 2024年 PBL 3.0 PBL 3.0 リリース
PBL 4.0 リリース PBL 5.0 リリース PBL 6.0 リリース PBL 7.0 リリース 93 PBL 4.0 PBL 5.0 -
Play Billing Library 5.0への移行 94 • PBL 3.xのサポートは2022年11月に終了する • 2022年11月以降、PBLを利用しているアプリがアプリの更新
を公開するにはPBL 4.x以上にアップグレードする必要がある • PBL 5.0は、新しい定期購入の機能へ順次対応できるように、 古いメソッドは非推奨の形で残っている -
Play Billing Library 5.0への移行 95 • 4.xへのアップグレードと5.xへのアップグレードの移行コスト はほとんど変わらないため、まだ 3.xを利用しているアプリは 一旦5.0にアップグレードするのがおすすめ
• すでに4.xを利用しているアプリも、一旦5.0に上げておき、 5.0から追加された機能や非推奨のメソッドは画面ごとに置き 換えるなど、順次対応していくのがおすすめ -
Agenda 97 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library
5.0への移行 Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 -
Play Billing Library 5.0の変更サマリ 98 • SkuDetailsに置き換わる、ProductDetailsが新たに追加された ◦ ProductDetailsには、すべての基本プランとそれに紐づくすべ てのオファー情報が含まれる
• Sku関連のメソッドなどの命名がProductになったものが追加され た • それに伴い、Sku関連のクラスやメソッドなどはすべて非推奨になっ た -
Play Billing Library 5.0の変更サマリ 99 • SkuDetailsによる購入フローでは、skuは商品自体に定期購 入の期間も含まれていたため、SkuDetailsを渡せば購入でき ていた •
ProductDetailsによる新しい定期購入の購入フローでは、 ProductDetailsの中に複数の基本プラン(定期購入の期間) やオファーが含まれるため、基本プランやオファーを選択する 実装が必要になった -
100 sku( pro_monthly )を元にSkuDetailsを取得 SkuDetailsを使って購入をリクエストする skuがpro_monthlyのSkuDetails • これまでは商品自体に購入期間も含まれていたため、 SkuDetailsだけで購入を開始できた Play
Billing Library 4.xまでのSkuDetailsを使った購入までの処理の流れ -
101 sku( pro_monthly )を元にSkuDetailsを取得 SkuDetailsを使って購入をリクエストする skuがpro_monthlyのSkuDetails • これまでは商品自体に購入期間も含まれていたため、 SkuDetailsだけで購入を開始できた Play
Billing Library 4.xまでのSkuDetailsを使った購入までの処理の流れ -
102 sku( pro_monthly )を元にSkuDetailsを取得 SkuDetailsを使って購入をリクエストする skuがpro_monthlyのSkuDetails • これまでは商品自体に購入期間も含まれていたため、 SkuDetailsだけで購入を開始できた Play
Billing Library 4.xまでのSkuDetailsを使った購入までの処理の流れ -
103 sku( pro_monthly )を元にSkuDetailsを取得 SkuDetailsを使って購入をリクエストする skuがpro_monthlyのSkuDetails • これまでは商品自体に購入期間も含まれていたため、 SkuDetailsだけで購入を開始できた Play
Billing Library 4.xまでのSkuDetailsを使った購入までの処理の流れ -
104 offerTag = p1m offerTag = free-trial offerTag = free-trial-60days
Play Billing Library 5.0からのProductDetailsを使った購入までの処理の流れ -
105 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id(
pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails • ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト • 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails • 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からのProductDetailsを使った購入までの処理の流れ -
106 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id(
pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails • ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト • 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails • 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からのProductDetailsを使った購入までの処理の流れ -
107 val product = QueryProductDetailsParams.Product .newBuilder() .setProductId("pro_plan") .setProductType(BillingClient.ProductType.SUBS) .build() val
queryProductDetailsParams = QueryProductDetailsParams .newBuilder() .setProductList(listOf(product)) .build() ProductDetailsを取得するためのパラメータを生成する -
108 val product = QueryProductDetailsParams.Product .newBuilder() .setProductId("pro_plan") .setProductType(BillingClient.ProductType.SUBS) .build() val
queryProductDetailsParams = QueryProductDetailsParams .newBuilder() .setProductList(listOf(product)) .build() ProductDetailsを取得するためのパラメータを生成する -
109 val product = QueryProductDetailsParams.Product .newBuilder() .setProductId("pro_plan") .setProductType(BillingClient.ProductType.SUBS) .build() val
queryProductDetailsParams = QueryProductDetailsParams .newBuilder() .setProductList(listOf(product)) .build() ProductDetailsを取得するためのパラメータを生成する -
110 val product = QueryProductDetailsParams.Product .newBuilder() .setProductId("pro_plan") .setProductType(BillingClient.ProductType.SUBS) .build() val
queryProductDetailsParams = QueryProductDetailsParams .newBuilder() .setProductList(listOf(product)) .build() ProductDetailsを取得するためのパラメータを生成する -
111 ProductDetailsを取得する billingClient.queryProductDetailsAsync( queryProductDetailsParams ) { billingResult: BillingResult, productDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && productDetailsList.isNotEmpty() ) { onResponse(productDetailsList) } else { // エラーハンドリング } } -
112 ProductDetailsを取得する billingClient.queryProductDetailsAsync( queryProductDetailsParams ) { billingResult: BillingResult, productDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && productDetailsList.isNotEmpty() ) { onResponse(productDetailsList) } else { // エラーハンドリング } } -
113 ProductDetailsを取得する billingClient.queryProductDetailsAsync( queryProductDetailsParams ) { billingResult: BillingResult, productDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && productDetailsList.isNotEmpty() ) { onResponse(productDetailsList) } else { // エラーハンドリング } } -
114 ProductDetailsを取得する billingClient.queryProductDetailsAsync( queryProductDetailsParams ) { billingResult: BillingResult, productDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && productDetailsList.isNotEmpty() ) { onResponse(productDetailsList) } else { // エラーハンドリング } } -
115 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id(
pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails • ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト • 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails • 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ -
116 基本プランとそれに紐づくオファーを探す // 基本プラン(オファー無し)と基本プランに紐づくオファーが取得できる val p1mOfferDetailsList = subscriptionOfferDetailsList .filter {
subscriptionOfferDetails -> // 基本プランに設定したタグを探す // 基本プランに紐づくオファーには基本プランに設定したタグが継承されるため、 // タグが一意であれば基本プランとそれに紐づくオファーが取得できる subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } } -
117 基本プランとそれに紐づくオファーを探す // 基本プラン(オファー無し)と基本プランに紐づくオファーが取得できる val p1mOfferDetailsList = subscriptionOfferDetailsList .filter {
subscriptionOfferDetails -> // 基本プランに設定したタグを探す // 基本プランに紐づくオファーには基本プランに設定したタグが継承されるため、 // タグが一意であれば基本プランとそれに紐づくオファーが取得できる subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } } -
118 基本プランとそれに紐づくオファーを探す // 基本プラン(オファー無し)と基本プランに紐づくオファーが取得できる val p1mOfferDetailsList = subscriptionOfferDetailsList .filter {
subscriptionOfferDetails -> // 基本プランに設定したタグを探す // 基本プランに紐づくオファーには基本プランに設定したタグが継承されるため、 // タグが一意であれば基本プランとそれに紐づくオファーが取得できる subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } } -
無料期間30日のオファーのSubscriptionOfferDetails val offerDetails = p1mOfferDetailsList[0] offerDetails.offerTags = p1m, free-trial ━━━━━━━━━━━━━━━━━━━━
offerDetails.pricingPhases.pricingPhaseList [0] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=1 pricingPhase.billingPeriod=P4W2D pricingPhase.formattedPrice=無料 pricingPhase.priceAmountMicros=0 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=FINITE_RECURRING ━━━━━━━━━━━━━━━━━━━━ offerDetails.pricingPhases.pricingPhaseList [1] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=0 pricingPhase.billingPeriod=P1M pricingPhase.formattedPrice=¥500 pricingPhase.priceAmountMicros=500000000 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=INFINITE_RECURRING ━━━━━━━━━━━━━━━━━━━━ 119 基本プランとそれに紐づくオファーを探す 無料期間60日のオファーのSubscriptionOfferDetails val offerDetails = p1mOfferDetailsList[1] offerDetails.offerTags = p1m, free-trial-60days ━━━━━━━━━━━━━━━━━━━━ offerDetails.pricingPhases.pricingPhaseList [0] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=1 pricingPhase.billingPeriod=P8W4D pricingPhase.formattedPrice=無料 pricingPhase.priceAmountMicros=0 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=FINITE_RECURRING ━━━━━━━━━━━━━━━━━━━━ offerDetails.pricingPhases.pricingPhaseList [1] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=0 pricingPhase.billingPeriod=P1M pricingPhase.formattedPrice=¥500 pricingPhase.priceAmountMicros=500000000 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=INFINITE_RECURRING ━━━━━━━━━━━━━━━━━━━━ -
無料期間30日のオファーのSubscriptionOfferDetails val offerDetails = p1mOfferDetailsList[0] offerDetails.offerTags = p1m, free-trial ━━━━━━━━━━━━━━━━━━━━
offerDetails.pricingPhases.pricingPhaseList [0] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=1 pricingPhase.billingPeriod=P4W2D pricingPhase.formattedPrice=無料 pricingPhase.priceAmountMicros=0 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=FINITE_RECURRING ━━━━━━━━━━━━━━━━━━━━ offerDetails.pricingPhases.pricingPhaseList [1] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=0 pricingPhase.billingPeriod=P1M pricingPhase.formattedPrice=¥500 pricingPhase.priceAmountMicros=500000000 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=INFINITE_RECURRING ━━━━━━━━━━━━━━━━━━━━ 120 基本プランとそれに紐づくオファーを探す 無料期間60日のオファーのSubscriptionOfferDetails val offerDetails = p1mOfferDetailsList[1] offerDetails.offerTags = p1m, free-trial-60days ━━━━━━━━━━━━━━━━━━━━ offerDetails.pricingPhases.pricingPhaseList [0] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=1 pricingPhase.billingPeriod=P8W4D pricingPhase.formattedPrice=無料 pricingPhase.priceAmountMicros=0 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=FINITE_RECURRING ━━━━━━━━━━━━━━━━━━━━ offerDetails.pricingPhases.pricingPhaseList [1] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=0 pricingPhase.billingPeriod=P1M pricingPhase.formattedPrice=¥500 pricingPhase.priceAmountMicros=500000000 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=INFINITE_RECURRING ━━━━━━━━━━━━━━━━━━━━ -
オファー無し(基本プラン) のSubscriptionOfferDetails val offerDetails = p1mOfferDetailsList[2] offerDetails.offerTags = p1m ━━━━━━━━━━━━━━━━━━━━
offerDetails.pricingPhases.pricingPhaseList [0] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=0 pricingPhase.billingPeriod=P1M pricingPhase.formattedPrice=¥500 pricingPhase.priceAmountMicros=500000000 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=INFINITE_RECURRING 121 基本プランとそれに紐づくオファーを探す -
オファー無し(基本プラン) のSubscriptionOfferDetails val offerDetails = p1mOfferDetailsList[2] offerDetails.offerTags = p1m ━━━━━━━━━━━━━━━━━━━━
offerDetails.pricingPhases.pricingPhaseList [0] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=0 pricingPhase.billingPeriod=P1M pricingPhase.formattedPrice=¥500 pricingPhase.priceAmountMicros=500000000 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=INFINITE_RECURRING 122 基本プランとそれに紐づくオファーを探す -
123 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id(
pro )を元にProductDetailsを取得 Product IdがproのProductDetails • ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト • 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails • 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ -
オファーの選択ロジックを考える 125 • また、たとえば次のようなケースではGoogle Playに判定を任 せることはできないので、タグを使ってどのオファーを優先させ るのかをアプリ側で判定する必要がある ◦ 季節限定でオファーを提供したい ◦
アプリが持つユーザー情報を元に特定のユーザー向けに オファーを提供したい -
128 オファーの中で一番低い価格のオファーを探して取得する // オファーの中で一番低い価格のオファーを探して取得する private fun List<ProductDetails.SubscriptionOfferDetails>.findOffer(): ProductDetails.SubscriptionOfferDetails? { var
lowestPrice = Long.MAX_VALUE var selectedOfferDetails: ProductDetails.SubscriptionOfferDetails? = null this.forEach { offerDetails -> offerDetails.pricingPhases.pricingPhaseList.forEach { pricingPhase -> // 価格が一番低いオファーを探して保持する if (pricingPhase.priceAmountMicros < lowestPrice) { lowestPrice = pricingPhase.priceAmountMicros selectedOfferDetails = offerDetails } } } return selectedOfferDetails } -
129 オファーの中で一番低い価格のオファーを探して取得する // オファーの中で一番低い価格のオファーを探して取得する private fun List<ProductDetails.SubscriptionOfferDetails>.findOffer(): ProductDetails.SubscriptionOfferDetails? { var
lowestPrice = Long.MAX_VALUE var selectedOfferDetails: ProductDetails.SubscriptionOfferDetails? = null this.forEach { offerDetails -> offerDetails.pricingPhases.pricingPhaseList.forEach { pricingPhase -> // 価格が一番低いオファーを探して保持する if (pricingPhase.priceAmountMicros < lowestPrice) { lowestPrice = pricingPhase.priceAmountMicros selectedOfferDetails = offerDetails } } } return selectedOfferDetails } -
130 オファーの中で一番低い価格のオファーを探して取得する // オファーの中で一番低い価格のオファーを探して取得する private fun List<ProductDetails.SubscriptionOfferDetails>.findOffer(): ProductDetails.SubscriptionOfferDetails? { var
lowestPrice = Long.MAX_VALUE var selectedOfferDetails: ProductDetails.SubscriptionOfferDetails? = null this.forEach { offerDetails -> offerDetails.pricingPhases.pricingPhaseList.forEach { pricingPhase -> // 価格が一番低いオファーを探して保持する if (pricingPhase.priceAmountMicros < lowestPrice) { lowestPrice = pricingPhase.priceAmountMicros selectedOfferDetails = offerDetails } } } return selectedOfferDetails } -
131 オファーの中で一番低い価格のオファーを探して取得する // オファーの中で一番低い価格のオファーを探して取得する private fun List<ProductDetails.SubscriptionOfferDetails>.findOffer(): ProductDetails.SubscriptionOfferDetails? { var
lowestPrice = Long.MAX_VALUE var selectedOfferDetails: ProductDetails.SubscriptionOfferDetails? = null this.forEach { offerDetails -> offerDetails.pricingPhases.pricingPhaseList.forEach { pricingPhase -> // 価格が一番低いオファーを探して保持する if (pricingPhase.priceAmountMicros < lowestPrice) { lowestPrice = pricingPhase.priceAmountMicros selectedOfferDetails = offerDetails } } } return selectedOfferDetails } -
132 オファーの中で一番低い価格のオファーを探して取得する // オファーの中で一番低い価格のオファーを探して取得する private fun List<ProductDetails.SubscriptionOfferDetails>.findOffer(): ProductDetails.SubscriptionOfferDetails? { var
lowestPrice = Long.MAX_VALUE var selectedOfferDetails: ProductDetails.SubscriptionOfferDetails? = null this.forEach { offerDetails -> offerDetails.pricingPhases.pricingPhaseList.forEach { pricingPhase -> // 価格が一番低いオファーを探して保持する if (pricingPhase.priceAmountMicros < lowestPrice) { lowestPrice = pricingPhase.priceAmountMicros selectedOfferDetails = offerDetails } } } return selectedOfferDetails } -
133 オファーの中で一番低い価格のオファーを探して取得する // p1mの基本プランの中の一番価格の低いオファーを取得する val p1mOfferDetails = subscriptionOfferDetailsList .filter {
subscriptionOfferDetails -> subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } } .findOffer() -
134 オファーの中で一番低い価格のオファーを探して取得する // p1mの基本プランの中の一番価格の低いオファーを取得する val p1mOfferDetails = subscriptionOfferDetailsList .filter {
subscriptionOfferDetails -> subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } } .findOffer() -
135 オファーの中で一番低い価格のオファーを探して取得する // p1mの基本プランの中の一番価格の低いオファーを取得する val p1mOfferDetails = subscriptionOfferDetailsList .filter {
subscriptionOfferDetails -> subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } } .findOffer() -
無料期間30日のオファーのSubscriptionOfferDetails val offerDetails = p1mOfferDetailsList[0] offerDetails.offerTags = p1m, free-trial ━━━━━━━━━━━━━━━━━━━━
offerDetails.pricingPhases.pricingPhaseList [0] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=1 pricingPhase.billingPeriod=P4W2D pricingPhase.formattedPrice=無料 pricingPhase.priceAmountMicros=0 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=FINITE_RECURRING ━━━━━━━━━━━━━━━━━━━━ offerDetails.pricingPhases.pricingPhaseList [1] ━━━━━━━━━━━━━━━━━━━━ pricingPhase.billingCycleCount=0 pricingPhase.billingPeriod=P1M pricingPhase.formattedPrice=¥500 pricingPhase.priceAmountMicros=500000000 pricingPhase.priceCurrencyCode=JPY pricingPhase.recurrenceMode=INFINITE_RECURRING ━━━━━━━━━━━━━━━━━━━━ 136 基本プランとそれに紐づくオファーを探す • 今回のロジックだと価格しか見ていな いので、価格が低いかつ一番最初に 見つかった30日のオファーが選択さ れる • 無料期間の長いものを選択するに は、これに追加してbillingPeriodが 長いものを優先するなど、提供するオ ファーに応じてユーザーに一番有利 なオファーが選択されるなどの工夫を したほうがよい -
137 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id(
pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails • ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト • 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails • 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ -
138 購入をリクエストする // 購入をリクエストするオファートークンを取得 val offerToken = p1mOfferDetails?.offerToken.orEmpty() val productDetailsParams
= ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .setOfferToken(offerToken) .build() val flowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(listOf(productDetailsParams)) .build() billingClient.launchBillingFlow(activity, flowParams) -
139 購入をリクエストする // 購入をリクエストするオファートークンを取得 val offerToken = p1mOfferDetails?.offerToken.orEmpty() val productDetailsParams
= ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .setOfferToken(offerToken) .build() val flowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(listOf(productDetailsParams)) .build() billingClient.launchBillingFlow(activity, flowParams) -
140 購入をリクエストする // 購入をリクエストするオファートークンを取得 val offerToken = p1mOfferDetails?.offerToken.orEmpty() val productDetailsParams
= ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .setOfferToken(offerToken) .build() val flowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(listOf(productDetailsParams)) .build() billingClient.launchBillingFlow(activity, flowParams) • 存在しないオファートークンを渡すとエラーになるが、空文字 を渡すとエラーにはならず、基本プランやオファーは自動で選 択される • テスト購入で試した感じ、おそらく基本プランの作成が早いも のが選択されている(確証はない) • ここではorEmptyしているが、意図しない商品の購入画面が 表示される恐れがあるため、空の場合は購入画面は表示せ ず、アプリ側でエラーとして扱ったほうがよいかもしれない -
141 購入をリクエストする // 購入をリクエストするオファートークンを取得 val offerToken = p1mOfferDetails?.offerToken.orEmpty() val productDetailsParams
= ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .setOfferToken(offerToken) .build() val flowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(listOf(productDetailsParams)) .build() billingClient.launchBillingFlow(activity, flowParams) -
142 購入をリクエストする // 購入をリクエストするオファートークンを取得 val offerToken = p1mOfferDetails?.offerToken.orEmpty() val productDetailsParams
= ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .setOfferToken(offerToken) .build() val flowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(listOf(productDetailsParams)) .build() billingClient.launchBillingFlow(activity, flowParams) -
143 購入をリクエストする // 購入をリクエストするオファートークンを取得 val offerToken = p1mOfferDetails?.offerToken.orEmpty() val productDetailsParams
= ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .setOfferToken(offerToken) .build() val flowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(listOf(productDetailsParams)) .build() billingClient.launchBillingFlow(activity, flowParams) -
144 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id(
pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails • ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト • 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails • 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ -
Play Billing Library 5.0の機能をサポートする 145 1. Play Consoleで基本プランやオファーにタグを設定する ◦ 基本プランが正しく判別できるように、基本プランのタグは必ず一意な
タグを設定しておく 2. オファーの決定ロジックを決める ◦ 先に決めておかないとオファーを正しく活用できない ◦ たとえばオファーの中で一番低い価格のオファーを自動で選択するな どを予め決めておき、チーム内で合意をとっておく -
Play Billing Library 5.0の機能をサポートする 146 3. SkuDetailsを使った購入フローの実装から、ProductDetails を使った購入フローの実装に置き換える ◦ ProductDetailsから基本プランやオファーを取得し、オ
ファーの選択ロジックなどを実装する 4. Skuと名のつく非推奨になったクラスやメソッドが残っていない か確認して、残っていればProductと名のつくものに置き換え る -
参考資料 147 • Success on Google Play with new acquisition,
engagement, and monetization tools ◦ 新しい定期購入にした背景や変更点の概要 ◦ https://youtu.be/7ky2PZl16i4 • Google Play Console での定期購入に関する最近の変更 - Play Console ヘルプ ◦ 下位互換性についての説明 ◦ https://support.google.com/googleplay/android-developer/answer/12124625 • 定期購入について理解する - Play Console ヘルプ ◦ 新しい定期購入の仕組みや機能などについての説明 ◦ https://support.google.com/googleplay/android-developer/answer/12154973 • May 2022 subscription changes guide | Google Play's billing system | Android Developers ◦ Backend APIを含めたサブスクリプションの変更に関する説明 ◦ https://developer.android.com/google/play/billing/compatibility?hl=ja -
参考資料 148 • Google Play Billing Library 4 to 5
Migration Guide ◦ PBL 4から5へのマイグレーション(課金アイテム取得方法の before/afterなど) ◦ https://developer.android.com/google/play/billing/migrate-gpblv5?hl=ja • Android Developers Blog: What's new in Google Play ◦ 課金含めたGoogle Play関連まとめ ◦ https://android-developers.googleblog.com/2022/05/whats-new-in-google-play.html • Play Billing Library 5 を使用してアプリ内で定期購入を販売する ◦ PBLv5のcodelab ◦ https://codelabs.developers.google.com/play-billing-codelab?hl=ja#0 • play-billing-samples/PlayBillingCodelab ◦ PBLv5のcodelabのサンプルコード ◦ https://github.com/android/play-billing-samples/tree/main/PlayBillingCodelab