Slide 1

Slide 1 text

詳解Google Playの新しい定期購入 ~オファーの活用や実装例を添えて~ Taichi Sato / @syarihu Giftmall, Inc. Android Engineer 1

Slide 2

Slide 2 text

Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行 Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 2

Slide 3

Slide 3 text

Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行 Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 3

Slide 4

Slide 4 text

従来の定期購入とその課題について ● 機能が同じ定期購入でも提供したいオファーなどが違う場合 は、ひとつの商品(SKU)として追加しなければならない ● SKUがどんどん増えていき、管理が複雑になってしまう ● SKUを作成するたびにアプリをリリースしなければならない 4

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

従来の定期購入とその課題について 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円

Slide 10

Slide 10 text

従来の定期購入とその課題について 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日

Slide 11

Slide 11 text

従来の定期購入とその課題について ● このように、従来の方法だとSKUが増え続け複雑になってしま う ● これらの課題を解決し、各定期購入の管理コストを下げ、より 柔軟にオファーを作成できるように、Googleは定期購入の販 売方法を再構築した 11

Slide 12

Slide 12 text

Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行 Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 12

Slide 13

Slide 13 text

13 https://support.google.com/googleplay/android-developer/answer/12124625 より引用

Slide 14

Slide 14 text

14 https://support.google.com/googleplay/android-developer/answer/12124625 より引用

Slide 15

Slide 15 text

15 https://support.google.com/googleplay/android-developer/answer/12124625 より引用

Slide 16

Slide 16 text

16 https://support.google.com/googleplay/android-developer/answer/12124625 より引用

Slide 17

Slide 17 text

17 https://support.google.com/googleplay/android-developer/answer/12124625 より引用

Slide 18

Slide 18 text

18 https://support.google.com/googleplay/android-developer/answer/12124625 より引用

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

25 ● Subscription (SKU) ● 商品名: 年額PROプラン ● 請求対象期間: 年間 ● 価格: 5,500円 オファー ● 無料期間: 30日 ● Subscription (SKU) ● 商品名: 年額PROプラン 期間限定 ● 請求対象期間: 年間 ● 価格: 5,500円 オファー ● 無料期間: 30日 ● Subscription (SKU) ● 商品名: 年額プレミアムプラン 初回限定 ● 請求対象期間: 年間 ● 価格: 5,500円 オファー ● 無料期間: 30日 ● Subscription (SKU) ● 商品名: 年額PROプラン 学生限定 ● 請求対象期間: 年間 ● 価格: 4,000円 オファー ● 無料期間: 30日 従来の定期購入

Slide 26

Slide 26 text

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円 固定割引

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

30 ● Subscription (SKU) ● 商品名: 年額PROプラン 学生限定 ● 請求対象期間: 年間 ● 価格: 4,000円 オファー ● 無料期間: 30日 Subscription ● 商品名: PROプラン Base plan ● 更新の種類: 自動更新 ● 請求対象期間 : 年間 ● 価格: 5,500円 従来の定期購入 新しい定期購入に再構築した例 定期購入を解約するまで 割引価格で利用できてしまう 定期的な支払いで割引価格が提供で きるようになったので、 今回は例として3年間固定割引にして いる オファー - 学生限定 提供の条件: デベロッパー指定 段階: ● 無料試用 30日間 ● 3年間 1,500円 固定割引

Slide 31

Slide 31 text

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円 固定割引

Slide 32

Slide 32 text

Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行 Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 32

Slide 33

Slide 33 text

新しい定期購入の作り方 1. 定期購入を作成する 2. 定期購入の下に基本プランを追加する ○ 更新期間や地域別の価格などを設定する 3. 基本プランの下にオファー(特典)を追加する ○ 無料試用や特定の価格から割引きたい場合はオファーと して追加する 33

Slide 34

Slide 34 text

BenefitsとOffer ● 定期購入の詳細で編集できる「特典」 とこれから説明する基本プランに追 加する「特典」は別物なので注意 ○ 定期購入の詳細では、定期購入を 購入することにより得られるメリット を記載する ● 定期購入の詳細の「特典」は英語で はBenefits、基本プランに追加する 「特典」はOfferとなっているため、本 発表では基本プランに追加する「特 典」はオファーとして扱う 34

Slide 35

Slide 35 text

1. 定期購入を作成する アイテムID ● 定期購入を識別する一意なID ● 実装ではProduct Id(旧SKU) 名前 ● ユーザーにも表示される定期購入 名 35

