$30 off During Our Annual Pro Sale. View Details »

iOSとAndroidで定期購入の意図しない解約を防ぐ/droidkaigi2023-day1

 iOSとAndroidで定期購入の意図しない解約を防ぐ/droidkaigi2023-day1

DroidKaigi 2023 Day1 「iOSとAndroidで定期購入の意図しない解約を防ぐ」 というテーマで発表しました。 #DroidKaigi 2023/09/14
https://2023.droidkaigi.jp/timetable/493521/

More Decks by 日本経済新聞社 エンジニア採用事務局

Other Decks in Technology

Transcript

  1. DroidKaigi 2023, Electric Eel - 2023/09/14 14:20-15:00
    Ryo Yamazaki & Yuya Sato
    iOSとAndroidで
    定期購入の意図しない解約を防ぐ
    1

    View Slide

  2. Caution
    アプリ内課金はユーザーにとってセンシティブであり、
    販売者の利益に直結する重要なシステムです。
    サービスのリリース前には必ずSandboxで検証してください。
    2

    View Slide

  3. ● Ryo Yamazaki / Android Engineer
    ○ GitHub: @ymnder
    ● Yuya Sato / iOS Engineer
    ○ GitHub: @yutosa261
    Speakers
    3

    View Slide

  4. ● 日本経済新聞社ではiOS / Androidエンジニアの募集中です!!!
    ○ https://hack.nikkei.com/jobs/ios/
    ○ https://hack.nikkei.com/jobs/android/
    We are Hiring :))
    4

    View Slide

  5. ● 非自発的解約を防ぐための機能について解説する
    ○ Grace PeriodとAccount Hold
    ● 非自発的解約を防ぐための実装について説明する
    ○ Google Play ストア(以降Play Store)とApp Storeの仕様差
    ○ アプリとサーバーのAPIについて
    本セッションのスコープ
    5

    View Slide

  6. ● Android
    ○ Google Play Billing Library 6.0の詳細な実装・解説
    ○ Amazonアプリストアのアプリ内課金について
    ● iOS
    ○ StoreKit 2の詳細な実装・解説
    本セッションで扱わないもの
    6

    View Slide

  7. 定期購入の解約とは
    7

    View Slide

  8. 定期購入の解約の種類と原因(Play Store)
    ストアでの解約
    アカウントの削除
    サポートへの解約の申し込み
    Google Play Developer API での解約
    注文管理による定期購入の解約
    自発的解約
    支払いに問題があった(残高の不足やアカ
    ウントの削除など)
    価格変更の確認が行われなかった
    非自発的解約
    8

    View Slide

  9. ● Grace Period
    ○ 支払いが失敗した場合に購読を解約せず、購読状態を一定期間保ち続ける機能
    ■ ユーザーが定期購入失敗時に自動解約されなくなる
    ■ ユーザーに支払い情報の修正を促すことができる
    ● Account Hold
    ○ 支払いの失敗が解決されなかった場合にアカウントを一時停止する機能
    ■ ユーザーは有料機能は使えないが、支払いを修正すればすぐ再開できる
    ■ サービスの利用期間のカウントがリセットされない
    ○ iOSではBilling RetryのうちGrace Periodを除いた期間に相当
    ■ 本セッションでは便宜的にAccount Holdと呼ぶ
    非自発的解約を減らすための機能
    9

    View Slide

  10. 有効な定期購入 Grace Period
    有料機能を維持
    Account Hold
    有料機能を制限
    解約
    Grace PeriodとAccount Hold
    有効な定期購入 Grace Period
    有料機能を維持
    Billing retry state
    有料機能を制限
    解約
    Play Store
    App Store
    10
    支払いに問題が発生 💥
    支払いに問題が発生 💥

    View Slide

  11. Grace PeriodとAccount Holdの効果は高い
    > Developers on Google Play who use a grace period see a 57% higher
    recovery rate from renewal declines.
    > ref:
    https://medium.com/googleplaydev/how-to-win-back-subscribers-who-cancel-9960731adeb
    > For example, account hold has helped developers achieve 8% lower
    involuntary churn and 35% higher payment decline recovery rate
    compared to developers without account hold.
    > ref:
    https://android-developers.googleblog.com/2020/06/new-features-to-acquire-and-retain-subscri
    bers.html
    note: 引用文のBoldは発表者が追加した 11

    View Slide

  12. Grace PeriodとAccount Holdの期間
    Play Store App Store
    Grace Period 3, 7, 14, 30日間 3, 6, 16, 28 日間
    Account Hold 30日間 60日間
    12

    View Slide

  13. Grace PeriodとAccount Holdの期間
    Play Store App Store
    Grace Period 3, 7, 14, 30日間 3, 6, 16, 28 日間
    Account Hold 30日間 60日間
    Play StoreはデフォルトON、App StoreはデフォルトOFF
    13

    View Slide

  14. Grace PeriodとAccount Holdの期間
    Play Store App Store
    Grace Period 3, 7, 14, 30日間 3, 6, 16, 28 日間
    Account Hold 30日間 60日間
    設定がなく常に有効である
    14

    View Slide

  15. ● Play StoreとApp Storeでのそれぞれの実行イメージを紹介する
    ● 説明に使う例の状況設定
    ○ 請求対象期間は1か月
    ○ Grace Period
    ■ Play Store: 14日
    ■ App Store: 16日
    Grace PeriodとAccount Holdの期間例
    15

    View Slide

  16. 16
    正常に購入が更新されているケース
    有効な定期購入
    4/1 5/1
    有効な定期購入
    6/1
    有効な定期購入
    7/1
    有効な定期購入
    請求対象期間は1か月なため、毎月1日から定
    期購入が開始されている

    View Slide

  17. Grace Periodがoffの場合(Play Store)
    有効な定期購入 Account Hold
    (30 days)
    解約
    4/1 5/1
    有効な定期購入
    5/20 6/20
    17
    支払いに問題が発生 💥
    支払いを修正

    View Slide

  18. Grace Periodがoffの場合(Play Store)
    有効な定期購入 Account Hold
    (30 days)
    解約
    4/1 5/1
    有効な定期購入
    5/20 6/20
    新しい定期購入期間となる
    18

    View Slide

  19. Grace Periodがonの場合(Play Store)
    有効な定期購入 Grace Period Account Hold
    (30 days)
    解約
    4/1 5/1 5/14
    有効な定期購入
    5/10 6/1
    19
    支払いを修正
    支払いに問題が発生 💥

    View Slide

  20. Grace Periodがonの場合(Play Store)
    有効な定期購入 Grace Period Account Hold
    (30 days)
    解約
    4/1 5/1 5/14
    有効な定期購入
    5/10 6/1
    Grace Periodの期間が算入
    定期購入期間に変更はない
    20

    View Slide

  21. Grace Periodがoffの場合(App Store)
    有効な定期購入 Billing retry state
    (60days)
    解約
    4/1 5/1
    有効な定期購入
    5/20 6/20
    21
    支払いを修正
    支払いに問題が発生 💥

    View Slide

  22. Grace Periodがonの場合(App Store)
    有効な定期購入 解約
    4/1 5/1 5/16
    有効な定期購入
    5/10 6/1
    Grace Period
    (16 days)
    Billing retry state
    (60 days)
    22
    支払いを修正
    支払いに問題が発生 💥

    View Slide

  23. いざ実装
    23

    View Slide

  24. の前に
    24

    View Slide

  25. 検証環境の整備
    25

    View Slide

  26. 1. ライセンステスターの登録をする
    a. Play Consoleで[設定] > [ライセンス テスト] を選択
    b. メーリング リストからライセンステスターを選択する
    c. メールアドレスを追加する
    2. ライセンステスターアカウントでPlay ストアアプリにログインする
    a. 注意:Play ストアアプリに複数のアカウントでログインしている場合、
    最初にログインしたアカウントがテスト対象となる
    テストができる環境を整える(Play Store)
    26

    View Slide

  27. 3. Play Consoleの基本プランから猶予期間の設定を有効にする
    テストができる環境を整える(Play Store)
    App Storeと異なり、Sandbox
    環境でのみ提供するオプショ
    ンは存在しない
    27

    View Slide

  28. 4. 定期購入後、Play ストアアプリでメインのお支払い方法を「テストカード、常
    に不承認」に更新する
    テストができる環境を整える(Play Store)
    28

    View Slide

  29. テストサイクル(Play Store)
    有効な定期購入 Grace Period Account Hold 解約
    00:00 00:05 00:10
    00:01
    常に不承認を選択
    00:20
    29

    View Slide

  30. テストサイクル(Play Store)
    有効な定期購入 Grace Period Account Hold 解約
    00:00 00:05 00:10
    00:01
    常に不承認を選択
    00:20
    30
    Account Holdまで検証するには
    20分かかる

    View Slide

  31. テストサイクル(Play Store)
    有効な定期購入 Grace Period Account Hold 解約
    00:00 00:05 00:10
    00:01
    常に不承認を選択
    00:20
    Play ストア アプリから通
    知とメールがくる
    Play ストア アプリから通
    知とメールがくる
    31

    View Slide

  32. ● Sandbox環境でのテスト方法を紹介
    ● StoreKitTestというフレームワークを用いることで、App Store Connet
    を介さずテストすることも可能
    ○ WWDC2020: Introducing StoreKit Testing in Xcode
    テストができる環境を整える(App Store)
    32

    View Slide

  33. 1. Sandboxテスターアカウントを作成する
    a. App Store Connectで [ユーザーとアクセ
    ス] > [Sandboxテスター] を選択
    b. メールアドレスなど、必要情報を入力しアカウ
    ント作成
    テストができる環境を整える(App Store)
    33

    View Slide

  34. 2. デバイスでログインする
    a. iPhoneの [設定] > [App Store] を選択
    b. メールアドレス、パスワードを入力しログイン
    テストができる環境を整える(App Store)
    34

    View Slide

  35. 3. 猶予期間の設定
    a. App Store Connectで [請求
    の猶予期間] に移動
    b. 猶予期間を何日にするか設定
    c. サーバー環境を選択することが
    でき、Sandbox環境のみで設
    定可能
    テストができる環境を整える(App Store)
    35

    View Slide

  36. 4. 定期購入後、Sandboxアカウント設定の
    [購入と更新を許可する] をオフにする
    テストができる環境を整える(App Store)
    36

    View Slide

  37. テストのサイクル(App Store)
    有効な定期購入 Grace Period Billing retry state 解約
    00:00 00:05 00:10
    00:01
    [購入と更新を許可する] をオフにする
    00:15
    ● ユーザー単位で更新間隔を変更できる
    ● 更新間隔に応じてGrace PeriodとBilling retry
    stateの時間も変わる
    ● テスト中に契約状態が変更されたことを知らせ
    るメールはこない
    37

    View Slide

  38. 購入ステータスを確認する
    38

    View Slide

  39. ● 購入ステータスを確認するAPIが用意されている
    ○ deprecated: purchases.subscriptions.get
    ■ 2022年5月に新機能がリリースされたがサポートがない
    ○ purchases.subscriptionsv2.get
    ■ すべての機能が移行されているわけではないため、2022 年 5
    月の定期購入変更ガイドを参照すること
    Google Play Developer API (Play Store)
    39

    View Slide

  40. Google Play Developer API (Play Store)
    40

    View Slide

  41. Google Play Developer API (Play Store)
    41
    V1は購入ステータスはレスポンスの値
    を組み合わせて判断していた
    V2は判定が簡潔になった

    View Slide

  42. deprecated: purchases.subscriptions.get
    ● expiryTimeMillis
    ○ Grace Periodは有効な定期購入
    として扱われ延長される
    ● paymentState
    ○ 0は支払い保留中を示す
    ● autoRenewing
    ○ 自動更新されるかどうか
    42

    View Slide

  43. ● subscriptionStateをチェックする
    ○ SUBSCRIPTION_STATE_ACTIVE
    ○ SUBSCRIPTION_STATE_IN_GRACE_PERIOD
    ○ SUBSCRIPTION_STATE_ON_HOLD
    ○ SUBSCRIPTION_STATE_CANCELED
    ○ SUBSCRIPTION_STATE_EXPIRED
    ○ …
    purchases.subscriptionsv2.get
    複雑な判定を行わず
    に済むようになった
    43

    View Slide

  44. ● 購入ステータスを確認するため次のAPIを呼ぶ
    ○ deprecated: verifyReceipt
    ○ Get All Subscription Statuses
    ● StoreKit 2、Get All Subscription Statuses、App Store Server
    Notifications V2の登場により従来のverifyReceiptと状況が変わった
    ○ クライアントでレシートが検証されているため、購入ステータスに集中
    できるようになった
    App Store Server API (App Store)
    44

    View Slide

  45. verifyReceipt (App Store)
    45

    View Slide

  46. verifyReceipt (App Store)
    レシートを検証する
    APIである
    購入ステータスはレ
    スポンスの値を組み
    合わせて判断する
    46

    View Slide

  47. ● is_in_billing_retry_period
    ○ 1はbilling retryの状態を示す
    ● expiration_intent
    ○ 2はBilling errorで支払い情報が有効でな
    い場合を示す
    ● grace_period_expires_date_ms > 現在時刻
    ○ expires_date_msはGrace Periodで延長
    されない
    verifyReceipt (App Store)
    47

    View Slide

  48. Get All Subscription Statuses
    48

    View Slide

  49. Get All Subscription Statuses
    StoreKit 2ではレシートの
    検証をクライアントででき

    49

    View Slide

  50. Get All Subscription Statuses
    購入ステータスを問い合
    わせるAPIとなっている
    50

    View Slide

  51. ● lastTransactionsItem.statusの値をチェックする
    ○ 1: 定期購入は有効である
    ○ 2: 定期購入は有効期限切れである
    ○ 3: 定期購入はAccount Holdである
    ○ 4: 定期購入はGrace Periodである
    ○ …
    Get All Subscription Statuses
    複雑な判定を行わず
    に済むようになった
    51

    View Slide

  52. Server Notification
    52

    View Slide

  53. ● subscriptionNotification.notificationType
    ○ (5)SUBSCRIPTION_ON_HOLD
    ○ (6)SUBSCRIPTION_IN_GRACE_PERIOD
    ● 通知で取得できる情報は一部である
    ○ 必要な場合Google Play Developer APIを呼ぶ
    Real-time developer notifications
    53

    View Slide

  54. ● アプリ > 収益化 > 収益化のセットアップを開きGoogle Play 請求サービ
    スの入力欄にCloud Pub/Subを入力する
    Real-time developer notifications
    54

    View Slide

  55. ● subTypeを見ることで現在の購入ステータスを把握できる
    ○ BILLING_RETRY
    ○ GRACE_PERIOD
    ● アプリ > アプリ情報 > App Storeサーバ通知から設定する
    ○ プロダクション環境とSandbox環境でURLを分けられる
    ○ V1とV2のバージョンを切り替えて検証できる
    App Store Server Notifications V2
    55

    View Slide

  56. App Store Server Notifications V2
    56

    View Slide

  57. アプリのUI/UX
    57

    View Slide

  58. ● Grace Periodを有効にした場合、ストアが自動的にサポートしてくれる機
    能がある
    ● Play Store
    ○ Play ストアアプリから通知とメールが送られてくる
    ● App Store
    ○ App Storeからメールが送られてくる
    アプリの外でのコミュニケーション
    58

    View Slide

  59. ● Play Store
    ○ showInAppMessages
    ● App Store
    ○ StoreKit2 Message API
    アプリの中で支払いを修正できる機能
    59

    View Slide

  60. ● Play Store
    ○ showInAppMessages
    ● App Store
    ○ StoreKit2 Message API
    アプリの中で支払いを修正できる機能
    60
    通常はPlay Store / App Storeの
    アプリを開いて修正を行う必要が
    ある

    View Slide

  61. ● Google Play Billing Library 4.1から追加された
    ○ Grace PeriodとAccount Hold中に1日1回だけお支払い方法を修
    正するUIを表示する
    ○ アプリ内から直接支払いの問題を修正できる
    ● 注意
    ○ 1日1回表示したら再表示されない
    ○ テストカードでも回数制限がかかる
    ○ 判定と表示の機能はまとめて提供されている
    showInAppMessagesを実装する
    61

    View Slide

  62. 実装コード
    val params = InAppMessageParams.newBuilder()
    .addInAppMessageCategoryToShow(InAppMessageParams.
    InAppMessageCategoryId
    .TRANSACTIONAL)
    .build()
    billingClient.showInAppMessages(activity
    , params) { inAppMessageResult ->
    when(inAppMessageResult.
    responseCode) {
    InAppMessageResult.
    InAppMessageResponseCode
    .NO_ACTION_NEEDED -> {
    // The flow has finished and there is no action needed from developers.
    }
    InAppMessageResult.
    InAppMessageResponseCode
    .SUBSCRIPTION_STATUS_UPDATED -> {
    // The subscription status changed. For example, a subscription
    // has been recovered from a suspend state. Developers should
    // expect the purchase token to be returned with this response
    // code and use the purchase token with the Google Play
    // Developer API.
    }
    }
    }
    62

    View Slide

  63. 63

    View Slide

  64. ● WWDC2022で発表された機能
    ● 支払いの問題が発生した際に、アプリ内で支払い方法
    の更新を促すシートが表示
    ● アプリ内で支払いに関する問題を解決できる
    ● アプリ側の対応は不要で、iOS 16.4 / iPadOS 16.4 以
    降のユーザーに表示される
    StoreKit2 Message APIについて
    64

    View Slide

  65. https://developer.apple.com/videos/play/wwdc2022/10007/
    Messageを表示するまでの流れ
    65

    View Slide

  66. ● 基本的にはアプリがフォアグラウンドに入ったタイミング
    ● リスナーを設定することで表示タイミングを遅延することが可能
    ○ UIKitの場合は、display(in:) を使用
    ○ SwiftUIの場合は、DisplayMessageAction を使用
    ● 支払いに関する問題が解消された場合、Messageは表示されない
    Messageが表示されるタイミング
    66

    View Slide

  67. Messageが表示される頻度
    Billing retry interval Message frequency
    1~3日目 24時間毎
    4~16日目 72時間毎
    17~30日目 96時間毎
    31~60日目 120時間毎
    67
    メッセージの表示頻度はだんだん少なくなる

    View Slide

  68. 独自に訴求するUI例
    68

    View Slide

  69. ● アプリの中で修正を行う機能は便利
    ○ しかし訴求できる内容に制限がある
    ● 独自にダイアログやバナーを表示する
    ○ ただ購入ステータスの判定はサー
    バーと連携した実装が必要
    サーバーから契約情報を返しアプリで表示する
    69

    View Slide

  70. ● 独自UIの場合、アプリの中で支払いを修正できないた
    めストアへ誘導する必要がある
    ● Play Store
    ○ https://play.google.com/store/account/su
    bscriptions?sku=your-sub-product-id&pac
    kage=your-app-package
    ● App Store
    ○ https://apps.apple.com/account/billing
    サーバーから契約情報を返しアプリで表示する
    70

    View Slide

  71. まとめ
    「どこまで、何を、実装するか」
    71

    View Slide

  72. ● Step1
    ○ 何もしない。ストアの通知・メールに任せる
    ○ アプリ内では何も表示されない。ユーザーに気が付かれにくい。
    ● Step2
    ○ 公式ライブラリのアプリ内での支払い修正機能を実装する
    ○ シンプルに訴求できる。表示内容や頻度に制限がある。
    ● Step3
    ○ アプリ内で独自のダイアログやバナーを表示する
    ○ 自由に訴求できる。実装コストがかかる。アプリ外での支払い修正となる。
    Grace Period / Account HoldのUI/UX対応
    72
    前提:API側でのハンドリングを行う

    View Slide

  73. 非自発的解約対応やっていこう💪
    73

    View Slide