Slide 1

Slide 1 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 1 株式会社 AbemaTV StoreKit 2によるiOSのア プリ内課金のリニューアル Aug 24th, 2024 AbemaTV 康 建斌

Slide 2

Slide 2 text

AbemaTV, Inc. All Rights Reserved
 自己 紹介 2 康 建斌(コウ ケンヒン) a.k.a ジャンビン ❏ 株式会社サイバーエージェント(2022年中途) ❏ ABEMATV配属 ❏ Product Engineering Div.のiOSリーダー ❏ 主に課金システムの開発を担当する ❏ 中国(山西省)出身 ❏ 趣味:映画とアニメ ❏ X: @kangnuxlion

Slide 3

Slide 3 text

AbemaTV, Inc. All Rights Reserved
 主な内容 3 ❏ ABEMA 紹介 ❏ ABEMAの課金について ❏ 課金システムをリニューアルする背景 ❏ StoreKit 2について ❏ 課金システムのリニューアル ❏ オファー ❏ リニューアル後のリリース ❏ 新商品提供

Slide 4

Slide 4 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 4 ABEMA 紹介

Slide 5

Slide 5 text

AbemaTV, Inc. All Rights Reserved
 ABEMA 紹介 5 テレビ × ビデオのハイブリッド型 24 時間 365 日完全編成型リニア配信と 見逃しや限定コンテンツを 登録不要で好きなタイミングに視聴できるビデオ配信を 楽しむことができます。 100%プロコンテンツ サイバーエージェントとテレビ朝日 それぞれの強みを活かした制作体制で 高品質なコンテンツを配信しています。 多彩なラインナップ 国内唯一の 24 時間編成のニュース専門チャンネルをはじめ、 オリジナルのドラマや恋愛番組、アニメ、スポーツなど、 多彩なジャンルの約 20 チャンネルを 24 時間 365 日放送しています。

Slide 6

Slide 6 text

AbemaTV, Inc. All Rights Reserved
 ABEMA 紹介 6

Slide 7

Slide 7 text

AbemaTV, Inc. All Rights Reserved
 ABEMA 紹介 7

Slide 8

Slide 8 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 8 ABEMAの課金について

Slide 9

Slide 9 text

AbemaTV, Inc. All Rights Reserved
 ABEMAの課金について-自動更新サブスクリプション&消耗型 9 自動更新サブスクリプション 消耗型 プレミアムプラン ペイパービュー

Slide 10

Slide 10 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 10 課金システムをリニューアルする背景 ❏ ビジネス要件 ❏ iOS側の考慮 ❏ バックエンド側の考慮

Slide 11

Slide 11 text

AbemaTV, Inc. All Rights Reserved
 11 課金システムをリニューアルする背景-ビジネス要件 ❏ ビジネス要件 ❏ これまでの単一プランに加えて、今後は複数のプランを提供した い。 ❏ 提供するプラン多様化した上に、簡単に拡張できるシステムを構 築したい。 ❏ Appleで利用できるオファーの仕組みを導入したい。

Slide 12

Slide 12 text

AbemaTV, Inc. All Rights Reserved
 課金システムをリニューアルする背景-iOS側の考慮 12 ❏ iOS側 ❏ iOS 14のサポートが終了し、iOS 15からサポートされている StoreKit 2の導入が可能になる。 ❏ 課金のSLI(Service Level Indicator)を整備したい。 ❏ StoreKit Testingも導入したい。 ※SLI(Service Level Indicator):サービスの運用品質(パフォーマンス)を計測するのに使われる指標

Slide 13

Slide 13 text

AbemaTV, Inc. All Rights Reserved
 課金システムをリニューアルする背景-バックエンド側の考慮 13 ❏ バックエンド側 ❏ App Store Server NotificationをV2へアップデートとイベン トハンドリングの改修

Slide 14

Slide 14 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 14 StoreKit 2について ❏ StoreKit 2とは? ❏ StoreKit 2のここが素晴らしい ❏ トランザクションの管理しやすさ①〜⑤ ❏ トランザクション管理の注意点①〜② ❏ トランザクション管理のAPI検証 ❏ クライアント完結のレシート検証 ❏ 新機能①〜②