Slide 36

Slide 36 text

2. 基本プランを追加する 基本プランID ● 定期購入ごとに一意なID 更新の種類 ● 自動更新 or 前払い ● 種類ごとに、1つの定期購入に対し て同じ請求対象期間の基本プラン を複数作ることはできない ○ 月額の基本プランを2つ追加 するなどはできない ○ 無効にしても同じ期間の基本 プランは追加できないので注 意 36

Slide 37

Slide 37 text

2. 基本プランを追加する タグ ● 実装時に基本プランやオファーを識別するため のタグ ● アプリの実装では基本プランやオファーをタグ でしか判別できないため、必ず設定しておく ● 基本プランやオファーごとに最大 20個設定でき る ● 基本プランに設定したタグは、オファーのタグ にも継承される ○ Play Console上からは見えないが、 アプリのAPIからオファーのタグを取 得するときに基本プランに設定したタ グも含まれている 37

Slide 38

Slide 38 text

3. オファーを追加する ● 追加する対象の基本プランを選択 して、オファーを追加する 38

Slide 39

Slide 39 text

3. オファーを追加する 特典ID ● オファーを識別する一意な ID 基本プランと公開設定 ● 基本プランは追加する対象の基本プラン が表示される ● 公開設定はオファーを提供する提供地域 を選択する(デフォルトでは基本プランと 同じ提供地域になる) タグ ● アプリでオファーを識別するタグ ● 用途は基本プランのタグと同じ 39

Slide 40

Slide 40 text

3. オファーを追加する 提供の条件 ● 新規ユーザーの獲得 ● アップグレード ● デベロッパー指定 40

Slide 41

Slide 41 text

3. オファーを追加する 提供の条件 ● 新規ユーザーの獲得 この定期購入を利用したことがないか、 このアプリの定期購入をまったく利用し たことが無いユーザーを対象としてオ ファーを提供する このオファーの対象になるかは購入時 にGoogle Playが判定する 41

Slide 42

Slide 42 text

3. オファーを追加する 提供の条件 ● アップグレード 特定の定期購入と基本プランを購読中 のユーザーを対象としてオファーを提 供する このオファーの対象になるかは購入時 にGoogle Playが判定する 42

Slide 43

Slide 43 text

3. オファーを追加する 提供の条件 ● デベロッパー指定 ユーザーがオファーの対象となるかどう かはデベロッパーがアプリ内で指定す る オファーをアプリ外で販売することはで きない 43

Slide 44

Slide 44 text

3. オファーを追加する 提供の条件 ● デベロッパー指定 たとえば、アプリが持つユーザー情報 を元にオファーを提供するのかを決め たい場合は、提供条件をデベロッパー 指定にする 44

Slide 45

Slide 45 text

3. オファーを追加する 提供の条件 ● デベロッパー指定 また、ユーザーが利用できるオファーの 一覧をアプリで表示したい場合は、条 件をデベロッパー指定にする デベロッパー指定のオファーにタグを設 定して、そのタグが設定されたオファー のみを表示するなどの使い方ができる 45

Slide 46

Slide 46 text

3. オファーを追加する 購入フェーズ(段階) タイプ ● 1回限りのお支払い ● 定期的なお支払い ● 無料試用 46

Slide 47

Slide 47 text

3. オファーを追加する 購入フェーズ(段階) タイプ ● 1回限りのお支払い 1回だけ固定額または割引した価格を支払 い、指定した期間だけ利用できる たとえば1年間だけ特別に割引したい場合 は12ヶ月に設定すると、12ヶ月間はその価 格で利用できる その後、基本プランの価格での支払いが 始まる 47

Slide 48

Slide 48 text

3. オファーを追加する 購入フェーズ(段階) 1回限りのお支払い - 価格のオーバーライド ● 固定額 ○ ここで設定した価格に上書きする ○ 通常価格が5,000円で、固定額が 4,000円なら4,000円になる ● 割引率 ○ 通常価格からの割引率( ~ %) ○ 通常価格が5,000円で10%に設定し たら4,500円 ● 割引額 ○ 通常価格からの割引額( ~ 円 ) ○ 通常価格が5,000円で500円に設定 したら4,500円 48

Slide 49

Slide 49 text

3. オファーを追加する 購入フェーズ(段階) タイプ ● 定期的なお支払い 2 ~ 52回のいずれかの請求対象期間、固定 額または割引した価格を支払うことで利用で きる たとえば年間払いで請求対象期間を 2回に すると、2回目の支払いまでは固定額または 割引した価格で利用できる その後、基本プランの価格での支払いが始 まる 49

