# iOS でサブスクリプションを管理 BillingSDK for iOS で継続支払いを受け付け、エンタイトルメントを管理します。 [BillingSDK for iOS](https://github.com/stripe-samples/billing-ios-sdk)を使用して、iOS アプリで直接、サブスクリプションの決済を受け付け、顧客にサブスクリプションを管理させ、[エンタイトルメント](https://docs.stripe.com/billing/entitlements.md)を管理します。この SDK は、購入ボタンを表示し、[カスタマーポータル](https://docs.stripe.com/customer-management/activate-no-code-customer-portal.md)を表示し、プレミアム機能へのアクセスを制御するためのエンタイトルメントをチェックするための UI コンポーネントを提供します。 ## はじめに BillingSDK for iOS を使用するには、以下のものが必要です。 - [顧客セッション](https://docs.stripe.com/api/customer_sessions.md)を作成するバックエンドサーバー - プライベートプレビューにアクセスできる Stripe アカウント - iOS 15.0 以降および macOS 12.0 以降 - Xcode 15.0 以降 ## 作成する内容 このガイドでは以下の方法を説明します。 - [顧客セッション](https://docs.stripe.com/api/customer_sessions.md)を作成するサーバーエンドポイントの設定 - BillingSDK for iOS のインストールと設定 - サブスクリプション購入のための購入ボタンの表示 - プレミアム機能へのアクセスを制御するためのエンタイトルメントの確認 - サブスクリプション管理のためのカスタマーポータルの表示 - エラー処理とセッション状態の管理 完全なサンプルアプリは [BillingSDK for iOS リポジトリ](https://github.com/stripe-samples/billing-ios-sdk)にあります。 ## バックエンドの設定 [サーバー] 認証済みユーザー用に [顧客セッション](https://docs.stripe.com/api/customer_sessions/create.md)を生成するサーバーエンドポイントを作成します。これらのセッションは、Stripe の Billing API で iOS アプリを安全に認証します。 ### 顧客セッションエンドポイントを作成 エンドポイントは次のことを行う必要があります。 1. ユーザーが認証されていることを確認します。 1. ユーザーの Stripe [顧客 ID](https://docs.stripe.com/api/customers/object.md)を作成または取得します。 1. 必要なコンポーネントを有効にして顧客セッションを作成します。 1. セッションの詳細をアプリに返します。 以下に実装例を示します。 ```javascript import 'dotenv/config'; import express from 'express'; import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || '', { apiVersion: '2025-07-30.basil', }); const app = express(); app.use(express.json()); app.post('/authenticate', async (req, res) => { try { // Replace with your auth; return 401 if the user is not logged in const user = authUser(req); if (!user) { res.status(401).json({ error: { type: 'no_session_present' } }); return; } const customerSession = await stripe.customerSessions.create({ customer: user.stripeCustomerId, components: { buy_button: { enabled: true }, active_entitlements: { enabled: true }, customer_portal: { enabled: true }, } as any, }); res.json({ clientSecret: customerSession.client_secret, expiresAt: customerSession.expires_at, customer: customerSession.customer as string, }); } catch (err) { res.status(500).json({ error: { message: 'Internal server error' } }); } }); app.listen(3000); ``` ### 応答形式 認証に成功した場合、エンドポイントはこれらのフィールドを返す必要があります。 | フィールド | タイプ | 説明 | | -------------- | --- | ------------------------------ | | `clientSecret` | 文字列 | SDK を認証するために使用される顧客セッションシークレット | | `expiresAt` | 数値 | 有効期限タイムスタンプ(エポックからの秒数) | | `customer` | 文字列 | Stripe 顧客 ID | 認証に失敗した場合、HTTP 401 を返し、SDK の未認証状態をトリガーします。 ## SDK のインストールと設定 [iOS] BillingSDK for iOS は、サブスクリプション管理と機能アクセスコントロールのためのネイティブ iOS エクスペリエンスを提供します。 ### SDK をプロジェクトに追加します。 プライベートプレビュー中に、BillingSDK パッケージをインストールします。 1. Xcode でプロジェクトを開く 1. **ファイル** → **パッケージの依存関係の追加** を選択します。 1. **Search or Enter Package URL** フィールドに `https://github.com/stripe-samples/billing-ios-sdk`と入力します。 1. `BillingSDK`を選択し、**Add Package** をクリックします。 ### SDK の設定 BillingSDK のセットアップと認証を処理するために、共有クラスで SDK を初期化します。 ```swift import Foundation import BillingSDK import SwiftUI class BillingManager { static let shared = BillingManager() public let billing: BillingSDK private init() { // Initialize with your publishable key and set how long entitlements can be stale before needing to refresh. let configuration = BillingSDK.Configuration( publishableKey: "pk_test_…", maximumStaleEntitlementsDuration: TimeInterval(60 * 5) ) self.billing = BillingSDK(configuration: configuration) // Set up authentication (see below for implementation) setupCustomerSessionProvider() // Add entitlement change listener (see below for implementation) setupEntitlementListener() } } ``` > SDK は内部的にスレッドセーフを処理するため、どのスレッドからでも安全に SDK のメソッドを呼び出すことができます。 ### 顧客セッションプロバイダーの設定 Billing マネージャーに認証方法を追加します。 ```swift import Foundation import BillingSDK extension BillingManager { func setupCustomerSessionProvider() { billing.setCustomerSessionProvider { [weak self] () async -> UBCustomerSessionDetails? in await self?.fetchCustomerSession() } } private func fetchCustomerSession() async -> UBCustomerSessionDetails? { // Configure request to your backend endpoint guard let url = URL(string: "https://your-backend.example.com/customer-session") else { print("Invalid URL") return nil } var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") // Send any additional headers needed to authenticate the user, for example, an auth token. request.setValue("Bearer \(authToken)", forHTTPHeaderField: "Authorization") do { // Make the request let (data, response) = try await URLSession.shared.data(for: request) guard let httpResponse = response as? HTTPURLResponse else { print("Invalid response format") return nil } switch httpResponse.statusCode { case 200: // Parse the successful response let decoder = JSONDecoder() decoder.dateDecodingStrategy = .secondsSince1970 return try decoder.decode(UBCustomerSessionDetails.self, from: data) case 401: // User not authenticated - SDK will enter unauthenticated state print("Authentication required") return nil default: print("Unexpected status code: \(httpResponse.statusCode)") return nil } } catch { print("Session provider error: \(error.localizedDescription)") return nil } } // Call this method when your user signs out func handleSignOut() async { await billing.reset() // Clear session data and caches } } ``` > BillingSDK は Stripe と認証する必要がある場合、自動的にセッションプロバイダーを呼び出します。プロバイダーが `nil`を返すと、SDK は認証されていない状態になります。 ## サブスクリプション購入ボタンの表示 [iOS] 購入ボタンは、顧客にサブスクリプションを購入させるための組み込み済み UI 要素を提供します。各ボタンは、Stripe アカウント内の特定の商品と価格にリンクされています。 ### ダッシュボードに購入ボタンを作成 [このガイド](https://docs.stripe.com/payment-links/buy-button.md)に従って、ダッシュボードに購入ボタンを作成してください。エンタイトルメントチェック機能を使用する予定の場合、作成した商品に [エンタイトルメント](https://docs.stripe.com/billing/entitlements.md)が添付されていることを確認してください。 ### 購入ボタンの表示 ```swift import SwiftUI import BillingSDK struct SubscriptionView: View { @State private var buyButton: BuyButton? @State private var isLoading = true @State private var errorMessage: String? // Replace with your buy button ID from the Dashboard let buttonId = "buy_btn_1Abcdef123456" var body: some View { VStack { if isLoading { ProgressView("Loading subscription options...") } else if let buyButton = buyButton { // Display the prebuilt buy button UI // Alternatively the BuyButton class also provides all the data to render // a custom UI element. buyButton.view() .frame(height: 56) .padding(.horizontal) } else if let errorMessage = errorMessage { Text("Error: \(errorMessage)") .foregroundColor(.red) Button("Retry") { loadBuyButton() } } } .onAppear { loadBuyButton() } } func loadBuyButton() { isLoading = true errorMessage = nil Task { do { // Fetch the button from Stripe buyButton = try await BillingManager.shared.billing.getBuyButton(id: buttonId) isLoading = false } catch { errorMessage = "Failed to load buy button" isLoading = false } } } } ``` > ユーザーが認証していない場合でも、購入ボタンは機能します。SDK は必要に応じて、購入時に新しい Stripe 顧客を作成します。 ## エンタイトルメントの確認と検証 [iOS] エンタイトルメントを使用すると、顧客のアクティブなサブスクリプションに基づいてプレミアム機能へのアクセスを制御できます。 ### 特定のエンタイトルメントをチェック ユーザーが特定の機能にアクセスできるかどうかを検証します。 ```swift func checkPremiumAccess() async { do { let hasPremiumAccess = try await BillingManager.shared.billing.hasEntitlement(lookupKey: "premium_tier") if hasPremiumAccess { // User has premium access - enable features unlockPremiumFeatures() } else { // User doesn't have access - show upsell showPremiumUpsell() } } catch { showErrorMessage("Couldn't verify entitlements status") } } ``` ### すべての有効なエンタイトルメントを取得 顧客がアクセスできるすべてのエンタイトルメントを取得します。 ```swift func loadUserEntitlements() async { do { // Force refresh ensures we get the latest data from the server let entitlements = try await BillingManager.shared.billing.getActiveEntitlements(forceRefresh: true) // Map entitlements to app features let features = entitlements.map { $0.lookupKey } updateAvailableFeatures(features) } catch { handleError(error) } } ``` ### エンタイトルメントの変更をリッスン エンタイトルメントが変更されたときに通知されるリスナーを設定します。 ```swift import Foundation import BillingSDK extension BillingManager { func setupEntitlementListener() { billing.onEntitlementsChanged { updatedEntitlements in // Update UI based on new entitlements DispatchQueue.main.async { self.updateAppFeatures(based: updatedEntitlements) self.refreshUI() } } } } ``` > セッションが存在しない場合、`getActiveEntitlements()`は空の配列を返します。 ## カスタマーポータルの表示 [iOS] カスタマーポータルでは、顧客はサブスクリプション、決済手段、請求を管理できます。 ### カスタマーポータルの表示 ユーザーがサブスクリプションを管理する必要がある場合にポータルを表示します。 ```swift func manageSubscriptionTapped() { Task { do { let portal = try await BillingManager.shared.billing.getCustomerPortal() // Show portal within your app (recommended) portal.presentCustomerPortal(from: self) } catch { if let billingError = error as? BillingSDKError { switch billingError { case .unauthenticated: // User needs to log in first showLoginPrompt() default: // Handle other errors showErrorMessage("Could not load subscription management") } } else { showErrorMessage("Could not load subscription management") } } } } ``` > カスタマーポータルには、アクティブな認証済みセッションが必要です。ユーザーがログインしていない場合、SDK は`.unauthenticated` エラーをスローします。 ### 外部リダイレクション(オプション) ブラウザでポータルを開くこともできます。 ```swift let portal = try await BillingManager.shared.billing.getCustomerPortal() portal.redirectToCustomerPortal() // Opens in default browser ``` ## セッション管理 [iOS] ### セッション管理 ユーザーがサインアウトしたときに SDK をリセットし、セッションデータとキャッシュをクリアします。 ```swift func signOut() async { // Clear your app's auth state UserDefaults.standard.removeObject(forKey: "auth_token") // Reset the BillingSDK state await BillingManager.shared.billing.reset() // Navigate to login screen showLoginScreen() } ``` ## 実装内容をテストする 開発中は、[サンドボックス API キー](https://docs.stripe.com/keys.md#obtain-api-keys)とサンドボックス顧客 ID を使用して、実際の決済を作成してください。 ### シナリオをテストする これらの一般的なシナリオをテストします。 | シナリオ | ステップ | | -------------- | --------------------------------------------- | | 新規サブスクリプションの購入 | 新規ユーザーに購入ボタンを表示 | | サブスクリプションの管理 | プラン変更にはカスタマーポータルを使用してください。 | | エンタイトルメントの確認 | `hasEntitlement`でプレミアム機能をチェックします。 | | エラー処理 | 無効なキーまたは有効期限切れのセッションでテスト | | サインアウトフロー | `billing.reset()`でキャッシュされたデータがクリアされることを確認します。 | ## See also - [エンタイトルメント](https://docs.stripe.com/billing/entitlements.md) - [カスタマーポータルを設定](https://docs.stripe.com/customer-management/integrate-customer-portal.md)します - [サブスクリプションの仕組み](https://docs.stripe.com/billing/subscriptions/overview.md)