Slide 15

Slide 15 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について 15 StoreKit 2とは? ❏ StoreKit 2は、Appleが提供するフレームワークの一つで、アプリ内課金やサブスク リプションの管理を行うためのツールです。StoreKit 2は、iOS 15、macOS Monterey、tvOS 15、およびwatchOS 8から導入されました。 ❏ 主な特徴 ❏ 商品情報や定期購入状態、トランザクション履歴を取得するための新しいAPI。 ❏ サーバーとの通信を確保するための新しい暗号署名検証機能。 ❏ ユーザーの購入体験を改善するための新しいUI。

Slide 16

Slide 16 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について 16 ❏ コード自体がOriginal API for In-App Purchase(a.k.a StoreKit 1)より圧倒的にシンプル! ❏ StoreKit Testingを活用して一部のテストケースに対してユニット テストを実装できる! ❏ 課金処理が理解しやすくなる! ※これから「StoreKit 1」と呼ばれる箇所は、Original API for In-App Purchaseを指し ます。 StoreKit 2でできること: StoreKit 1はiOS 18からdeprecatedに なることが発表されました!!

Slide 17

Slide 17 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-トランザクションの管理しやすさ① 17 プロダクト情報の取得-Product.products(for: [productID]) let appProducts = try await Product.products(for: [productID]) ❏ 説明: ❏ 1行で商品情報の取得が可能になる。 ❏ 取得したStoreKit.Productの中にはあらゆる情報が含まれている。 ❏ プロダクトの課金タイプ、購読状態、価格情報、名前など let request = SKProductsRequest(productIdentifiers: [productID]) request.start() return request.didReceiveResponse .flatMap { response -> Observable in return response.findProduct(productID) } public var didReceiveResponse: Observable { return RxSKProductsRequestDelegateProxy.proxy(for: self) .productResponse }

Slide 18

Slide 18 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-トランザクションの管理しやすさ② 18 プロダクトの購入-Product.purchase(options:) func purchase(_ product: Product) async throws -> Transaction? { let result = try await product.purchase() switch result { ... } } ❏ 説明: ❏ 1メソッド/1行で全ての処理を完結できる、StoreKit1の SKProductsRequestより圧倒的にシンプル。 ❏ Delegateなしの世界! let request = SKProductsRequest(productIdentifiers: [productID]) request.start() request.didReceiveResponse .flatMap { response -> Observable in return response.findProduct(productID) } .flatMap { [weak self] product -> Observable in return Observable.create { [weak self] observer in let disposable = self?.newPurchaseTransactionResult .subscribe(onNext: { ... }) let payment = SKMutablePayment(product: product) self?.paymentQueue.add(payment) return Disposables.create { disposable.dispose() } } } .flatMap { [weak self] transaction -> Observable in self?.loadReceipt() }

Slide 19