Slide 50

Slide 50 text

3. オファーを追加する 50 購入フェーズ(段階) 価格のオーバーライド ● 固定額 ○ ここで設定した価格に上書きする ● 割引率 ○ 既存価格からの割引率( ~ %) ○ 既存価格が5,000円で10%に設定し たら4,500円 ● 割引額 ○ 既存価格からの割引額( ~ 円 ) ○ 既存価格が5,000円で500円に設定 したら4,500円

Slide 51

Slide 51 text

3. オファーを追加する 購入フェーズ(段階) タイプ ● 無料試用 無料試用期間を設定する 既存の定期購入で提供していた無料期 間は購入フェーズの一部となっている ため、無料試用を設定したい場合はこ れを利用する 51

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

基本プランやオファーの反映について ● 即時反映されるわけではなく、若干のラグがある ● Playストアのアプリデータを消し飛ばすと再取得されることも あるので、デバッグ時に更新されないなと思ったらアプリデー タを消してみるのがいいかも 58

Slide 59

Slide 59 text

Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行 Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 59

Slide 60

Slide 60 text

自動移行された既存の定期購入 ● 既存の定期購入は、新しい定期購入として自動移行されてい る ● しかし、自動移行された定期購入は理想の定期購入の構成に はならない 60

Slide 61

Slide 61 text

61 自動移行された既存の定期購入 ● Subscription (SKU) ● 商品名: 月額PROプラン ● 請求対象期間: 月別 ● 価格: 500円 オファー ● 無料期間: 30日

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

64 自動移行された既存の定期購入 ● Subscription (SKU) ● 商品名: 月額PROプラン ● 請求対象期間: 月別 ● 価格: 500円 オファー ● 無料期間: 30日 自動移行されたプランは下位互換性のあるプランとしてマークされている 下位互換性のある基本プランは、 PBLv5で非推奨になっている querySkuDetailsAsync() メソッドに 返されるため、PBLv4以前の既存の実装でも問題なく購入できるようになっている

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

66 Subscription ● 商品名: PROプラン Offer 提供の条件: 新規ユーザー 段階: ● 無料試用 30日間 Base plan ● 更新の種類: 自動更新 ● 請求対象期間 : 月別 ● 価格: 500円 Offer 提供の条件: 新規ユーザー 段階: ● 無料試用 60日間 実際に自動移行された定期購入の構成 - 月額 Base plan ● 更新の種類: 自動更新 ● 請求対象期間 : 月別 ● 価格: 500円 Subscription ● 商品名: PROプラン 2ヶ月無料

Slide 67

Slide 67 text

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円 固定割引

Slide 68

Slide 68 text

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円 固定割引

Slide 69

Slide 69 text

自動移行された既存の定期購入 ● 新しい定期購入では、定期購入に基本プランを追加し、基本 プランにオファーを追加できるようになった ○ すでに提供されている定期購入を新しい定期購入の構成 に移行しようと思うと、期間ごとに定期購入商品が違うた め、別の商品に買い直さなければならない ○ 自動では移行できないのでこの構成になったのだと思わ れる 69

Slide 70

Slide 70 text

既存の定期購入を新しい定期購入にしようとすると… ● 既存の定期購入を購読中のユーザーは勝手には移行できな いので、そのままにするか、新しい定期購入への切り替え手 順を用意して移行を促すしかない ● それでも移行しないユーザーはいるので、単純に定期購入商 品が増えて売上管理や分析、実装などが面倒になる 70

Slide 71

Slide 71 text

既存の定期購入を新しい 定期購入にしようとすると … 既存の定期購入に基本プランを増やす こともできるが、一つの定期購入商品 に対して一つの請求対象期間のみだっ たのが、 71 Base plan ● 請求対象期間 : 月間 Subscription 定期購入1 - 月額 Base plan ● 請求対象期間 : 年間 Subscription 定期購入1 - 年額 既存の定期購入の構成

Slide 72

Slide 72 text

既存の定期購入を新しい 定期購入にしようとすると … 新しく作成する一部の定期購入だけ一 つの定期購入に複数の請求対象期間 が含まれることになる 実装上も、定期購入に紐づく基本プラ ンをタグを元に探すというステップが必 要になり、既存の定期購入と実装方法 が異なるため、そういった点も考慮が必 要になる 72 Base plan ● 請求対象期間 : 月間 Base plan ● 請求対象期間 : 年間 Subscription 定期購入1 新しい定期購入の形

