Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[DroidKaigi 2022] 詳解Google Playの新しい定期購入 ~オファーの活用や実装例を添えて~

syarihu
October 05, 2022

[DroidKaigi 2022] 詳解Google Playの新しい定期購入 ~オファーの活用や実装例を添えて~

DroidKaigi 2022で発表した「詳解Google Playの新しい定期購入 ~オファーの活用や実装例を添えて~」の発表資料です。
https://droidkaigi.jp/2022/timetable/363682

syarihu

October 05, 2022
Tweet

More Decks by syarihu

Other Decks in Technology

Transcript

  1. 従来の定期購入とその課題について 6 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間:

    月別 • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日
  2. 従来の定期購入とその課題について 7 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間:

    月別 • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日
  3. 従来の定期購入とその課題について 8 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間:

    月別 • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額プレミアムプラン • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 期間限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日、お試し価格: 5,000円
  4. 従来の定期購入とその課題について 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円
  5. 従来の定期購入とその課題について 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日
  6. 19 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別

    • 価格: 500円 オファー • 無料期間: 30日 従来の定期購入
  7. 20 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別

    • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 従来の定期購入
  8. 21 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別

    • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 従来の定期購入
  9. 22 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別

    • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 Subscription • 商品名: PROプラン 従来の定期購入 新しい定期購入に再構築した例
  10. 23 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別

    • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 Subscription • 商品名: PROプラン Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 従来の定期購入 新しい定期購入に再構築した例
  11. 24 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別

    • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 Subscription • 商品名: PROプラン オファー 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 オファー 提供の条件: 新規ユーザー 段階: • 無料試用 60日間 従来の定期購入 新しい定期購入に再構築した例
  12. 25 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間

    • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 期間限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額プレミアムプラン 初回限定 • 請求対象期間: 年間 • 価格: 5,500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 年額PROプラン 学生限定 • 請求対象期間: 年間 • 価格: 4,000円 オファー • 無料期間: 30日 従来の定期購入
  13. 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円 固定割引
  14. 27 • Subscription (SKU) • 商品名: 年額PROプラン • 請求対象期間: 年間

    • 価格: 5,500円 オファー • 無料期間: 30日 Subscription • 商品名: PROプラン Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 無料試用 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 従来の定期購入 新しい定期購入に再構築した例
  15. 28 • Subscription (SKU) • 商品名: 年額PROプラン 期間限定 • 請求対象期間:

    年間 • 価格: 5,500円 オファー • お試し価格: 5,000円 Subscription • 商品名: PROプラン Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 期間限定 提供の条件: デベロッパー指定 段階: • 1年間 500円 固定割引 従来の定期購入 新しい定期購入に再構築した例
  16. 29 • Subscription (SKU) • 商品名: 年額PROプラン 初回限定 • 請求対象期間:

    年間 • 価格: 5,500円 オファー • 無料期間: 30日、お試し価格: 4,500円 Subscription • 商品名: PROプラン Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円 固定割引 従来の定期購入 新しい定期購入に再構築した例
  17. 30 • Subscription (SKU) • 商品名: 年額PROプラン 学生限定 • 請求対象期間:

    年間 • 価格: 4,000円 オファー • 無料期間: 30日 Subscription • 商品名: PROプラン Base plan • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 従来の定期購入 新しい定期購入に再構築した例 定期購入を解約するまで 割引価格で利用できてしまう 定期的な支払いで割引価格が提供で きるようになったので、 今回は例として3年間固定割引にして いる オファー - 学生限定 提供の条件: デベロッパー指定 段階: • 無料試用 30日間 • 3年間 1,500円 固定割引
  18. 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円 固定割引
  19. BenefitsとOffer • 定期購入の詳細で編集できる「特典」 とこれから説明する基本プランに追 加する「特典」は別物なので注意 ◦ 定期購入の詳細では、定期購入を 購入することにより得られるメリット を記載する •

    定期購入の詳細の「特典」は英語で はBenefits、基本プランに追加する 「特典」はOfferとなっているため、本 発表では基本プランに追加する「特 典」はオファーとして扱う 34
  20. 2. 基本プランを追加する 基本プランID • 定期購入ごとに一意なID 更新の種類 • 自動更新 or 前払い

    • 種類ごとに、1つの定期購入に対し て同じ請求対象期間の基本プラン を複数作ることはできない ◦ 月額の基本プランを2つ追加 するなどはできない ◦ 無効にしても同じ期間の基本 プランは追加できないので注 意 36
  21. 2. 基本プランを追加する タグ • 実装時に基本プランやオファーを識別するため のタグ • アプリの実装では基本プランやオファーをタグ でしか判別できないため、必ず設定しておく •

    基本プランやオファーごとに最大 20個設定でき る • 基本プランに設定したタグは、オファーのタグ にも継承される ◦ Play Console上からは見えないが、 アプリのAPIからオファーのタグを取 得するときに基本プランに設定したタ グも含まれている 37
  22. 3. オファーを追加する 特典ID • オファーを識別する一意な ID 基本プランと公開設定 • 基本プランは追加する対象の基本プラン が表示される

    • 公開設定はオファーを提供する提供地域 を選択する(デフォルトでは基本プランと 同じ提供地域になる) タグ • アプリでオファーを識別するタグ • 用途は基本プランのタグと同じ 39
  23. 3. オファーを追加する 購入フェーズ(段階) 1回限りのお支払い - 価格のオーバーライド • 固定額 ◦ ここで設定した価格に上書きする

    ◦ 通常価格が5,000円で、固定額が 4,000円なら4,000円になる • 割引率 ◦ 通常価格からの割引率( ~ %) ◦ 通常価格が5,000円で10%に設定し たら4,500円 • 割引額 ◦ 通常価格からの割引額( ~ 円 ) ◦ 通常価格が5,000円で500円に設定 したら4,500円 48
  24. 3. オファーを追加する 購入フェーズ(段階) タイプ • 定期的なお支払い 2 ~ 52回のいずれかの請求対象期間、固定 額または割引した価格を支払うことで利用で

    きる たとえば年間払いで請求対象期間を 2回に すると、2回目の支払いまでは固定額または 割引した価格で利用できる その後、基本プランの価格での支払いが始 まる 49
  25. 3. オファーを追加する 50 購入フェーズ(段階) 価格のオーバーライド • 固定額 ◦ ここで設定した価格に上書きする •

    割引率 ◦ 既存価格からの割引率( ~ %) ◦ 既存価格が5,000円で10%に設定し たら4,500円 • 割引額 ◦ 既存価格からの割引額( ~ 円 ) ◦ 既存価格が5,000円で500円に設定 したら4,500円
  26. 52 Subscription • 商品名: PROプラン Base plan - 初回限定 •

    更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引 新しい定期購入の作り方
  27. 53 Subscription • 商品名: PROプラン Base plan - 初回限定 •

    更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引 新しい定期購入の作り方
  28. 54 Subscription • 商品名: PROプラン Base plan - 初回限定 •

    更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引 新しい定期購入の作り方
  29. 55 Subscription • 商品名: PROプラン Base plan - 初回限定 •

    更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引 新しい定期購入の作り方
  30. 56 新しい定期購入の作り方 Subscription • 商品名: PROプラン Base plan - 初回限定

    • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引
  31. 57 新しい定期購入の作り方 Subscription • 商品名: PROプラン Base plan - 初回限定

    • 更新の種類: 自動更新 • 請求対象期間 : 年間 • 価格: 5,500円 オファー - 初回限定 提供の条件: 新規ユーザー 段階: • 無料試用 30日間 • 1年間 1,000円割引
  32. 62 自動移行された既存の定期購入 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間:

    月別 • 価格: 500円 オファー • 無料期間: 30日 月別のプランはp1mの基本プランとして追加されている(半年なら p6m、年間ならp1yになる)
  33. 63 自動移行された既存の定期購入 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間:

    月別 • 価格: 500円 オファー • 無料期間: 30日 無料期間は freetrial のオファーとして追加されている
  34. 64 自動移行された既存の定期購入 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間:

    月別 • 価格: 500円 オファー • 無料期間: 30日 自動移行されたプランは下位互換性のあるプランとしてマークされている 下位互換性のある基本プランは、 PBLv5で非推奨になっている querySkuDetailsAsync() メソッドに 返されるため、PBLv4以前の既存の実装でも問題なく購入できるようになっている
  35. 65 Subscription • 商品名: PROプラン Offer 提供の条件: 新規ユーザー 段階: •

    無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 • Subscription (SKU) • 商品名: 月額PROプラン • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 30日 • Subscription (SKU) • 商品名: 月額PROプラン 2ヶ月無料 • 請求対象期間: 月別 • 価格: 500円 オファー • 無料期間: 60日 Offer 提供の条件: 新規ユーザー 段階: • 無料試用 60日間 理想の定期購入の構成 - 月額
  36. 66 Subscription • 商品名: PROプラン Offer 提供の条件: 新規ユーザー 段階: •

    無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 Offer 提供の条件: 新規ユーザー 段階: • 無料試用 60日間 実際に自動移行された定期購入の構成 - 月額 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 Subscription • 商品名: PROプラン 2ヶ月無料
  37. 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円 固定割引
  38. 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円 固定割引
  39. 既存の定期購入を新しい 定期購入にしようとすると … 既存の定期購入に基本プランを増やす こともできるが、一つの定期購入商品 に対して一つの請求対象期間のみだっ たのが、 71 Base plan

    • 請求対象期間 : 月間 Subscription 定期購入1 - 月額 Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年額 既存の定期購入の構成
  40. 新しく作成する定期購入だけ新しい構成に則る 74 Base plan • 請求対象期間 : 月間 Subscription 定期購入1

    - 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い 自動移行された既存の定期購入
  41. 新しく作成する定期購入だけ新しい構成に則る 75 Base plan • 請求対象期間 : 月間 Subscription 定期購入1

    - 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い Base plan • 請求対象期間 : 月間 Subscription 新しい定期購入 Base plan • 請求対象期間 : 年間 自動移行された既存の定期購入 新しい定期購入だけ定期購入の下に 複数の期間を追加する
  42. 新しく作成する定期購入だけ新しい構成に則る 76 Base plan • 請求対象期間 : 月間 Subscription 定期購入1

    - 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い Base plan • 請求対象期間 : 月間 Subscription 新しい定期購入 Base plan • 請求対象期間 : 年間 新しい定期購入だけ定期購入の下に 複数の期間を追加する 自動移行された既存の定期購入 • 自動移行された既存の定期購入は定期購入と 期間がイコールなのでシンプルだったが、一つ の定期購入に複数の基本プランが紐づく • 特定の期間(基本プラン)を購入しようとしたと き、定期購入に紐づく基本プランを探さなけれ ばならないので、既存と新しい定期購入で異な る実装をしなければならないため、実装が少し 複雑になる • 一部だけ違う構成になるので、構成が統一さ れておらず管理上も少しややこしくなる
  43. 新しく作成する定期購入だけ新しい構成に則る 77 Base plan • 請求対象期間 : 月間 Subscription 定期購入1

    - 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い Base plan • 請求対象期間 : 半年 自動移行された既存の定期購入 • 既存の定期購入は定期購入と期間が 同じのため、商品名を変えないとおかし くなってしまう • 既存の定期購入に別の期間を追加す ることはできない
  44. 新しく作成する定期購入だけ新しい構成に則る 78 Base plan • 請求対象期間 : 月間 Subscription 定期購入1

    - 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い Subscription 定期購入1 - 半年払い Base plan • 請求対象期間 : 半年 1つの定期購入に対して 1つの期間のみが 紐づくように作成する 自動移行された既存の定期購入
  45. 新しい定期購入の構成に則るのを諦める 79 Base plan • 請求対象期間 : 月間 Subscription 定期購入1

    - 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い 自動移行された既存の定期購入 Subscription 定期購入2 - 月払い Base plan • 請求対象期間 : 月間 Subscription 定期購入2 - 年払い Base plan • 請求対象期間 : 年間 いままでどおり、一つの定期購入に対して一つの 期間(基本プラン)のみを追加する 商品の扱いが変わらないのでオファー以外の実装 は既存の実装がそのまま使える(はず)
  46. 全部新しい定期購入の構成に則るように頑張る ※これをやるのはおすすめしません 80 Base plan • 請求対象期間 : 月間 Subscription

    定期購入1 - 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い 自動移行された既存の定期購入 Base plan • 請求対象期間 : 月間 Subscription 新しい定期購入1 Base plan • 請求対象期間 : 年間 案1. 新しい定期購入を作る 新規ユーザーはこちらを購入させ、 既存ユーザーはそのままにするか、切り替えを促す(なぜ 移行しないといけないのか、ユーザーへの説明が難しい)
  47. 全部新しい定期購入の構成に則るように頑張る ※これをやるのはおすすめしません 81 Base plan • 請求対象期間 : 月間 Subscription

    定期購入1 - 月払い Base plan • 請求対象期間 : 年間 Subscription 定期購入1 - 年払い 自動移行された既存の定期購入 案2. 既存の定期購入に基本プランを増やす 新規ユーザーは新しい基本プランを購入させ、 既存ユーザーはそのままにするか、別の定期購入を購入 しているユーザーには移行を促す 既存の定期購入の商品名も変える必要がある Base plan • 請求対象期間 : 月間
  48. 85 Subscription • 商品名: 月額PROプラン Offer 提供の条件: 新規ユーザー 段階: •

    無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 既存の定期購入における新しいオファーの活用方法 通常の無料試用
  49. 86 Subscription • 商品名: 月額PROプラン Offer 提供の条件: 新規ユーザー 段階: •

    無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 既存の定期購入における新しいオファーの活用方法 Offer 提供の条件: デベロッパー指定 段階: • 1ヶ月 20%割引 通常の無料試用 季節限定セール
  50. 87 Subscription • 商品名: 月額PROプラン Offer 提供の条件: 新規ユーザー 段階: •

    無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 既存の定期購入における新しいオファーの活用方法 Offer 提供の条件: デベロッパー指定 段階: • 1ヶ月 20%割引 通常の無料試用 季節限定セール Offer 提供の条件: 新規ユーザー 段階: • 3ヶ月 価格を300円に固定 期間限定 新規ユーザー向けセール
  51. 88 Subscription • 商品名: 月額PROプラン Offer 提供の条件: 新規ユーザー 段階: •

    無料試用 30日間 Base plan • 更新の種類: 自動更新 • 請求対象期間 : 月別 • 価格: 500円 既存の定期購入における新しいオファーの活用方法 Offer 提供の条件: デベロッパー指定 段階: • 1ヶ月 20%割引 通常の無料試用 季節限定セール Offer 提供の条件: デベロッパー指定 段階: • 1ヶ月 100円割引 Offer 提供の条件: 新規ユーザー 段階: • 3ヶ月 価格を300円に固定 期間限定 新規ユーザー向けセール 過去に購読してくれていた ユーザーに対しての おかえりなさいオファー
  52. 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" }
  53. 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
  54. Play Billing Library 5.0への移行 94 • PBL 3.xのサポートは2022年11月に終了する • 2022年11月以降、PBLを利用しているアプリがアプリの更新

    を公開するにはPBL 4.x以上にアップグレードする必要がある • PBL 5.0は、新しい定期購入の機能へ順次対応できるように、 古いメソッドは非推奨の形で残っている
  55. Play Billing Library 5.0への移行 95 • 4.xへのアップグレードと5.xへのアップグレードの移行コスト はほとんど変わらないため、まだ 3.xを利用しているアプリは 一旦5.0にアップグレードするのがおすすめ

    • すでに4.xを利用しているアプリも、一旦5.0に上げておき、 5.0から追加された機能や非推奨のメソッドは画面ごとに置き 換えるなど、順次対応していくのがおすすめ
  56. Play Billing Library 5.0の変更サマリ 98 • SkuDetailsに置き換わる、ProductDetailsが新たに追加された ◦ ProductDetailsには、すべての基本プランとそれに紐づくすべ てのオファー情報が含まれる

    • Sku関連のメソッドなどの命名がProductになったものが追加され た • それに伴い、Sku関連のクラスやメソッドなどはすべて非推奨になっ た
  57. Play Billing Library 5.0の変更サマリ 99 • SkuDetailsによる購入フローでは、skuは商品自体に定期購 入の期間も含まれていたため、SkuDetailsを渡せば購入でき ていた •

    ProductDetailsによる新しい定期購入の購入フローでは、 ProductDetailsの中に複数の基本プラン(定期購入の期間) やオファーが含まれるため、基本プランやオファーを選択する 実装が必要になった
  58. 104 offerTag = p1m offerTag = free-trial offerTag = free-trial-60days

    Play Billing Library 5.0からのProductDetailsを使った購入までの処理の流れ
  59. 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を使った購入までの処理の流れ
  60. 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を使った購入までの処理の流れ
  61. 107 val product = QueryProductDetailsParams.Product .newBuilder() .setProductId("pro_plan") .setProductType(BillingClient.ProductType.SUBS) .build() val

    queryProductDetailsParams = QueryProductDetailsParams .newBuilder() .setProductList(listOf(product)) .build() ProductDetailsを取得するためのパラメータを生成する
  62. 108 val product = QueryProductDetailsParams.Product .newBuilder() .setProductId("pro_plan") .setProductType(BillingClient.ProductType.SUBS) .build() val

    queryProductDetailsParams = QueryProductDetailsParams .newBuilder() .setProductList(listOf(product)) .build() ProductDetailsを取得するためのパラメータを生成する
  63. 109 val product = QueryProductDetailsParams.Product .newBuilder() .setProductId("pro_plan") .setProductType(BillingClient.ProductType.SUBS) .build() val

    queryProductDetailsParams = QueryProductDetailsParams .newBuilder() .setProductList(listOf(product)) .build() ProductDetailsを取得するためのパラメータを生成する
  64. 110 val product = QueryProductDetailsParams.Product .newBuilder() .setProductId("pro_plan") .setProductType(BillingClient.ProductType.SUBS) .build() val

    queryProductDetailsParams = QueryProductDetailsParams .newBuilder() .setProductList(listOf(product)) .build() ProductDetailsを取得するためのパラメータを生成する
  65. 111 ProductDetailsを取得する billingClient.queryProductDetailsAsync( queryProductDetailsParams ) { billingResult: BillingResult, productDetailsList ->

    if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && productDetailsList.isNotEmpty() ) { onResponse(productDetailsList) } else { // エラーハンドリング } }
  66. 112 ProductDetailsを取得する billingClient.queryProductDetailsAsync( queryProductDetailsParams ) { billingResult: BillingResult, productDetailsList ->

    if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && productDetailsList.isNotEmpty() ) { onResponse(productDetailsList) } else { // エラーハンドリング } }
  67. 113 ProductDetailsを取得する billingClient.queryProductDetailsAsync( queryProductDetailsParams ) { billingResult: BillingResult, productDetailsList ->

    if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && productDetailsList.isNotEmpty() ) { onResponse(productDetailsList) } else { // エラーハンドリング } }
  68. 114 ProductDetailsを取得する billingClient.queryProductDetailsAsync( queryProductDetailsParams ) { billingResult: BillingResult, productDetailsList ->

    if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && productDetailsList.isNotEmpty() ) { onResponse(productDetailsList) } else { // エラーハンドリング } }
  69. 115 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id(

    pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails • ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト • 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails • 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ
  70. 116 基本プランとそれに紐づくオファーを探す // 基本プラン(オファー無し)と基本プランに紐づくオファーが取得できる val p1mOfferDetailsList = subscriptionOfferDetailsList .filter {

    subscriptionOfferDetails -> // 基本プランに設定したタグを探す // 基本プランに紐づくオファーには基本プランに設定したタグが継承されるため、 // タグが一意であれば基本プランとそれに紐づくオファーが取得できる subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } }
  71. 117 基本プランとそれに紐づくオファーを探す // 基本プラン(オファー無し)と基本プランに紐づくオファーが取得できる val p1mOfferDetailsList = subscriptionOfferDetailsList .filter {

    subscriptionOfferDetails -> // 基本プランに設定したタグを探す // 基本プランに紐づくオファーには基本プランに設定したタグが継承されるため、 // タグが一意であれば基本プランとそれに紐づくオファーが取得できる subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } }
  72. 118 基本プランとそれに紐づくオファーを探す // 基本プラン(オファー無し)と基本プランに紐づくオファーが取得できる val p1mOfferDetailsList = subscriptionOfferDetailsList .filter {

    subscriptionOfferDetails -> // 基本プランに設定したタグを探す // 基本プランに紐づくオファーには基本プランに設定したタグが継承されるため、 // タグが一意であれば基本プランとそれに紐づくオファーが取得できる subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } }
  73. 無料期間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 ━━━━━━━━━━━━━━━━━━━━
  74. 無料期間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 ━━━━━━━━━━━━━━━━━━━━
  75. オファー無し(基本プラン) の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 基本プランとそれに紐づくオファーを探す
  76. オファー無し(基本プラン) の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 基本プランとそれに紐づくオファーを探す
  77. 123 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id(

    pro )を元にProductDetailsを取得 Product IdがproのProductDetails • ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト • 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails • 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ
  78. 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 }
  79. 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 }
  80. 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 }
  81. 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 }
  82. 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 }
  83. 無料期間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が 長いものを優先するなど、提供するオ ファーに応じてユーザーに一番有利 なオファーが選択されるなどの工夫を したほうがよい
  84. 137 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id(

    pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails • ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト • 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails • 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ
  85. 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)
  86. 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)
  87. 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しているが、意図しない商品の購入画面が 表示される恐れがあるため、空の場合は購入画面は表示せ ず、アプリ側でエラーとして扱ったほうがよいかもしれない
  88. 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)
  89. 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)
  90. 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)
  91. 144 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id(

    pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails • ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト • 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails • 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ
  92. Play Billing Library 5.0の機能をサポートする 145 1. Play Consoleで基本プランやオファーにタグを設定する ◦ 基本プランが正しく判別できるように、基本プランのタグは必ず一意な

    タグを設定しておく 2. オファーの決定ロジックを決める ◦ 先に決めておかないとオファーを正しく活用できない ◦ たとえばオファーの中で一番低い価格のオファーを自動で選択するな どを予め決めておき、チーム内で合意をとっておく
  93. Play Billing Library 5.0の機能をサポートする 146 3. SkuDetailsを使った購入フローの実装から、ProductDetails を使った購入フローの実装に置き換える ◦ ProductDetailsから基本プランやオファーを取得し、オ

    ファーの選択ロジックなどを実装する 4. Skuと名のつく非推奨になったクラスやメソッドが残っていない か確認して、残っていればProductと名のつくものに置き換え る
  94. 参考資料 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
  95. 参考資料 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