Slide 19 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-トランザクションの管理しやすさ③ 19 トランザクションの監視-Transaction.updates for await result in Transaction.updates { do { let transaction = try self.checkVerified(result) // **権限付与などABEMA側の処理を行う** // トランザクションを完了する await transaction.finish() } catch { ... } } ❏ 説明: ❏ アプリ外部(App Store)とアプリ内部の購入が簡単に分けて処理できる。 ❏ アプリ内で購入した時はTransactionが流れない。 ❏ StoreKit1はユーザーが購読した際も問答無用でSKPaymentQueueにTransaction が流れていた、新規購入かどうかの判定が必要だった。 ❏ 注意: ❏ アプリ起動時できる限り早めにlistenする必要がある。 paymentQueue.updatedTransactions .flatMap { transactions -> Observable in Observable.from(transactions) } .flatMap { [weak self] transaction -> Observable in let transactionState = transaction.transactionState let productID = transaction.payment.productIdentifier let isRetrying = transaction.transactionState != .purchasing && !me.newPurchaseTransactions.value.contains(.init(transaction: transaction)) // アプリ内で新規購入かどうかの判定が必要 if isRetrying { ... } else { ... } }

Slide 20

Slide 20 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-トランザクションの管理しやすさ④ 20 購読中トランザクションの取得-Transaction.currentEntitlements for await result in Transaction.currentEntitlements { do { let transaction = try checkVerified(result) // **権限付与などABEMA側の処理を行う** // トランザクションを完了する await transaction.finish() } catch { ... } } ❏ 説明: ❏ 購読中サブスクリプションのトランザクションが簡単に取得できる。 ❏ 用途例: ❏ AppleIDベースのリストアが実現できる。 Bundle.main .appStoreReceiptURL .flatMap { (url) -> Data? in try? Data(contentsOf: url) } .flatMap { $0.base64EncodedString(options: .init(rawValue: 0)) }

Slide 21

Slide 21 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-トランザクションの管理しやすさ⑤ 21 finishしていないトランザクションの取得-Transaction.unfinished for await result in Transaction.unfinished { do { let transaction = try checkVerified(result) // **権限付与などABEMA側の処理を行う** // トランザクションを完了する await transaction.finish() } catch { ... } } ❏ 説明: ❏ finishしていないトランザクションが簡単に取得できる。 ❏ 用途例: ❏ App Storeの決済が完了したが、サーバー側の権限付与処理がまだ終わってい ないトランザクションを取得し、リトライなどを行う。 paymentQueue.transactions .filter { !newPurchaseTransactions.value.contains(.init(transaction: $0)) } \

Slide 22

Slide 22 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-トランザクション管理の注意点① 22 iOS/tvOS15.4未満のバージョンにてTransaction.updatesの不具合 ● Transaction.updates/Transaction.unfinished ❏ 現象:起動時にTransaction.updatesよりfinishしていないトランザクションが流れることが 書いてあるが! ❏ iOS15.4未満のバージョンは流れていない現象がある! ❏ Apple側にも該当現象を修復してリリースノートに書いてある ❏ https://developer.apple.com/documentation/tvos-release-notes/tvos-15_4- release-notes#StoreKit // If your app has unfinished transactions, the updates listener receives them once, immediately after the app launches. https://developer.apple.com/documentation/storekit/transaction/3851206-updates#discussion ❏ 対策:起動時にiOS15.4未満のバージョンに対してTransaction.updatesと Transaction.unfinished両方とも監視するように対策した。

Slide 23

Slide 23 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-トランザクション管理の注意点② 23 Transaction.updatesの不具合 ● Transaction.updates ❏ 現象:Transaction.updatesよりアプリ内で商品を購入した時はTransactionが流れな いと書いてあるが! ❏ AppStoreの購入結果画面が表示されて時間が経つとTransaction.updatesより流 れることがある! // This sequence receives transactions that occur outside of the app, such as Ask to Buy transactions, subscription offer code redemptions, and purchases that customers make in the App Store. It also emits transactions that customers complete in your app on another device. https://developer.apple.com/documentation/storekit/transaction/3851206-updates#discussion ❏ 対策:アプリ内で商品購入する途中でTransaction.updatesより流れた同じ商品IDのトラ ンザクションのハンドリング処理をスキップするように対策した。

Slide 24

Slide 24 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-トランザクション管理のAPI検証 24 SandBox環境でStoreKit 2のAPIがドキュメント通りに動作するかを検証 しました。その結果、特にドキュメントの説明との差異はないようです

Slide 25

Slide 25 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-クライアント完結のレシート検証 25 アプリ側でレシート検証の結果が確認できる func checkVerified(_ result: VerificationResult) throws -> T { switch result { case .unverified: // レシート検証失敗 throw StoreError.failedVerification case .verified(let safe): // レシート検証成功 return safe } } ❏ レシートの検証ができるようになりましたが、ABEMAでは従来と同じく サーバー側でレシートを検証する方針で対応し、jwsRepresentationを サーバー側へ送信することで実現できる。

Slide 26

Slide 26 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-新機能① 26 アプリ内にユーザーへの返金動線を用意できる try await Transaction.beginRefundRequest(for: id, in: scene) ❏ StoreKit 2からアプリ側に返金導線の用 意ができて、問い合わせとガイドライン など経由しなくても返金できるように なった。

Slide 27

Slide 27 text

AbemaTV, Inc. All Rights Reserved
 StoreKit 2について-新機能② 27 appAccountTokenでユーザーを一意に特定できる。 let appAccountToken = <# Generate an app account token. #> let purchaseResult = try await product.purchase(options: [ .appAccountToken(appAccountToken) ]) ❏ appAccountToken(UUID型)を活用してどのユーザーでの購入がはっき り把握できる。

Slide 28

Slide 28 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 28 課金システムのリニューアル ❏ StoreKit 1からStoreKit 2へ入れ替え ❏ 課金フローのリニューアル ❏ 従来のサブスクリプション課金フロー ❏ 新たなサブスクリプション課金フロー ❏ 汎用的なサブスクリプション登録ページを提供する ❏ 従来のサブスクリプション登録ページ ❏ 新たなサブスクリプション登録ページ

Slide 29

Slide 29 text

AbemaTV, Inc. All Rights Reserved
 課金システムのリニューアル-StoreKit 1からStoreKit 2へ入れ替え 29 ❏ AppStoreと通信処理をStoreKit 1からStoreKit 2にアップグレードした。

Slide 30

Slide 30 text

AbemaTV, Inc. All Rights Reserved
 課金システムのリニューアル-従来のサブスクリプション課金フロー 30 現在のサブスクリプ ション状態確認 ユーザー情報取得 AppStore経由購入 レシート検証 権限チェック ❏ AppStore経由して課金を行う前と課金後に複数APIを叩く 必要がある。 ❏ リトライフローがなくて、サーバーエラーより購入失敗す る場合、再度購入・復元ボタンをタップする必要がある。 購入完了 購入ボタンタップ

Slide 31

Slide 31 text

AbemaTV, Inc. All Rights Reserved
 課金システムのリニューアル-新たなサブスクリプション課金フロー 31 レシート処理API AppStore経由購入 レシート処理API ❏ AppStore経由して課金を行う前と課金後に叩くABEMA サーバー側の複数APIを一本化にした。 ❏ 購入前に購入可否のチェック ❏ リトライ必要するかのチェック ❏ 購入後のレシート検証と権限付与 ❏ 復元対象の確認など ❏ リトライフローも合わせて用意して、バックグラウンドか らフォアグラウンドになるなどの場合にリトライを行って ユーザーの購入が早めに反映できるようになった。 購入完了 購入ボタンタップ

Slide 32

Slide 32 text

AbemaTV, Inc. All Rights Reserved
 汎用的なサブスクリプション登録ページを提供する-従来のサブスクリプショ ン登録ページ 32 ❏ タイトルが固定されており、プレミアムプランしか購入できない。 ❏ 購入ボタンの文言が固定されており、異なる価格が表示できない。 ❏ オファーを提供する場合、都度実装が必要になる。 ❏ 注意文言なども固定されている。

Slide 33

Slide 33 text

AbemaTV, Inc. All Rights Reserved
 汎用的なサブスクリプション登録ページを提供する-新たなサブスクリプショ ン登録ページ 33 ❏ 画像やタイトル、注意文言を入稿でき、複数のプランに対応可能な汎用 性がある。 ❏ プラン情報も入稿により動的に変化し、柔軟に対応できる。 ❏ オファーの提供が可能で、動的な変更にも対応できる。 ❏ ユーザー購読情報に基づいてリロード機能を付与し、二重課金をある程 度防止できる。 ❏ 入稿によって開放時間をコントロールでき、開放と閉じるを自由に操作 できる。

Slide 34

Slide 34 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 34 オファー ❏ お試しオファー ❏ プロモーションオファー ※オファーコードは適用するCaseがないため割愛した。

Slide 35

Slide 35 text

AbemaTV, Inc. All Rights Reserved
 オファー-お試しオファー 35 ❏ サブスクリプション単位で1件だけ有効なオファー設定が可能です。 ❏ サブスクリプショングループごとに1度だけお試しオファーが適用可能で す。 ❏ 適用条件はApple側に判定するため、購入Request(product.purchase)にて オファーの指定が必要なし。 ❏ 同じサブスクリプショングループにユーザーが購読中のサブスクリプション があれば適用できない。 ❏ 3種類のタイプでの提供が可能です。 ❏ 都度払い、前払い、無料

Slide 36

Slide 36 text

AbemaTV, Inc. All Rights Reserved
 オファー-お試しオファー 36 お試しオファー適用条件を判定する let fetchProductResult = await fetchProduct(productID: productID) switch fetchProductResult { case let .success(product): // 商品情報からお試しオファーの有無をチェックする。 guard let subscription = product.subscription, subscription.introductoryOffer != nil else { return false } // 購読中のサブスクリプションがある場合、お試しオファーは適用できない。 // 購読中のサブスクリプションがない場合、 // `isEligibleForIntroOffer`の値に基づいてお試しオファーの適用可否を判断する。 let groupID = subscription.subscriptionGroupID guard await getCurrentTransaction(groupID: groupID) == nil else { return false } return await Product.SubscriptionInfo.isEligibleForIntroOffer(for: groupID) case let .failure(error): throw error }

Slide 37

Slide 37 text

AbemaTV, Inc. All Rights Reserved
 オファー-プロモーションオファー 37 ❏ サブスクリプション単位で同時的に10件有効なオファー設定が可能です。 ❏ アプリ単位でサブスクリプション購読経験がないと適用できない。 ❏ サービス側の判定で何度でも提供が可能です。 ❏ 購入Request(product.purchase)にてオファーID指定が必要です。 ❏ 3種類のタイプでの提供が可能です。 ❏ 都度払い、前払い、無料

Slide 38

Slide 38 text

AbemaTV, Inc. All Rights Reserved
 オファー-プロモーションオファー 38 プロモーションオファー適用条件を判定する // 該当ユーザーがABEMAでサブスクリプションを一度も購入したことがない場合、 // プロモーションオファーを適用する権限がなし let allTransactions = await getAllTransactions().filter(\.isSubscription) guard !allTransactions.isEmpty else { return false } let fetchProductResult = await fetchProduct(productID: productID) switch fetchProductResult { case let .success(product): guard let subscription = product.subscription else { return false } // 該当商品のプロモーションに`OfferID`が見つからない場合、 // プロモーションオファーを適用する権限がなし return subscription.promotionalOffers.compactMap(\.id).contains(offerID) case let .failure(error): throw error }

Slide 39

Slide 39 text

AbemaTV, Inc. All Rights Reserved
 オファー-プロモーションオファー 39 プロモーションオファー利用して商品購入する。 let options: [Product.PurchaseOption] = { [.promotionalOffer( offerID: offer.offerID, keyID: offer.keyID, nonce: offer.nonce, signature: offer.signature, timestamp: offer.timestamp )] }() let result = try await product.purchase(options: options) ❏ 注意①:署名が生成されてから24時間のみ有効する。 ❏ 注意②:購入リクエストにつき1度きり有効です、購入失敗になったら署名 が失効になり再生成が必要になる。 ❏ 対策:購入直前にオファー署名を再生成し、取得するようにする。

Slide 40

Slide 40 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 40 リニューアル後のリリース ❏ 課金のSLIの整備 ❏ リリース戦略 ❏ リリース実施

Slide 41

Slide 41 text

AbemaTV, Inc. All Rights Reserved
 リニューアル後のリリース-課金のSLI整備 41 ❏ なぜSLI計測が必要か? ❏ アプリの運用品質を日々観測し、エラーなどが発生した際に早期 に検知したい。 ❏ AppleやGoogleなどとのやりとりを含む様々な事情により、バッ クエンドから見るとブラックボックスになってしまう。

Slide 42

Slide 42 text

AbemaTV, Inc. All Rights Reserved
 リニューアル後のリリース-課金のSLI整備 42 enum SLIAttributeKey { // サブスクリプション商品 ID static let productID = "productID" // Abemaサーバー側持っているプランの ID static let planID = "planID" // サブスクリプショングループ ID static let groupID = "groupID" // 購入・復元・リトライ区別用のタイプ static let actionType = "actionType" // オファーID static let offerID = "offerID" // 結果タイプ static let resultType = "resultType" // 結果詳細 static let resultDescription = "resultDescription" // end時のサーバーレスポンス static let responseDescription = "responseDescription" // failure時のエラー詳細 orサーバーレスポンス static let errorDescription = "errorDescription" } ❏ 既存: ❏ 一部分不具合があり、正しい数値が得られない。 ❏ 失敗する時の詳細情報もほとんど送信していない。 ❏ 新規: ❏ New Relicへ送信処理をリニューアルした。 attributes: [:]

Slide 43

Slide 43 text

AbemaTV, Inc. All Rights Reserved
 リニューアル後のリリース-リリース戦略 43 ❏ 通常の機能リリースへ影響しないために、専用のリリーススケジュールを用 意し、SLIの様子を見ながら手動リリースを行いました。 ❏ App Store Connect経由して「段階的リリース」でリリースを行った。 ❏ リリース自体を既存状態に切り戻せるように、Feature Flagも利用した。

Slide 44

Slide 44 text

AbemaTV, Inc. All Rights Reserved
 リニューアル後のリリース-リリース実施 44 ❏ New Relicに専用の DashBoardを用意して課金 起因の障害監視でを行っ た。 ❏ SLI整備でエラー詳細がすぐ に気づくように対応した。 ❏ 特に異常なしでリリースし ました、今までも特に大き な問題がなさそうです!

Slide 45

Slide 45 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 45 新商品提供 ❏ 新商品構成 ❏ 商品作成 ❏ 販売開始 ❏ 商品入稿注意点①〜②

Slide 46

Slide 46 text

AbemaTV, Inc. All Rights Reserved
 46 ● 提供するサービスごとにサブスクリプショングループを定義した。 ABEMA de A ABEMA de B ABEMAプレミアム 期間: 1ヶ月 レベル: 1 期間: 1ヶ月 レベル: 1 期間: 1年 レベル: 1 期間: 1ヶ月 レベル: 1 新商品提供-新商品構成

Slide 47

Slide 47 text

AbemaTV, Inc. All Rights Reserved
 新商品提供-商品作成 47 ❏ App Store Connect側で商品を作成する。

Slide 48

Slide 48 text

AbemaTV, Inc. All Rights Reserved
 新商品提供-商品作成 48 ❏ ABEMAの管理画面で商品を作成する。

Slide 49

Slide 49 text

AbemaTV, Inc. All Rights Reserved
 新商品提供-販売開始 49 ❏ 予定の販売日時になると、アプリ側でサブスクリプション登録ページが表 示され、販売が開始できるようになる。 ❏ 販売開始後に致命的な問題が発生した場合でも、サブスクリプション登録 ページを閉じて販売を停止することが可能です。

Slide 50

Slide 50 text

AbemaTV, Inc. All Rights Reserved
 新商品提供-商品入稿注意点① 50 サマータイムの影響で、入稿の反映時間が日本時間より1時間早まる。 ❏ 現象:あるオファーをxx日まで有効に設定した場合、サマータイム中は前 日の23:00に失効してしまう。 ❏ 対策:オファーが失効する時間を正しい時間で予告する必要がある。

Slide 51

Slide 51 text

AbemaTV, Inc. All Rights Reserved
 新商品提供-商品入稿注意点② 51 自動更新サブスクリプションの価格は為替レートに基づいて自動的に更 新されない。 ❏ 現象:円安や円高になったとしても、海外の自動更新サブスクリプション の価格は為替レートに基づいて自動的に更新されない。 ❏ 対策:地域ごとに自動更新サブスクリプションの価格を策定し、必要に応 じて為替レートに合わせて価格調整を行う必要がある

Slide 52

Slide 52 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 52 StoreKit2へ移行することで課金実装が 簡潔になり、サブスクリンプションをよ り効果的に管理することができます! ぜひ、StoreKit 2の世界へお越し下さ い!

Slide 53

Slide 53 text

AbemaTV, Inc. All Rights Reserved
 AbemaTV, Inc. All Rights Reserved
 53 以上になります。 ご清聴、ありがとうございました!