Slide 73

Slide 73 text

自動移行された既存の定期購入をどうするか ● 新しく作成する定期購入だけ新しい構成に則る ● 新しい定期購入の構成に則るのを諦める ● 全部新しい定期購入の構成に則るように頑張る ○ これはデメリットが多すぎるのでおすすめしません 73

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

全部新しい定期購入の構成に則るように頑張る ※これをやるのはおすすめしません 81 Base plan ● 請求対象期間 : 月間 Subscription 定期購入1 - 月払い Base plan ● 請求対象期間 : 年間 Subscription 定期購入1 - 年払い 自動移行された既存の定期購入 案2. 既存の定期購入に基本プランを増やす 新規ユーザーは新しい基本プランを購入させ、 既存ユーザーはそのままにするか、別の定期購入を購入 しているユーザーには移行を促す 既存の定期購入の商品名も変える必要がある Base plan ● 請求対象期間 : 月間

Slide 82

Slide 82 text

自動移行された既存の定期購入をどうするか ● 新しく作成する定期購入だけ新しい構成に則る ● 新しい定期購入の構成に則るのを諦める ○ 新しい定期購入があまり増えないのであればこれが良さそうかも ● 全部新しい定期購入の構成に則るように頑張る 新しい定期購入をどのような構成にするかは予めチームで相談し て方針を決めておくとよい 82

Slide 83

Slide 83 text

Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行 Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 83

Slide 84

Slide 84 text

既存の定期購入における新しいオファーの活用方法 ● 既存の定期購入は新しい定期購入の理想の構成にはできな いものの、新しい構成に自動移行されているのでオファーの 活用はできる 84

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

既存の定期購入における新しいオファーの活用方法 ● 既存の定期購入では1つの定期購入に対して1つの基本プランしか 紐付いていないため、別々の期間に同じオファーを提供したい場合 は、定期購入ごとにオファーを追加する必要がある ● オファーが使えるように正しく実装していれば、リリースしなくてもオ ファーが提供できる場合がある ● 下位互換性の無いオファーを使うにはPBLv5へのアップグレードか つ新しいオファーを利用するための実装が必要 89

Slide 90

Slide 90 text

既存の定期購入における新しいオファーの活用方法 ● どのようなオファーを作成できるのかをチームに共有し、オ ファーの条件に応じて適切な実装をすることでオファーを活用 できるようにしておく 90

Slide 91

Slide 91 text

Agenda 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行 Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07 91

Slide 92

Slide 92 text

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" }

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

Play Billing Library 5.0への移行 94 ● PBL 3.xのサポートは2022年11月に終了する ● 2022年11月以降、PBLを利用しているアプリがアプリの更新 を公開するにはPBL 4.x以上にアップグレードする必要がある ● PBL 5.0は、新しい定期購入の機能へ順次対応できるように、 古いメソッドは非推奨の形で残っている

Slide 95

Slide 95 text

Play Billing Library 5.0への移行 95 ● 4.xへのアップグレードと5.xへのアップグレードの移行コスト はほとんど変わらないため、まだ 3.xを利用しているアプリは 一旦5.0にアップグレードするのがおすすめ ● すでに4.xを利用しているアプリも、一旦5.0に上げておき、 5.0から追加された機能や非推奨のメソッドは画面ごとに置き 換えるなど、順次対応していくのがおすすめ

Slide 96

Slide 96 text

PBLv3からv4へのアップグレードは必要があれば こちらの技術書を参考に… 96 https://techbooster.booth.pm/items/3101567

Slide 97

Slide 97 text

Agenda 97 従来の定期購入とその課題について 再構築された新しい定期購入 新しい定期購入の作り方 自動移行された既存の定期購入 既存の定期購入における新しいオファーの活用方法 Play Billing Library 5.0への移行 Play Billing Library 5.0の機能をサポートする 01 02 03 04 05 06 07

Slide 98

Slide 98 text

Play Billing Library 5.0の変更サマリ 98 ● SkuDetailsに置き換わる、ProductDetailsが新たに追加された ○ ProductDetailsには、すべての基本プランとそれに紐づくすべ てのオファー情報が含まれる ● Sku関連のメソッドなどの命名がProductになったものが追加され た ● それに伴い、Sku関連のクラスやメソッドなどはすべて非推奨になっ た

Slide 99

Slide 99 text

Play Billing Library 5.0の変更サマリ 99 ● SkuDetailsによる購入フローでは、skuは商品自体に定期購 入の期間も含まれていたため、SkuDetailsを渡せば購入でき ていた ● ProductDetailsによる新しい定期購入の購入フローでは、 ProductDetailsの中に複数の基本プラン(定期購入の期間) やオファーが含まれるため、基本プランやオファーを選択する 実装が必要になった

Slide 100

Slide 100 text

100 sku( pro_monthly )を元にSkuDetailsを取得 SkuDetailsを使って購入をリクエストする skuがpro_monthlyのSkuDetails ● これまでは商品自体に購入期間も含まれていたため、 SkuDetailsだけで購入を開始できた Play Billing Library 4.xまでのSkuDetailsを使った購入までの処理の流れ

Slide 101

Slide 101 text

101 sku( pro_monthly )を元にSkuDetailsを取得 SkuDetailsを使って購入をリクエストする skuがpro_monthlyのSkuDetails ● これまでは商品自体に購入期間も含まれていたため、 SkuDetailsだけで購入を開始できた Play Billing Library 4.xまでのSkuDetailsを使った購入までの処理の流れ

Slide 102

Slide 102 text

102 sku( pro_monthly )を元にSkuDetailsを取得 SkuDetailsを使って購入をリクエストする skuがpro_monthlyのSkuDetails ● これまでは商品自体に購入期間も含まれていたため、 SkuDetailsだけで購入を開始できた Play Billing Library 4.xまでのSkuDetailsを使った購入までの処理の流れ

Slide 103

Slide 103 text

103 sku( pro_monthly )を元にSkuDetailsを取得 SkuDetailsを使って購入をリクエストする skuがpro_monthlyのSkuDetails ● これまでは商品自体に購入期間も含まれていたため、 SkuDetailsだけで購入を開始できた Play Billing Library 4.xまでのSkuDetailsを使った購入までの処理の流れ

Slide 104

Slide 104 text

104 offerTag = p1m offerTag = free-trial offerTag = free-trial-60days Play Billing Library 5.0からのProductDetailsを使った購入までの処理の流れ

Slide 105

Slide 105 text

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を使った購入までの処理の流れ

Slide 106

Slide 106 text

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を使った購入までの処理の流れ

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

115 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id( pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails ● ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト ● 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails ● 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

無料期間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 ━━━━━━━━━━━━━━━━━━━━

Slide 120

Slide 120 text

無料期間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 ━━━━━━━━━━━━━━━━━━━━

Slide 121

Slide 121 text

オファー無し(基本プラン) の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 基本プランとそれに紐づくオファーを探す

Slide 122

Slide 122 text

オファー無し(基本プラン) の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 基本プランとそれに紐づくオファーを探す

Slide 123

Slide 123 text

123 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id( pro )を元にProductDetailsを取得 Product IdがproのProductDetails ● ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト ● 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails ● 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ

Slide 124

Slide 124 text

オファーの選択ロジックを考える 124 ● 基本プランに複数のオファーが紐付いているとき、どのオ ファーをユーザーに購入させるのかはアプリが決めなければな らない ● たとえば、オファーの中で一番低い価格のオファーを選択する ように実装しておけば、無料期間があれば無料期間のオファー が選択されるなど

Slide 125

Slide 125 text

オファーの選択ロジックを考える 125 ● また、たとえば次のようなケースではGoogle Playに判定を任 せることはできないので、タグを使ってどのオファーを優先させ るのかをアプリ側で判定する必要がある ○ 季節限定でオファーを提供したい ○ アプリが持つユーザー情報を元に特定のユーザー向けに オファーを提供したい

Slide 126

Slide 126 text

オファーの選択ロジックを考える 126 ● 通常時は一番低い価格のオファーが選択されるようにしてお き、何らかの条件に一致したユーザーにはそのユーザーに対 して特別なオファーが適用されるようにするなど、どのようにオ ファーを選択するのかは予めチームで相談して決めておくとよ い

Slide 127

Slide 127 text

オファーの選択ロジックを考える 127 ● 今回は、Play Billing Libraryのサンプルにもある一番低い価 格のオファーを選択するロジックを例に説明する

Slide 128

Slide 128 text

128 オファーの中で一番低い価格のオファーを探して取得する // オファーの中で一番低い価格のオファーを探して取得する private fun List.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 }

Slide 129

Slide 129 text

129 オファーの中で一番低い価格のオファーを探して取得する // オファーの中で一番低い価格のオファーを探して取得する private fun List.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 }

Slide 130

Slide 130 text

130 オファーの中で一番低い価格のオファーを探して取得する // オファーの中で一番低い価格のオファーを探して取得する private fun List.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 }

Slide 131

Slide 131 text

131 オファーの中で一番低い価格のオファーを探して取得する // オファーの中で一番低い価格のオファーを探して取得する private fun List.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 }

Slide 132

Slide 132 text

132 オファーの中で一番低い価格のオファーを探して取得する // オファーの中で一番低い価格のオファーを探して取得する private fun List.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 }

Slide 133

Slide 133 text

133 オファーの中で一番低い価格のオファーを探して取得する // p1mの基本プランの中の一番価格の低いオファーを取得する val p1mOfferDetails = subscriptionOfferDetailsList .filter { subscriptionOfferDetails -> subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } } .findOffer()

Slide 134

Slide 134 text

134 オファーの中で一番低い価格のオファーを探して取得する // p1mの基本プランの中の一番価格の低いオファーを取得する val p1mOfferDetails = subscriptionOfferDetailsList .filter { subscriptionOfferDetails -> subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } } .findOffer()

Slide 135

Slide 135 text

135 オファーの中で一番低い価格のオファーを探して取得する // p1mの基本プランの中の一番価格の低いオファーを取得する val p1mOfferDetails = subscriptionOfferDetailsList .filter { subscriptionOfferDetails -> subscriptionOfferDetails.offerTags.any { offerTag -> offerTag == "p1m" } } .findOffer()

Slide 136

Slide 136 text

無料期間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が 長いものを優先するなど、提供するオ ファーに応じてユーザーに一番有利 なオファーが選択されるなどの工夫を したほうがよい

Slide 137

Slide 137 text

137 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id( pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails ● ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト ● 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails ● 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ

Slide 138

Slide 138 text

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)

Slide 139

Slide 139 text

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)

Slide 140

Slide 140 text

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しているが、意図しない商品の購入画面が 表示される恐れがあるため、空の場合は購入画面は表示せ ず、アプリ側でエラーとして扱ったほうがよいかもしれない

Slide 141

Slide 141 text

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)

Slide 142

Slide 142 text

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)

Slide 143

Slide 143 text

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)

Slide 144

Slide 144 text

144 購入対象の基本プランとそれに紐づくすべてのオファーの中か ら、どのオファーを提供する(またはオファー無しで提供する)の かを何らかのロジックで決める ProductDetailsのSubscriptionOfferDetailsのリストから、 基本プランに設定したタグ( p1m)を元に基本プランとそれに紐 づくオファーを探す Product Id( pro_plan )を元にProductDetailsを取得 Product IdがproのProductDetails ● ProductDetailsのSubscriptionOfferDetailsのリストに すべての基本プランやそれに紐づくすべてのオファー の情報が含まれる SubscriptionOfferDetailsのリスト ● 購入対象の基本プランとそれに紐づくすべてのオファーが含ま れる SubscriptionOfferDetails ● 購入対象の基本プランとオファーのセットが含まれる SubscriptionOfferDetailsに含まれるofferTokenを使って購入 をリクエストする Play Billing Library 5.0からの購入までの処理の流れ

Slide 145

Slide 145 text

Play Billing Library 5.0の機能をサポートする 145 1. Play Consoleで基本プランやオファーにタグを設定する ○ 基本プランが正しく判別できるように、基本プランのタグは必ず一意な タグを設定しておく 2. オファーの決定ロジックを決める ○ 先に決めておかないとオファーを正しく活用できない ○ たとえばオファーの中で一番低い価格のオファーを自動で選択するな どを予め決めておき、チーム内で合意をとっておく

Slide 146

Slide 146 text

Play Billing Library 5.0の機能をサポートする 146 3. SkuDetailsを使った購入フローの実装から、ProductDetails を使った購入フローの実装に置き換える ○ ProductDetailsから基本プランやオファーを取得し、オ ファーの選択ロジックなどを実装する 4. Skuと名のつく非推奨になったクラスやメソッドが残っていない か確認して、残っていればProductと名のつくものに置き換え る

Slide 147

Slide 147 text

参考資料 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

Slide 148

Slide 148 text

参考資料 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

Slide 149

Slide 149 text

ありがとうございました Taichi Sato / @syarihu Giftmall, Inc. Android Engineer 149