# アプリ内での決済を受け付け Payment Element を使用して、iOS、Android、または React Native アプリでカスタマイズされた決済導入を構築します。 Payment Element はカスタマイズ可能なコンポーネントであり、アプリの任意の画面に追加できる決済手段のリストを表示します。顧客がリスト内の決済手段を操作すると、このコンポーネントは決済詳細を収集する個別の下部シートを開きます。 > #### Accounts v2 API のサポート > > Payment Sheet は *顧客設定のアカウント* (Account configurations represent role-based functionality that you can enable for accounts, such as merchant, customer, or recipient) をサポートしていません。`Customer` オブジェクトのみをサポートしています。 # 決済を受け付ける > This is a 決済を受け付ける for when platform is ios and type is payment. View the full page at https://docs.stripe.com/payments/mobile/accept-payment-embedded?platform=ios&type=payment. PaymentIntent フローを使用すると、アプリで決済を作成できます。この導入では、Payment Element を表示して、*PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成し、アプリで決済を確定します。 ## Stripe を設定する [サーバー側] [クライアント側] ### サーバー側 この組み込みは、Stripe API と通信するサーバー上にエンドポイントを必要とします。サーバーから Stripe API にアクセスするには、次のように Stripe の公式ライブラリーを使用します。 #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### クライアント側 [Stripe iOS SDK](https://github.com/stripe/stripe-ios) はオープンソースです。[詳細なドキュメントが提供されており](https://stripe.dev/stripe-ios/index.html)、iOS 13 以降をサポートするアプリと互換性があります。 #### Swift Package Manager SDK をインストールするには、以下のステップに従います。 1. Xcode で、**File (ファイル)** > **Add Package Dependencies… (パッケージ依存関係を追加)** を選択し、リポジトリー URL として `https://github.com/stripe/stripe-ios-spm` を入力します。 1. [リリースページ](https://github.com/stripe/stripe-ios/releases)から最新のバージョン番号を選択します。 1. **StripePaymentSheet** 製品を[アプリのターゲット](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app)に追加します。 #### CocoaPods 1. まだインストールしていない場合は、[CocoaPods](https://guides.cocoapods.org/using/getting-started.html) の最新バージョンをインストールします。 1. 既存の [Podfile](https://guides.cocoapods.org/syntax/podfile.html) がない場合は、以下のコマンドを実行して作成します。 ```bash pod init ``` 1. この行を `Podfile` に追加します。 ```podfile pod 'StripePaymentSheet' ``` 1. 以下のコマンドを実行します。 ```bash pod install ``` 1. これ以降は、Xcode でプロジェクトを開く際に、`.xcodeproj` ファイルではなく、必ず `.xcworkspace` ファイルを使用するということを忘れないでください。 1. 今後、SDK の最新バージョンに更新するには、以下を実行します。 ```bash pod update StripePaymentSheet ``` #### Carthage 1. まだインストールしていない場合は、[Carthage](https://github.com/Carthage/Carthage#installing-carthage) の最新バージョンをインストールします。 1. この行を `Cartfile` に追加します。 ```cartfile github "stripe/stripe-ios" ``` 1. [Carthage のインストール手順](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos)に従います。必ず、[こちら](https://github.com/stripe/stripe-ios/tree/master/StripePaymentSheet/README.md#manual-linking)にリストされている必要なフレームワークのすべてを埋め込んでください。 1. 今後、SDK の最新バージョンに更新するには、以下のコマンドを実行します。 ```bash carthage update stripe-ios --platform ios ``` #### 手動のフレームワーク 1. Stripe の [GitHub リリースページ](https://github.com/stripe/stripe-ios/releases/latest)に移動して、**Stripe.xcframework.zip** をダウンロードして解凍します。 1. **StripePaymentSheet.xcframework** を、Xcode プロジェクトの **General (一般) ** 設定の **Embedded Binaries (埋め込みバイナリー)** セクションにドラッグします。**Copy items if needed (必要に応じてアイテムをコピーする)** を必ず選択してください。 1. [こちら](https://github.com/stripe/stripe-ios/tree/master/StripePaymentSheet/README.md#manual-linking)にリストされている必要なフレームワークのすべてに対して、ステップ 2 を繰り返します。 1. 今後、Stripe の SDK の最新バージョンに更新するには、ステップ 1 から 3 を繰り返します。 > SDK の最新リリースおよび過去バージョンの詳細については、GitHub の [Releases (リリース)](https://github.com/stripe/stripe-ios/releases) ページをご覧ください。リポジトリの[リリースをウォッチ](https://help.github.com/en/articles/watching-and-unwatching-releases-for-a-repository#watching-releases-for-a-repository)して、新しいリリースの公開時に通知を受け取ることも可能です。 また、SDK が Stripe への API コールを実行できるように、[公開可能キー](https://dashboard.stripe.com/apikeys)を設定する必要もあります。開始するには、導入中にクライアント側で公開可能キーをハードコード化できますが、本番環境ではサーバーから公開可能キーを取得します。 ```swift // Set your publishable key: remember to change this to your live publishable key in production // See your keys here: https://dashboard.stripe.com/apikeys STPAPIClient.shared.publishableKey = "<>" ``` ## 支払い方法を有効にする [支払い方法の設定](https://dashboard.stripe.com/settings/payment_methods)を表示して、サポートする支払い方法を有効にします。*PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成するには、少なくとも 1 つは支払い方法を有効にする必要があります。 多くの顧客から決済を受け付けられるよう、Stripe では、カードやその他一般的な決済手段がデフォルトで有効になっていますが、ビジネスや顧客に適した追加の決済手段を有効にすることをお勧めします。プロダクトと決済手段のサポートについては[決済手段のサポート](https://docs.stripe.com/payments/payment-methods/payment-method-support.md)を、手数料については[料金体系ページ](https://stripe.com/pricing/local-payment-methods)をご覧ください。 ## 支払いの詳細を収集する [クライアント側] Embedded Mobile Payment Element は、ネイティブモバイルアプリの決済ページに配置するように設計されています。この Element には決済手段の一覧が表示され、アプリのデザインに合わせてカスタマイズできます。 顧客が**カード**行をタップすると、決済手段の詳細を入力できる画面が表示されます。画面にデフォルトで**続行**というボタンが表示され、タップすると画面が閉じます。これにより、顧客は決済フローで支払いを完了できます。 ![Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/ios-embedded.b5867c116d537ffcb920dd80ebdfc741.png) また、続行するのではなく、すぐに支払いを完了するようにボタンを設定することもできます。これを行うには、ガイドに沿って手順を実行した後で、[このステップ](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#embedded-let-customer-pay-immediately)を完了します。 #### UIKit ### Payment Element を初期化する `create` を呼び出し、`EmbeddedPaymentElement.Configuration` と [PaymentSheet.IntentConfiguration](https://github.com/stripe/stripe-ios/blob/master/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetIntentConfiguration.swift) を使用して EmbeddedPaymentElement をインスタンス化します。 Configuration オブジェクトには、`returnURL` のような、決済間で変更されない EmbeddedPaymentElement の汎用設定オプションが含まれます。`IntentConfiguration` オブジェクトには、金額や通貨などの特定の決済に関する詳細と、`confirmationTokenConfirmHandler` コールバックが含まれます。今のところ、その実装は空のままにしておきます。初期化に成功したら、`presentingViewController` と `delegate` プロパティを設定します。 ```swift import StripePaymentSheet class MyCheckoutVC: UIViewController { func createEmbeddedPaymentElement() async throws -> EmbeddedPaymentElement { let intentConfig = PaymentSheet.IntentConfiguration( mode: .payment(amount: 1099, currency: "USD") ) { [weak self] confirmationToken in return await self?.handleConfirmationToken(confirmationToken) } var configuration = EmbeddedPaymentElement.Configuration() configuration.returnURL = "your-app://stripe-redirect" // Use the return url you set up in the previous step let embeddedPaymentElement = try await EmbeddedPaymentElement.create(intentConfiguration: intentConfig, configuration: configuration) embeddedPaymentElement.presentingViewController = self embeddedPaymentElement.delegate = self return embeddedPaymentElement } func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // You'll implement this in the "Confirm the payment" section below } } ``` ### Payment Element ビューを追加する EmbeddedPaymentElement が正常に初期化されたら、そのビューを決済 UI に配置します。 > ビューは固定サイズではなく、最初にレンダリングされた後に高さが変わる可能性があるため、UIScrollView などのスクロール可能なビュー内に含める必要があります。 ```swift class MyCheckoutVC: UIViewController { // ... private(set) var embeddedPaymentElement: EmbeddedPaymentElement? private lazy var checkoutButton: UIButton = { let checkoutButton = UIButton(type: .system) checkoutButton.backgroundColor = .systemBlue checkoutButton.layer.cornerRadius = 5.0 checkoutButton.clipsToBounds = true checkoutButton.setTitle("Checkout", for: .normal) checkoutButton.setTitleColor(.white, for: .normal) checkoutButton.translatesAutoresizingMaskIntoConstraints = false checkoutButton.isEnabled = embeddedPaymentElement?.paymentOption != nil checkoutButton.addTarget(self, action: #selector(didTapConfirmButton), for: .touchUpInside) return checkoutButton }() // ... @objc func didTapConfirmButton() { // You'll implement this in the "Confirm the payment" section below } override func viewDidLoad() { super.viewDidLoad() Task { @MainActor in do { // Create a UIScrollView let scrollView = UIScrollView() scrollView.translatesAutoresizingMaskIntoConstraints = false self.view.addSubview(scrollView) // Create the Payment Element let embeddedPaymentElement = try await createEmbeddedPaymentElement() embeddedPaymentElement.delegate = self embeddedPaymentElement.presentingViewController = self self.embeddedPaymentElement = embeddedPaymentElement // Add its view to the scroll view scrollView.addSubview(embeddedPaymentElement.view) // Add your own checkout button to the scroll view scrollView.addSubview(checkoutButton) // Set up layout constraints embeddedPaymentElement.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), embeddedPaymentElement.view.topAnchor.constraint(equalTo: scrollView.topAnchor), embeddedPaymentElement.view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), embeddedPaymentElement.view.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), checkoutButton.topAnchor.constraint(equalTo: embeddedPaymentElement.view.bottomAnchor, constant: 4.0), checkoutButton.leadingAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.leadingAnchor, constant: 4.0), checkoutButton.trailingAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.trailingAnchor, constant: -4.0), ]) } catch { // Handle view not being added to view } } } } ``` この時点で、アプリを実行して、Embedded Mobile Payment Element を確認できます。 ### 高さの変更に対応する EmbeddedPaymentElement のビューは、サイズが大きくなり、または小さくなる可能性があり、ビューのレイアウトに影響を与える可能性があります。 高さの変更に対応するには、`embeddedPaymentElementDidUpdateHeight` デリゲートメソッドを実装します。EmbeddedPaymentElement のビューは、高さを更新するアニメーションブロック内でこのメソッドを呼び出します。実装では、高さ変更のアニメーションをスムーズに行うために、EmbeddedPaymentElement のビューを含むスクロールビューで `setNeedsLayout()` と `layoutIfNeeded()` を呼び出すことが想定されます。 ```swift extension MyCheckoutVC: EmbeddedPaymentElementDelegate { func embeddedPaymentElementDidUpdateHeight(embeddedPaymentElement: StripePaymentSheet.EmbeddedPaymentElement) { // Handle layout appropriately self.view.setNeedsLayout() self.view.layoutIfNeeded() } } ``` ビューが高さの変更に適切に応答することをテストすることを推奨します。これを行うには、`testHeightChange()` を EmbeddedPaymentElement 上で呼び出し、エレメント内の同意書の表示と非表示をシミュレートします。`testHeightChange()` を呼び出した後、スクロールビューがスムーズに調整されることを確認してください。 ```swift class MyCheckoutVC: UIViewController { override func viewDidLoad() { // This is only for testing purposes: #if DEBUG Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in Task { @MainActor in self?.embeddedPaymentElement?.testHeightChange() } } #endif } } ``` ### (任意)選択した支払いオプションを表示する ラベル (「····4242」など)、画像 (VISA ロゴなど)、UI に表示する請求先情報など、顧客が選択した決済オプションの詳細にアクセスする必要がある場合は、EmbeddedPaymentElement の `paymentOption` プロパティを使用します。 `paymentOption` が変更されたときに通知を受けるには、`embeddedPaymentElementDidUpdatePaymentOption` デリゲートメソッドを実装します。 ```swift extension MyCheckoutVC: EmbeddedPaymentElementDelegate { func embeddedPaymentElementDidUpdatePaymentOption(embeddedPaymentElement: EmbeddedPaymentElement) { print("The payment option changed: \(embeddedPaymentElement.paymentOption)") checkoutButton.isEnabled = embeddedPaymentElement.paymentOption != nil } } ``` ### (任意)支払いの詳細を更新する 顧客が決済情報を変更する操作 (割引コードの適用など) を実行したときに、`update` メソッドを呼び出して `EmbeddedPaymentElement` インスタンスを更新し、新しい値を反映します。Apple Pay や Google Pay など、一部の決済手段では UI に金額が表示されるため、常に正確で最新の状態を維持するようにしてください。 更新コールが完了したら、UI を更新します。更新コールにより、顧客が現在選択している決済オプションが変更される場合もあります。 ```swift extension MyCheckoutVC { func update() { Task { @MainActor in var updatedIntentConfig = oldIntentConfig // Update the amount to reflect the price after applying the discount code updatedIntentConfig.mode = PaymentSheet.IntentConfiguration.Mode.payment(amount: 999, currency: "USD") let result = await embeddedPaymentElement?.update(intentConfiguration: updatedIntentConfig) switch result { case .canceled, nil: // Do nothing; this happens when a subsequent `update` call cancels this one break case .failed(let error): // Display error to user in an alert, let users retry case .succeeded: // Update your UI in case the payment option changed } } } } ``` ### 支払いを確定する 顧客が決済ボタンをタップしたら、`embeddedPaymentElement.confirm()` を呼び出して支払いを完了します。確定する際は、ユーザーの操作を必ず無効にしてください。 ```swift extension MyCheckoutVC { @objc func didTapConfirmButton() { Task { @MainActor in guard let embeddedPaymentElement else { return } self.view.isUserInteractionEnabled = false // Disable user interaction, show a spinner, and so on before calling confirm. let result = await embeddedPaymentElement.confirm() switch result { case .completed: // Payment completed - show a confirmation screen. case .failed(let error): self.view.isUserInteractionEnabled = true // Encountered an unrecoverable error. You can display the error to the user, log it, and so on. case .canceled: self.view.isUserInteractionEnabled = true // Customer canceled - you should probably do nothing. break } } } } ``` 次に、`PaymentSheet.IntentConfiguration` に渡した `confirmationTokenConfirmHandler` コールバックを実装し、サーバーにリクエストを送信します。サーバーは PaymentIntent を作成し、Client Secret を返します。このプロセスについては、[PaymentIntent の作成](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#submit-payment) を参照してください。 リクエストが返されたら、サーバー応答のクライアントシークレットを返すか、エラーをスローします。EmbeddedPaymentElement は、クライアントシークレットを使用して PaymentIntent を確認するか、ローカライズされたエラーメッセージを UI に表示します ([errorDescription](https://developer.apple.com/documentation/foundation/localizederror/2946895-errordescription) または [localizedDescription](https://developer.apple.com/documentation/foundation/nserror/1414418-localizeddescription))。確認が完了すると、EmbeddedPaymentElement は使用できなくなります。代わりに、ユーザーを領収書画面などに誘導します。 ```swift extension MyCheckoutVC { func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // Make a request to your own server. Pass `confirmationToken.stripeId` if using server-side confirmation, and return the client secret or throw an error. let myServerClientSecret = try await fetchIntentClientSecret(...) } } ``` #### SwiftUI ### Payment Element を初期化する `load` を呼び出し、`EmbeddedPaymentElement.Configuration` と [PaymentSheet.IntentConfiguration](https://github.com/stripe/stripe-ios/blob/master/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetIntentConfiguration.swift) を指定して EmbeddedPaymentElementViewModel を読み込みます。 Configuration オブジェクトには、`returnURL` のような、決済間で変更されない EmbeddedPaymentElement の汎用設定オプションが含まれます。`IntentConfiguration` オブジェクトには、金額や通貨などの特定の決済に関する詳細と、`confirmationTokenConfirmHandler` コールバックが含まれます。今のところ、その実装は空のままにしておきます。 ```swift import SwiftUI import StripePaymentSheet struct MyEmbeddedCheckoutView: View { // Store an `EmbeddedPaymentElementViewModel` as a `@StateObject` @StateObject var embeddedViewModel = EmbeddedPaymentElementViewModel() var body: some View { ScrollView { // Empty scroll view for now } .task { do { if !embeddedViewModel.isLoaded { // Load the view model with your configuration try await loadEmbeddedViewModel() } } catch { // On load failure, implement retry logic (automatic retry or user-triggered through UI). } } } private func loadEmbeddedViewModel() async throws { let intentConfiguration = PaymentSheet.IntentConfiguration( mode: .payment(amount: 1099, currency: "USD") ) { confirmationToken in try await self.handleConfirmationToken(confirmationToken) } var configuration = EmbeddedPaymentElement.Configuration() configuration.returnURL = "your-app://stripe-redirect" // Use the return url you set up in the previous step try await embeddedViewModel.load(intentConfiguration: intentConfiguration, configuration: configuration) } func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // You'll implement this in the "Confirm the payment" section below } } ``` ### Payment Element ビューを追加する EmbeddedPaymentElementViewModel が正常にロードされたら、決済 UI に EmbeddedPaymentElementView を追加します。 > ビューは固定サイズではなく、最初にレンダリングされた後に高さが変わる可能性があるため、ScrollView などのスクロール可能なビュー内に含める必要があります。 ```swift struct MyEmbeddedCheckoutView: View { var body: some View { ScrollView { // isLoaded becomes true only after a successful call to `embeddedViewModel.load()` if embeddedViewModel.isLoaded { EmbeddedPaymentElementView(viewModel: embeddedViewModel) } } } } ``` この時点で、アプリを実行して、Embedded Mobile Payment Element を確認できます。 ### 高さの変更に対応する EmbeddedPaymentElementView のサイズが大きくなり、または小さくなると、ビューのレイアウトに影響を与える可能性があります。EmbeddedPaymentElementView は、ScrollView 内にある場合、これを自動的に処理します。 ビューが高さの変更に適切に応答することをテストすることを推奨します。これを行うには、`testHeightChange()` を EmbeddedPaymentElementViewModel 上で呼び出し、エレメント内の同意書の表示と非表示をシミュレートします。`testHeightChange()` を呼び出した後、スクロールビューがスムーズに調整されることを確認してください。 ```swift struct MyEmbeddedCheckoutView: View { var body: some View { ScrollView { if embeddedViewModel.isLoaded { EmbeddedPaymentElementView(viewModel: embeddedViewModel) // For testing only #if DEBUG Button("Test height change") { embeddedViewModel.testHeightChange() } #endif } } } } ``` ### (任意)選択した支払いオプションを表示する ラベル (「····4242」など)、画像 (VISA ロゴなど)、UI に表示する請求先情報など、顧客が選択した決済オプションの詳細にアクセスする必要がある場合は、EmbeddedPaymentElementViewModel の `paymentOption` プロパティを使用します。顧客が、フォーム画面を開く決済手段を選択した場合は、画面で**続行**をタップすると、決済オプションが更新されます。 ```swift struct MyEmbeddedCheckoutView: View { var body: some View { ScrollView { if embeddedViewModel.isLoaded { // ... // A real integration probably wouldn't show the selected payment option on the same screen as the embedded payment element. We display it as an example. if let paymentOption = embeddedViewModel.paymentOption { HStack { Image(uiImage: paymentOption.image) .resizable() .aspectRatio(contentMode: .fit) .frame(height: 30) Text(paymentOption.label) Spacer() } .padding() } } } } } ``` ### (任意)支払いの詳細を更新する 顧客が決済情報を変更する操作 (割引コードの適用など) を実行したときに、`update` メソッドを呼び出して `EmbeddedPaymentElementViewModel` インスタンスを更新し、新しい値を反映します。Apple Pay や Google Pay など、一部の決済手段では UI に金額が表示されるため、常に正確で最新の状態を維持するようにしてください。 更新が完了すると、それにより顧客が現在選択している決済オプションが変更される場合もあります。 ```swift extension MyEmbeddedCheckoutView { func update() { Task { @MainActor in var updatedIntentConfig = oldIntentConfig updatedIntentConfig.mode = PaymentSheet.IntentConfiguration.Mode.payment(amount: 999, currency: "USD") let result = await embeddedViewModel.update(intentConfiguration: updatedIntentConfig) switch result { case .canceled: // Do nothing; this happens when a subsequent `update` call cancels this one break case .failed(let error): // Display error to user in an alert, let users retry case .succeeded: // Update your UI in case the payment option changed } } } } ``` ### 支払いを確定する 顧客が決済ボタンをタップしたら、`embeddedViewModel.confirm()` を呼び出して支払いを完了します。確定する際は、ユーザーの操作を必ず無効にしてください。 ```swift extension MyEmbeddedCheckoutView { func didTapConfirmButton() { Task { @MainActor in // Be sure to disable user interaction during confirm (not shown in this example) let result = await embeddedViewModel.confirm() switch result { case .completed: // Payment completed - show a confirmation screen. case .failed(let error): // Encountered an unrecoverable error. Re-enable user interaction, display the error to the user, log it, and so on. case .canceled: // Customer canceled - re-enable user interaction and let them try again. break } } } } ``` 次に、`PaymentSheet.IntentConfiguration` に渡した `confirmationTokenConfirmHandler` コールバックを実装し、サーバーにリクエストを送信します。サーバーは PaymentIntent を作成し、Client Secret を返します。このプロセスについては、[PaymentIntent の作成](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#submit-payment) を参照してください。 サーバー応答の Client Secret を返すか、エラーをスローします。EmbeddedPaymentElement は、クライアントシークレットを使用して PaymentIntent を確認するか、ローカライズされたエラーメッセージを UI に表示します ( [errorDescription](https://developer.apple.com/documentation/foundation/localizederror/2946895-errordescription) または [localizedDescription](https://developer.apple.com/documentation/foundation/nserror/1414418-localizeddescription)) 確認が完了すると、EmbeddedPaymentElement は使用できなくなります。代わりに、ユーザーを領収書画面などに誘導します。 ```swift extension MyEmbeddedCheckoutView { func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // Make a request to your own server. Pass confirmationToken.stripeId if using server-side confirmation. // Return the client secret or throw an error return try await MyAPIClient.shared.createIntent(confirmationTokenId: confirmationToken.stripeId) } } ``` ## Optional: 選択した決済オプションを消去する EmbeddedPaymentElement 以外の決済オプションがある場合は、選択した決済オプションの消去が必要になることがあります。これを行うには、`clearPaymentOption` API を使用して、選択した決済オプションの選択を解除します。 #### UIKit ```swift extension MyCheckoutVC: UIViewController { func deselectPaymentMethod() { embeddedPaymentElement?.clearPaymentOption() } } ``` #### SwiftUI ```swift @available(iOS 15.0, *) extension MyEmbeddedCheckoutView { func deselectPaymentMethod() { embeddedViewModel.clearPaymentOption() } } ``` ## Optional: 同意書を自分で表示する デフォルトでは、Embedded Mobile Payment Element は、規制遵守を維持するための同意書と法的免責事項を表示します。このテキストは購入ボタンの近くに配置する必要があります。必要に応じて、ビューでの表示を無効にし、代わりに自分で表示します。 > 規制を遵守するため、実装で同意書のテキストを表示する必要があります。テキスト内の URL を UITextView などを使用して開けることを確認してください。 #### UIKit ```swift var configuration = EmbeddedPaymentElement.Configuration(...) configuration.embeddedViewDisplaysMandateText = true ... let mandateTextView = UITextView() mandateTextView.attributedText = embeddedPaymentElement.paymentOption.mandateText ``` #### SwiftUI ```swift var configuration = EmbeddedPaymentElement.Configuration(...) configuration.embeddedViewDisplaysMandateText = true ... if let attributedText = embeddedViewModel.paymentOption?.mandateText { Text(AttributedString(attributedText)) } ``` ## Optional: 顧客が画面ですぐに支払えるようにする ![Embedded Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/embedded-pay-immediate.057c691220d43158ac8000de10815ed9.png) 支払いをすぐに確定するようにフォーム画面のボタンを設定するには、`EmbeddedPaymentElement.Configuration` オブジェクトで `formSheetAction` を設定します。 画面が閉じられた後、支払い結果によって完了ブロックが実行されます。埋め込み UI は支払い完了後には使用できなくなるため、実装でユーザーを領収書画面など別の画面に誘導することをお勧めします。 ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.formSheetAction = .confirm(completion: { result in switch result { case .completed: // Payment completed. You can for example, show a confirmation screen. print("Completed") case .failed(let error): // Encountered an unrecoverable error. You can display the error to the user, log it, etc. print(error) case .canceled: // Customer canceled - you should probably do nothing. break } }) ``` ## PaymentIntent を作成する [サーバー側] サーバー側で、金額と通貨を指定して *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成します。支払い方法は[ダッシュボード](https://dashboard.stripe.com/settings/payment_methods)で管理できます。Stripe は取引額、通貨、決済フローなどの要素に基づいて、適切な支払い方法が返されるように処理します。悪意のある顧客が金額を恣意的に選択できないようにするために、請求額はクライアント側ではなく、常にサーバー側 (信頼性の高い環境) で指定してください。 コールが成功した場合は、PaymentIntent *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)) を返します。コールが失敗した場合は、[エラーを処理](https://docs.stripe.com/error-handling.md)して、エラーメッセージと顧客向けの簡単な説明を返します。 > すべての IntentConfiguration プロパティが PaymentIntent (`setup_future_usage`、`amount`、`currency` など) と一致していることを確認します。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', automatic_payment_methods: {enabled: true}, } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` ## 戻り先 URL を設定する [クライアント側] 顧客はお客様のアプリから離れて、(Safari やバンキングアプリなどで) 認証する場合があります。ユーザーが認証後にアプリに自動的に戻れるようにするには、[カスタム URL スキームを構成](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app)し、URL を SDK に転送するようにアプリのデリゲートを設定します。Stripe は[ユニバーサルリンク](https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content)には対応していません。 #### SceneDelegate #### Swift ```swift // This method handles opening custom URL schemes (for example, "your-app://stripe-redirect") func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { guard let url = URLContexts.first?.url else { return } let stripeHandled = StripeAPI.handleURLCallback(with: url) if (!stripeHandled) { // This was not a Stripe url – handle the URL normally as you would } } ``` #### AppDelegate #### Swift ```swift // This method handles opening custom URL schemes (for example, "your-app://stripe-redirect") func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { let stripeHandled = StripeAPI.handleURLCallback(with: url) if (stripeHandled) { return true } else { // This was not a Stripe url – handle the URL normally as you would } return false } ``` #### SwiftUI #### Swift ```swift @main struct MyApp: App { var body: some Scene { WindowGroup { Text("Hello, world!").onOpenURL { incomingURL in let stripeHandled = StripeAPI.handleURLCallback(with: incomingURL) if (!stripeHandled) { // This was not a Stripe url – handle the URL normally as you would } } } } } ``` さらに、[EmbeddedPaymentElement.Configuration](https://github.com/stripe/stripe-ios/blob/aa3234a7fafde98c9203b6ed77e0278c04310eb0/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L12) オブジェクトの [returnURL](https://github.com/stripe/stripe-ios/blob/aa3234a7fafde98c9203b6ed77e0278c04310eb0/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L70) をアプリの URL に設定します。 ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.returnURL = "your-app://stripe-redirect" ``` ## 支払い後のイベントを処理する [サーバー側] 支払いが完了すると、Stripe は [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) イベントを送信します。[ダッシュボードの Webhook ツール](https://dashboard.stripe.com/webhooks)を使用するか [Webhook のガイド](https://docs.stripe.com/webhooks/quickstart.md)に従ってこれらのイベントを受信し、顧客への注文確認メールの送信、データベースでの売上の記録、配送ワークフローの開始などのアクションを実行します。 クライアントからのコールバックを待つのではなく、これらのイベントをリッスンします。クライアントでは、コールバックが実行される前に顧客がブラウザーのウィンドウを閉じたり、アプリを終了する場合、また悪意を持つクライアントがレスポンスを不正操作する場合もあります。非同期型のイベントをリッスンするよう組み込みを設定すると、単一の組み込みで[複数の異なるタイプの支払い方法](https://stripe.com/payments/payment-methods-guide)を受け付けることができます。 Payment Element を使用して支払いを回収する場合は、`payment_intent.succeeded` イベントのほかにこれらのイベントを処理することをお勧めします。 | イベント | 説明 | アクション | | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.succeeded) | 顧客が正常に支払いを完了したときに送信されます。 | 顧客に注文の確定を送信し、顧客の注文の*フルフィルメント* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected)を実行します。 | | [payment_intent.processing](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.processing) | 顧客が正常に支払いを開始したが、支払いがまだ完了していない場合に送信されます。このイベントは、多くの場合、顧客が口座引き落としを開始するときに送信されます。その後、`payment_intent.succeeded` イベント、また、失敗の場合は `payment_intent.payment_failed` イベントが送信されます。 | 顧客に注文確認メールを送信し、支払いが保留中であることを示します。デジタル商品では、支払いの完了を待たずに注文のフルフィルメントを行うことが必要になる場合があります。 | | [payment_intent.payment_failed](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.payment_failed) | 顧客が支払いを試みたが、支払いに失敗する場合に送信されます。 | 支払いが `processing` から `payment_failed` に変わった場合は、顧客に再度支払いを試すように促します。 | ## 実装をテストする #### カード | カード番号 | シナリオ | テスト方法 | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | | 4242424242424242 | カード支払いは成功し、認証は必要とされません。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000002500003155 | カード支払いには*認証* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase)が必要です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000000000009995 | カードは、`insufficient_funds` などの拒否コードで拒否されます。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 6205500000000000004 | UnionPay カードは、13 ~ 19 桁の可変長です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | #### 銀行へのリダイレクト | 決済手段 | シナリオ | テスト方法 | | ---------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Bancontact、iDEAL | 顧客は、リダイレクトベースの即時通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | リダイレクトベースの任意の支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払い失敗)** をクリックします。 | | Pay by Bank | 顧客はリダイレクトベースの、[遅延通知型](https://docs.stripe.com/payments/payment-methods.md#payment-notification)の支払い方法で支払いに成功します。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Complete test payment (テスト支払い完了)** をクリックします。 | | Pay by Bank | 顧客は、リダイレクトベースの遅延通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払いを失敗させる)** をクリックします。 | | BLIK | BLIK による支払いはさまざまな理由で失敗します。即時の失敗 (コードの有効期限切れや無効など)、遅延型のエラー (銀行による拒否など)、またはタイムアウト (顧客が時間内に応答しなかったなど) などがあります。 | メールパターンを使用して[さまざまな失敗をシミュレーションします。](https://docs.stripe.com/payments/blik/accept-a-payment.md#simulate-failures) | #### 口座引き落とし | 決済手段 | シナリオ | テスト方法 | | -------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | SEPA ダイレクトデビット | 顧客が SEPA ダイレクトデビットによる支払いに成功しました。 | 口座番号 `AT321904300235473204` を使用して、フォームに入力します。確定された PaymentIntent のステータスはまず、processing に移行し、3 分後に succeeded ステータスに移行します。 | | SEPA ダイレクトデビット | 顧客の Payment Intent のステータスが、`processing` から `requires_payment_method` に移行します。 | 口座番号 `AT861904300235473202` を使用して、フォームに入力します。 | 実装内容をテストするためのその他の情報については、[テスト](https://docs.stripe.com/testing.md)をご覧ください。 ## カードのスキャンを有効にする iOS をサポートするカードスキャン機能を有効にするには、アプリケーションの `Info.plist` の `NSCameraUsageDescription` (**プライバシー - カメラ利用の詳細**)を設定し、カメラにアクセスする理由を入力して下さい (例:「カードをスキャンするため」)。 ## Optional: 保存済みのカードを有効にする [サーバー側] [クライアント側] `EmbeddedPaymentElement` では、顧客がカードを保存でき、利用可能な決済手段に顧客が保存済みのカードを含めることもできます。顧客は、サーバー上で関連付けられた顧客設定済みの [Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) オブジェクトまたは [Customer](https://docs.stripe.com/api/customers/create.md) オブジェクトを持っている必要があります。顧客がカードを保存できるようにするチェックボックスを有効にするには、[CustomerSession](https://docs.stripe.com/api/customer_sessions.md) を作成し、`payment_method_save` を `enabled` に設定します。 #### Accounts v2 ```javascript const stripe = require('stripe')('sk_test_your_secret_key'); app.post('/mobile-payment-element', async (req, res) => { // Use an existing Account ID if this is a returning customer. const customer_account = await stripe.v2.core.accounts.create(); const customerSession = await stripe.customerSessions.create({ customer_account: customer_account.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer_account: customer_account.id, }); }); ``` 次に、顧客の ID と CustomerSession の client secret を使用して EmbeddedPaymentElement を設定します。 #### UIKit ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerAccountId, customerSessionClientSecret: customerSessionClientSecret) self.embeddedPaymentElement = try await EmbeddedPaymentElement.create(..., configuration: configuration) ``` #### SwiftUI ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerAccountId, customerSessionClientSecret: customerSessionClientSecret) try await embeddedViewModel.load(..., configuration: configuration) ``` #### Customers v1 ```javascript const stripe = require('stripe')('sk_test_your_secret_key'); app.post('/mobile-payment-element', async (req, res) => { // Use an existing Customer ID if this is a returning customer. const customer = await stripe.customers.create(); const customerSession = await stripe.customerSessions.create({ customer: customer.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer: customer.id, }); }); ``` 次に、顧客の ID と CustomerSession の client secret を使用して EmbeddedPaymentElement を設定します。 #### UIKit ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerId, customerSessionClientSecret: customerSessionClientSecret) self.embeddedPaymentElement = try await EmbeddedPaymentElement.create(..., configuration: configuration) ``` #### SwiftUI ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerId, customerSessionClientSecret: customerSessionClientSecret) try await embeddedViewModel.load(..., configuration: configuration) ``` ## Optional: 遅延型の支払い方法を許可する [クライアント側] *遅延型の決済手段* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods)では、購入の終了時に顧客から売上を受け取ることが保証されません。これは、決済に時間がかかる (アメリカの銀行口座、SEPA デビット、iDEAL、Bancontact など) 場合や、完了に顧客の対応を必要とする (OXXO、コンビニ決済、Boleto など) 場合があるためです。 デフォルトでは、EmbeddedPaymentElement は遅延型の決済手段を表示しません。オプトインするには、`EmbeddedPaymentElement.Configuration` で `allowsDelayedPaymentMethods` を true に設定します。このステップのみでは、特定の決済手段を有効にすることはできませんが、アプリがその決済手段に対応できることが示されます。たとえば、現在 OXXO は EmbeddedPaymentElement でサポートされていませんが、サポートされるようになり、最新の SDK バージョンに更新すると、実装に関する追加の変更なしで、アプリに決済オプションとして OXXO を表示できます。 #### UIKit ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.allowsDelayedPaymentMethods = true self.embeddedPaymentElement = try await EmbeddedPaymentElement.create(..., configuration: configuration) ``` #### SwiftUI ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.allowsDelayedPaymentMethods = true try await embeddedViewModel.load(..., configuration: configuration) ``` 顧客が EmbeddedPaymentElement でこれらの遅延型の決済手段のいずれかを適切に使用すると、`.completed` の支払い結果が返されます。 ## Optional: Apple Pay を有効にする > 決済画面に専用の **Apple Pay** ボタンがある場合は、[Apple Pay ガイド](https://docs.stripe.com/apple-pay.md#present-payment-sheet)に従い、`ApplePayContext` を使用して **Apple Pay** ボタンから支払いを回収します。`EmbeddedPaymentElement` を使用して、他のタイプの決済手段に対応することも可能です。 ### Apple 加盟店 ID を登録する Apple Developer Web サイトで [新規 ID を登録](https://developer.apple.com/account/resources/identifiers/add/merchant) して、Apple 加盟店 ID を取得します。 フォームに説明と ID を入力します。説明はお客様の記録用であり、後で変更できます。アプリの名前を ID として使用することをお勧めします (`merchant.com.{{YOUR_APP_NAME}}` など)。 ### 新しい Apple Pay 証明書を作成する 支払いデータを暗号化するためのアプリの証明書を作成します。 ダッシュボードの [iOS certificate settings (iOS 証明書の設定)](https://dashboard.stripe.com/settings/ios_certificates) に移動して、**新規アプリケーションを追加**をクリックし、表示されるガイドに従います。 証明書署名リクエスト (CSR) ファイルをダウンロードして、Apple Pay の利用を可能にする安全な証明書を Apple から取得します。 1 つの CSR ファイルを使用して証明書を 1 つだけ発行する必要があります。Apple 加盟店 ID を切り替えた場合、ダッシュボードの [iOS Certificate Settings (iOS 証明書の設定)](https://dashboard.stripe.com/settings/ios_certificates) に移動して、新しい CSR と証明書を取得する必要があります。 ### Xcode を導入する Apple Pay ケイパビリティをアプリに追加します。Xcode でプロジェクト設定を開き、**Signing & Capabilities (署名およびケイパビリティ)** タブを選択して、**Apple Pay** ケイパビリティを追加します。この段階で開発者アカウントへのログインを要求される場合があります。前の手順で作成した加盟店 ID を選択すると、アプリで Apple Pay を受け付けられるようになります。 ![](https://b.stripecdn.com/docs-statics-srv/assets/xcode.a701d4c1922d19985e9c614a6f105bf1.png) Xcode で Apple Pay ケイパビリティを有効化する ### Apple Pay を追加する #### 1 回限りの支払い EmbeddedPaymentElement に Apple Pay を追加するには、Apple マーチャント ID とビジネスの[国コード](https://dashboard.stripe.com/settings/account)を使用して `EmbeddedPaymentElement.Configuration` を初期化してから、[applePay](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L28) を設定します。 #### iOS (Swift) ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.applePay = .init( merchantId: "merchant.com.your_app_name", merchantCountryCode: "US" ) ``` #### 継続支払い EmbeddedPaymentElement に Apple Pay を追加するには、Apple マーチャント ID とビジネスの[国コード](https://dashboard.stripe.com/settings/account)を使用して `EmbeddedPaymentElement.Configuration` を初期化してから、[applePay](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L28) を設定します。 継続支払いに関する [Apple のガイドライン](https://developer.apple.com/design/human-interface-guidelines/apple-pay#Supporting-subscriptions)に従い、`PKPaymentRequest` で追加の属性を設定する必要もあります。[ApplePayConfiguration.paymentRequestHandlers](https://stripe.dev/stripe-ios/stripepaymentsheet/documentation/stripepaymentsheet/paymentsheet/applepayconfiguration/handlers/paymentrequesthandler) にハンドラーを追加して、請求する予定の金額 (たとえば月額 9.95 USD) を指定して [PKPaymentRequest.paymentSummaryItems](https://developer.apple.com/documentation/passkit/pkpaymentrequest/1619231-paymentsummaryitems) を設定します。 `PKPaymentRequest` で `recurringPaymentRequest` または `automaticReloadPaymentRequest` プロパティを設定することで、[加盟店トークン](https://developer.apple.com/apple-pay/merchant-tokens/)を導入することもできます。 Apple Pay で継続支払いを使用する方法について、詳細は [Apple の PassKit に関するドキュメント](https://developer.apple.com/documentation/passkit/pkpaymentrequest)をご覧ください。 #### iOS (Swift) ```swift let customHandlers = EmbeddedPaymentElement.ApplePayConfiguration.Handlers( paymentRequestHandler: { request in // PKRecurringPaymentSummaryItem is available on iOS 15 or later if #available(iOS 15.0, *) { let billing = PKRecurringPaymentSummaryItem(label: "My Subscription", amount: NSDecimalNumber(string: "59.99")) // Payment starts today billing.startDate = Date() // Payment ends in one year billing.endDate = Date().addingTimeInterval(60 * 60 * 24 * 365) // Pay once a month. billing.intervalUnit = .month billing.intervalCount = 1 // recurringPaymentRequest is only available on iOS 16 or later if #available(iOS 16.0, *) { request.recurringPaymentRequest = PKRecurringPaymentRequest(paymentDescription: "Recurring", regularBilling: billing, managementURL: URL(string: "https://my-backend.example.com/customer-portal")!) request.recurringPaymentRequest?.billingAgreement = "You'll be billed $59.99 every month for the next 12 months. To cancel at any time, go to Account and click 'Cancel Membership.'" } request.paymentSummaryItems = [billing] request.currencyCode = "USD" } else { // On older iOS versions, set alternative summary items. request.paymentSummaryItems = [PKPaymentSummaryItem(label: "Monthly plan starting July 1, 2022", amount: NSDecimalNumber(string: "59.99"), type: .final)] } return request } ) var configuration = EmbeddedPaymentElement.Configuration() configuration.applePay = .init(merchantId: "merchant.com.your_app_name", merchantCountryCode: "US", customHandlers: customHandlers) ``` ### 注文の追跡 iOS 16 以降で[注文の追跡](https://developer.apple.com/design/human-interface-guidelines/technologies/wallet/designing-order-tracking)情報を追加するには、`PaymentSheet.ApplePayConfiguration.Handlers` で [authorizationResultHandler](https://stripe.dev/stripe-ios/stripepaymentsheet/documentation/stripepaymentsheet/paymentsheet/applepayconfiguration/handlers/authorizationresulthandler) を設定します。支払いの完了後、Stripe は iOS が Apple Pay の決済画面を閉じる前に実装を呼び出します。 `authorizationResultHandler` の実装で、完了した注文の注文の詳細をサーバーから取得します。提供された [PKPaymentAuthorizationResult](https://developer.apple.com/documentation/passkit/pkpaymentauthorizationresult) に詳細を追加し、変更された結果を返します。 注文の追跡の詳細については、[Apple のウォレットでの注文に関するドキュメント](https://developer.apple.com/documentation/walletorders)をご覧ください。 #### iOS (Swift) ```swift let customHandlers = PaymentSheet.ApplePayConfiguration.Handlers( authorizationResultHandler: { result in do { // Fetch the order details from your service let myOrderDetails = try await MyAPIClient.shared.fetchOrderDetails(orderID: orderID) result.orderDetails = PKPaymentOrderDetails( orderTypeIdentifier: myOrderDetails.orderTypeIdentifier, // "com.myapp.order" orderIdentifier: myOrderDetails.orderIdentifier, // "ABC123-AAAA-1111" webServiceURL: myOrderDetails.webServiceURL, // "https://my-backend.example.com/apple-order-tracking-backend" authenticationToken: myOrderDetails.authenticationToken) // "abc123" // Return your modified PKPaymentAuthorizationResult return result } catch { return PKPaymentAuthorizationResult(status: .failure, errors: [error]) } } ) var configuration = EmbeddedPaymentElement.Configuration() configuration.applePay = .init(merchantId: "merchant.com.your_app_name", merchantCountryCode: "US", customHandlers: customHandlers) ``` ## Optional: 構成要素をカスタマイズする すべてのカスタマイズは、[EmbeddedPaymentElement.Configuration](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L12) オブジェクトで設定されます。 ### デザイン [Appearance API](https://docs.stripe.com/elements/appearance-api/embedded-mobile.md?platform=ios) を使用して、アプリのデザインに合うように色やフォントなどをカスタマイズします。 ### ユーザーの住所を収集する [Address Element](https://docs.stripe.com/elements/address-element.md?platform=ios) を使用して、顧客から国内および国外の配送先住所や請求先住所を収集します。 ### 加盟店の表示名 [merchantDisplayName](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L66) を設定し、顧客に表示するビジネス名を指定します。デフォルトではアプリ名になります。 #### Swift ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.merchantDisplayName = "My app, Inc." ``` ### ダークモード `EmbeddedPaymentElement` は、ユーザーのシステム全体の表示設定 (ライトモードとダークモード) に合わせて自動的に調整されます。アプリがダークモードに対応していない場合は、[スタイル](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L51)を `alwaysLight` または `alwaysDark` モードに設定できます。 ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.style = .alwaysLight ``` ## Optional: 確定時のセキュリティコードの再収集を有効にする PaymentIntent の確定時に保存されたカードのセキュリティコードを再徴収するには、統合で PaymentIntent を作成する前に決済詳細を徴収する必要があります。 ### インテントの設定を更新する `PaymentSheet.IntentConfiguration` では、保存されたカードのセキュリティコードを再収集する時期を管理するオプションパラメーターを使用できます。 ```swift let intentConfig = PaymentSheet.IntentConfiguration( mode: .payment(amount: 1099, currency: "USD"), confirmHandler: { confirmationToken in // Handle ConfirmationToken...}, requireCVCRecollection: true) ``` ### インテント作成のパラメーターを更新する 支払いの確定時にセキュリティコードを再収集するには、PaymentIntent の作成時に `customerId` パラメーターと `require_cvc_recollection` パラメーターの両方を含めます。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. # Find your keys at https://dashboard.stripe.com/apikeys. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', automatic_payment_methods: {enabled: true},customer: customer.id, payment_method_options: { card: {require_cvc_recollection: true} } } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` # 決済手段を収集して保存する > This is a 決済手段を収集して保存する for when platform is ios and type is setup. View the full page at https://docs.stripe.com/payments/mobile/accept-payment-embedded?platform=ios&type=setup. SetupIntent フローを使用すると、決済を作成せずに、今後の決済に備えて決済手段を保存できます。この導入では、Payment Element を表示して、*SetupIntent* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) を作成し、決済手段をアプリに保存します。 ## Stripe を設定する [サーバー側] [クライアント側] ### サーバー側 この組み込みは、Stripe API と通信するサーバー上にエンドポイントを必要とします。サーバーから Stripe API にアクセスするには、次のように Stripe の公式ライブラリーを使用します。 #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### クライアント側 [Stripe iOS SDK](https://github.com/stripe/stripe-ios) はオープンソースです。[詳細なドキュメントが提供されており](https://stripe.dev/stripe-ios/index.html)、iOS 13 以降をサポートするアプリと互換性があります。 #### Swift Package Manager SDK をインストールするには、以下のステップに従います。 1. Xcode で、**File (ファイル)** > **Add Package Dependencies… (パッケージ依存関係を追加)** を選択し、リポジトリー URL として `https://github.com/stripe/stripe-ios-spm` を入力します。 1. [リリースページ](https://github.com/stripe/stripe-ios/releases)から最新のバージョン番号を選択します。 1. **StripePaymentSheet** 製品を[アプリのターゲット](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app)に追加します。 #### CocoaPods 1. まだインストールしていない場合は、[CocoaPods](https://guides.cocoapods.org/using/getting-started.html) の最新バージョンをインストールします。 1. 既存の [Podfile](https://guides.cocoapods.org/syntax/podfile.html) がない場合は、以下のコマンドを実行して作成します。 ```bash pod init ``` 1. この行を `Podfile` に追加します。 ```podfile pod 'StripePaymentSheet' ``` 1. 以下のコマンドを実行します。 ```bash pod install ``` 1. これ以降は、Xcode でプロジェクトを開く際に、`.xcodeproj` ファイルではなく、必ず `.xcworkspace` ファイルを使用するということを忘れないでください。 1. 今後、SDK の最新バージョンに更新するには、以下を実行します。 ```bash pod update StripePaymentSheet ``` #### Carthage 1. まだインストールしていない場合は、[Carthage](https://github.com/Carthage/Carthage#installing-carthage) の最新バージョンをインストールします。 1. この行を `Cartfile` に追加します。 ```cartfile github "stripe/stripe-ios" ``` 1. [Carthage のインストール手順](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos)に従います。必ず、[こちら](https://github.com/stripe/stripe-ios/tree/master/StripePaymentSheet/README.md#manual-linking)にリストされている必要なフレームワークのすべてを埋め込んでください。 1. 今後、SDK の最新バージョンに更新するには、以下のコマンドを実行します。 ```bash carthage update stripe-ios --platform ios ``` #### 手動のフレームワーク 1. Stripe の [GitHub リリースページ](https://github.com/stripe/stripe-ios/releases/latest)に移動して、**Stripe.xcframework.zip** をダウンロードして解凍します。 1. **StripePaymentSheet.xcframework** を、Xcode プロジェクトの **General (一般) ** 設定の **Embedded Binaries (埋め込みバイナリー)** セクションにドラッグします。**Copy items if needed (必要に応じてアイテムをコピーする)** を必ず選択してください。 1. [こちら](https://github.com/stripe/stripe-ios/tree/master/StripePaymentSheet/README.md#manual-linking)にリストされている必要なフレームワークのすべてに対して、ステップ 2 を繰り返します。 1. 今後、Stripe の SDK の最新バージョンに更新するには、ステップ 1 から 3 を繰り返します。 > SDK の最新リリースおよび過去バージョンの詳細については、GitHub の [Releases (リリース)](https://github.com/stripe/stripe-ios/releases) ページをご覧ください。リポジトリの[リリースをウォッチ](https://help.github.com/en/articles/watching-and-unwatching-releases-for-a-repository#watching-releases-for-a-repository)して、新しいリリースの公開時に通知を受け取ることも可能です。 また、SDK が Stripe への API コールを実行できるように、[公開可能キー](https://dashboard.stripe.com/apikeys)を設定する必要もあります。開始するには、導入中にクライアント側で公開可能キーをハードコード化できますが、本番環境ではサーバーから公開可能キーを取得します。 ```swift // Set your publishable key: remember to change this to your live publishable key in production // See your keys here: https://dashboard.stripe.com/apikeys STPAPIClient.shared.publishableKey = "<>" ``` ## 支払い方法を有効にする [支払い方法の設定](https://dashboard.stripe.com/settings/payment_methods)を表示して、サポートする支払い方法を有効にします。*SetupIntent* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) を作成するには、少なくとも 1 つは支払い方法を有効にする必要があります。 多くの顧客から決済を受け付けられるよう、Stripe では、カードやその他一般的な決済手段がデフォルトで有効になっていますが、ビジネスや顧客に適した追加の決済手段を有効にすることをお勧めします。プロダクトと決済手段のサポートについては[決済手段のサポート](https://docs.stripe.com/payments/payment-methods/payment-method-support.md)を、手数料については[料金体系ページ](https://stripe.com/pricing/local-payment-methods)をご覧ください。 ## 顧客を作成する [サーバー側] 以降の決済のために決済手段を設定するには、顧客を表すオブジェクトに関連付ける必要があります。顧客がアカウントを作成するか、ビジネスと最初の取引を行うときに、[Customer](https://docs.stripe.com/api/customers/create.md) オブジェクトを作成します。 ```curl curl -X POST https://api.stripe.com/v1/customers \ -u "<>:" ``` ## 支払いの詳細を収集する [クライアント側] Embedded Mobile Payment Element は、ネイティブモバイルアプリの決済ページに配置するように設計されています。この Element には決済手段の一覧が表示され、アプリのデザインに合わせてカスタマイズできます。 顧客が**カード**行をタップすると、決済手段の詳細を入力できる画面が表示されます。画面にデフォルトで**続行**というボタンが表示され、タップすると画面が閉じます。これにより、顧客は決済フローで設定を完了できます。 ![Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/ios-embedded.b5867c116d537ffcb920dd80ebdfc741.png) また、続行するのではなく、すぐに設定を完了するようにボタンを設定することもできます。これを行うには、ガイドに沿って手順を実行した後で、[このステップ](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#embedded-let-customer-pay-immediately)を完了します。 #### UIKit ### Payment Element を初期化する `create` を呼び出し、`EmbeddedPaymentElement.Configuration` と [PaymentSheet.IntentConfiguration](https://github.com/stripe/stripe-ios/blob/master/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetIntentConfiguration.swift) を使用して EmbeddedPaymentElement をインスタンス化します。 Configuration オブジェクトには、`returnURL` のような、決済間で変更されない EmbeddedPaymentElement の汎用設定オプションが含まれます。`IntentConfiguration` オブジェクトには、金額や通貨などの特定の決済に関する詳細と、`confirmationTokenConfirmHandler` コールバックが含まれます。今のところ、その実装は空のままにしておきます。初期化に成功したら、`presentingViewController` と `delegate` プロパティを設定します。 ```swift import StripePaymentSheet class MyCheckoutVC: UIViewController { func createEmbeddedPaymentElement() async throws -> EmbeddedPaymentElement { let intentConfig = PaymentSheet.IntentConfiguration( mode: .setup(currency: "USD") ) { [weak self] confirmationToken in return await self?.handleConfirmationToken(confirmationToken) } var configuration = EmbeddedPaymentElement.Configuration() configuration.returnURL = "your-app://stripe-redirect" // Use the return url you set up in the previous step let embeddedPaymentElement = try await EmbeddedPaymentElement.create(intentConfiguration: intentConfig, configuration: configuration) embeddedPaymentElement.presentingViewController = self embeddedPaymentElement.delegate = self return embeddedPaymentElement } func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // You'll implement this in the "Confirm the payment" section below } } ``` ### Payment Element ビューを追加する EmbeddedPaymentElement が正常に初期化されたら、そのビューを決済 UI に配置します。 > ビューは固定サイズではなく、最初にレンダリングされた後に高さが変わる可能性があるため、UIScrollView などのスクロール可能なビュー内に含める必要があります。 ```swift class MyCheckoutVC: UIViewController { // ... private(set) var embeddedPaymentElement: EmbeddedPaymentElement? private lazy var checkoutButton: UIButton = { let checkoutButton = UIButton(type: .system) checkoutButton.backgroundColor = .systemBlue checkoutButton.layer.cornerRadius = 5.0 checkoutButton.clipsToBounds = true checkoutButton.setTitle("Checkout", for: .normal) checkoutButton.setTitleColor(.white, for: .normal) checkoutButton.translatesAutoresizingMaskIntoConstraints = false checkoutButton.isEnabled = embeddedPaymentElement?.paymentOption != nil checkoutButton.addTarget(self, action: #selector(didTapConfirmButton), for: .touchUpInside) return checkoutButton }() // ... @objc func didTapConfirmButton() { // You'll implement this in the "Confirm the payment" section below } override func viewDidLoad() { super.viewDidLoad() Task { @MainActor in do { // Create a UIScrollView let scrollView = UIScrollView() scrollView.translatesAutoresizingMaskIntoConstraints = false self.view.addSubview(scrollView) // Create the Payment Element let embeddedPaymentElement = try await createEmbeddedPaymentElement() embeddedPaymentElement.delegate = self embeddedPaymentElement.presentingViewController = self self.embeddedPaymentElement = embeddedPaymentElement // Add its view to the scroll view scrollView.addSubview(embeddedPaymentElement.view) // Add your own checkout button to the scroll view scrollView.addSubview(checkoutButton) // Set up layout constraints embeddedPaymentElement.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), embeddedPaymentElement.view.topAnchor.constraint(equalTo: scrollView.topAnchor), embeddedPaymentElement.view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), embeddedPaymentElement.view.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), checkoutButton.topAnchor.constraint(equalTo: embeddedPaymentElement.view.bottomAnchor, constant: 4.0), checkoutButton.leadingAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.leadingAnchor, constant: 4.0), checkoutButton.trailingAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.trailingAnchor, constant: -4.0), ]) } catch { // Handle view not being added to view } } } } ``` この時点で、アプリを実行して、Embedded Mobile Payment Element を確認できます。 ### 高さの変更に対応する EmbeddedPaymentElement のビューは、サイズが大きくなり、または小さくなる可能性があり、ビューのレイアウトに影響を与える可能性があります。 高さの変更に対応するには、`embeddedPaymentElementDidUpdateHeight` デリゲートメソッドを実装します。EmbeddedPaymentElement のビューは、高さを更新するアニメーションブロック内でこのメソッドを呼び出します。実装では、高さ変更のアニメーションをスムーズに行うために、EmbeddedPaymentElement のビューを含むスクロールビューで `setNeedsLayout()` と `layoutIfNeeded()` を呼び出すことが想定されます。 ```swift extension MyCheckoutVC: EmbeddedPaymentElementDelegate { func embeddedPaymentElementDidUpdateHeight(embeddedPaymentElement: StripePaymentSheet.EmbeddedPaymentElement) { // Handle layout appropriately self.view.setNeedsLayout() self.view.layoutIfNeeded() } } ``` ビューが高さの変更に適切に応答することをテストすることを推奨します。これを行うには、`testHeightChange()` を EmbeddedPaymentElement 上で呼び出し、エレメント内の同意書の表示と非表示をシミュレートします。`testHeightChange()` を呼び出した後、スクロールビューがスムーズに調整されることを確認してください。 ```swift class MyCheckoutVC: UIViewController { override func viewDidLoad() { // This is only for testing purposes: #if DEBUG Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in Task { @MainActor in self?.embeddedPaymentElement?.testHeightChange() } } #endif } } ``` ### (任意)選択した支払いオプションを表示する ラベル (「····4242」など)、画像 (VISA ロゴなど)、UI に表示する請求先情報など、顧客が選択した決済オプションの詳細にアクセスする必要がある場合は、EmbeddedPaymentElement の `paymentOption` プロパティを使用します。 `paymentOption` が変更されたときに通知を受けるには、`embeddedPaymentElementDidUpdatePaymentOption` デリゲートメソッドを実装します。 ```swift extension MyCheckoutVC: EmbeddedPaymentElementDelegate { func embeddedPaymentElementDidUpdatePaymentOption(embeddedPaymentElement: EmbeddedPaymentElement) { print("The payment option changed: \(embeddedPaymentElement.paymentOption)") checkoutButton.isEnabled = embeddedPaymentElement.paymentOption != nil } } ``` ### (任意)支払いの詳細を更新する 顧客が決済情報を変更する操作 (割引コードの適用など) を実行したときに、`update` メソッドを呼び出して `EmbeddedPaymentElement` インスタンスを更新し、新しい値を反映します。Apple Pay や Google Pay など、一部の決済手段では UI に金額が表示されるため、常に正確で最新の状態を維持するようにしてください。 更新コールが完了したら、UI を更新します。更新コールにより、顧客が現在選択している決済オプションが変更される場合もあります。 ```swift extension MyCheckoutVC { func update() { Task { @MainActor in var updatedIntentConfig = oldIntentConfig // Update the currency updatedIntentConfig.mode = PaymentSheet.IntentConfiguration.Mode.setup(currency: "USD") let result = await embeddedPaymentElement?.update(intentConfiguration: updatedIntentConfig) switch result { case .canceled, nil: // Do nothing; this happens when a subsequent `update` call cancels this one break case .failed(let error): // Display error to user in an alert, let users retry case .succeeded: // Update your UI in case the payment option changed } } } } ``` ### 設定を確定する 顧客が決済ボタンをタップしたら、`embeddedPaymentElement.confirm()` を呼び出して設定を完了します。確定する際は、ユーザーの操作を必ず無効にしてください。 ```swift extension MyCheckoutVC { @objc func didTapConfirmButton() { Task { @MainActor in guard let embeddedPaymentElement else { return } self.view.isUserInteractionEnabled = false // Disable user interaction, show a spinner, and so on before calling confirm. let result = await embeddedPaymentElement.confirm() switch result { case .completed: // Setup completed - show a confirmation screen. case .failed(let error): self.view.isUserInteractionEnabled = true // Encountered an unrecoverable error. You can display the error to the user, log it, and so on. case .canceled: self.view.isUserInteractionEnabled = true // Customer canceled - you should probably do nothing. break } } } } ``` 次に、`PaymentSheet.IntentConfiguration` に渡した `confirmationTokenConfirmHandler` コールバックを実装し、サーバーにリクエストを送信します。サーバーは SetupIntent を作成し、Client Secret を返します。このプロセスについては、[SetupIntent の作成](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#submit-setup) を参照してください。 リクエストが返されたら、サーバー応答のクライアントシークレットを返すか、エラーをスローします。EmbeddedPaymentElement は、クライアントシークレットを使用して PaymentIntent を確認するか、ローカライズされたエラーメッセージを UI に表示します ([errorDescription](https://developer.apple.com/documentation/foundation/localizederror/2946895-errordescription) または [localizedDescription](https://developer.apple.com/documentation/foundation/nserror/1414418-localizeddescription))。確認が完了すると、EmbeddedPaymentElement は使用できなくなります。代わりに、ユーザーを領収書画面などに誘導します。 ```swift extension MyCheckoutVC { func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // Make a request to your own server. Pass `confirmationToken.stripeId` if using server-side confirmation, and return the client secret or throw an error. let myServerClientSecret = try await fetchIntentClientSecret(...) } } ``` #### SwiftUI ### Payment Element を初期化する `load` を呼び出し、`EmbeddedPaymentElement.Configuration` と [PaymentSheet.IntentConfiguration](https://github.com/stripe/stripe-ios/blob/master/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetIntentConfiguration.swift) を指定して EmbeddedPaymentElementViewModel を読み込みます。 Configuration オブジェクトには、`returnURL` のような、決済間で変更されない EmbeddedPaymentElement の汎用設定オプションが含まれます。`IntentConfiguration` オブジェクトには、金額や通貨などの特定の決済に関する詳細と、`confirmationTokenConfirmHandler` コールバックが含まれます。今のところ、その実装は空のままにしておきます。 ```swift import SwiftUI import StripePaymentSheet struct MyEmbeddedCheckoutView: View { // Store an `EmbeddedPaymentElementViewModel` as a `@StateObject` @StateObject var embeddedViewModel = EmbeddedPaymentElementViewModel() var body: some View { ScrollView { // Empty scroll view for now } .task { do { if !embeddedViewModel.isLoaded { // Load the view model with your configuration try await loadEmbeddedViewModel() } } catch { // On load failure, implement retry logic (automatic retry or user-triggered through UI). } } } private func loadEmbeddedViewModel() async throws { let intentConfiguration = PaymentSheet.IntentConfiguration( mode: .setup(currency: "USD") ) { confirmationToken in try await self.handleConfirmationToken(confirmationToken) } var configuration = EmbeddedPaymentElement.Configuration() configuration.returnURL = "your-app://stripe-redirect" // Use the return url you set up in the previous step try await embeddedViewModel.load(intentConfiguration: intentConfiguration, configuration: configuration) } func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // You'll implement this in the "Confirm the payment" section below } } ``` ### Payment Element ビューを追加する EmbeddedPaymentElementViewModel が正常にロードされたら、決済 UI に EmbeddedPaymentElementView を追加します。 > ビューは固定サイズではなく、最初にレンダリングされた後に高さが変わる可能性があるため、ScrollView などのスクロール可能なビュー内に含める必要があります。 ```swift struct MyEmbeddedCheckoutView: View { var body: some View { ScrollView { // isLoaded becomes true only after a successful call to `embeddedViewModel.load()` if embeddedViewModel.isLoaded { EmbeddedPaymentElementView(viewModel: embeddedViewModel) } } } } ``` この時点で、アプリを実行して、Embedded Mobile Payment Element を確認できます。 ### 高さの変更に対応する EmbeddedPaymentElementView のサイズが大きくなり、または小さくなると、ビューのレイアウトに影響を与える可能性があります。EmbeddedPaymentElementView は、ScrollView 内にある場合、これを自動的に処理します。 ビューが高さの変更に適切に応答することをテストすることを推奨します。これを行うには、`testHeightChange()` を EmbeddedPaymentElementViewModel 上で呼び出し、エレメント内の同意書の表示と非表示をシミュレートします。`testHeightChange()` を呼び出した後、スクロールビューがスムーズに調整されることを確認してください。 ```swift struct MyEmbeddedCheckoutView: View { var body: some View { ScrollView { if embeddedViewModel.isLoaded { EmbeddedPaymentElementView(viewModel: embeddedViewModel) // For testing only #if DEBUG Button("Test height change") { embeddedViewModel.testHeightChange() } #endif } } } } ``` ### (任意)選択した支払いオプションを表示する ラベル (「····4242」など)、画像 (VISA ロゴなど)、UI に表示する請求先情報など、顧客が選択した決済オプションの詳細にアクセスする必要がある場合は、EmbeddedPaymentElementViewModel の `paymentOption` プロパティを使用します。顧客が、フォーム画面を開く決済手段を選択した場合は、画面で**続行**をタップすると、決済オプションが更新されます。 ```swift struct MyEmbeddedCheckoutView: View { var body: some View { ScrollView { if embeddedViewModel.isLoaded { // ... // A real integration probably wouldn't show the selected payment option on the same screen as the embedded payment element. We display it as an example. if let paymentOption = embeddedViewModel.paymentOption { HStack { Image(uiImage: paymentOption.image) .resizable() .aspectRatio(contentMode: .fit) .frame(height: 30) Text(paymentOption.label) Spacer() } .padding() } } } } } ``` ### (任意)支払いの詳細を更新する 顧客が決済情報を変更する操作 (割引コードの適用など) を実行したときに、`update` メソッドを呼び出して `EmbeddedPaymentElementViewModel` インスタンスを更新し、新しい値を反映します。Apple Pay や Google Pay など、一部の決済手段では UI に金額が表示されるため、常に正確で最新の状態を維持するようにしてください。 更新が完了すると、それにより顧客が現在選択している決済オプションが変更される場合もあります。 ```swift extension MyEmbeddedCheckoutView { func update() { Task { @MainActor in var updatedIntentConfig = oldIntentConfig updatedIntentConfig.mode = PaymentSheet.IntentConfiguration.Mode.setup(currency: "USD") let result = await embeddedViewModel.update(intentConfiguration: updatedIntentConfig) switch result { case .canceled: // Do nothing; this happens when a subsequent `update` call cancels this one break case .failed(let error): // Display error to user in an alert, let users retry case .succeeded: // Update your UI in case the payment option changed } } } } ``` ### 設定を確定する 顧客が決済ボタンをタップしたら、`embeddedViewModel.confirm()` を呼び出して設定を完了します。確定する際は、ユーザーの操作を必ず無効にしてください。 ```swift extension MyEmbeddedCheckoutView { func didTapConfirmButton() { Task { @MainActor in // Be sure to disable user interaction during confirm (not shown in this example) let result = await embeddedViewModel.confirm() switch result { case .completed: // Setup completed - show a confirmation screen. case .failed(let error): // Encountered an unrecoverable error. Re-enable user interaction, display the error to the user, log it, and so on. case .canceled: // Customer canceled - re-enable user interaction and let them try again. break } } } } ``` 次に、`PaymentSheet.IntentConfiguration` に渡した `confirmationTokenConfirmHandler` コールバックを実装し、サーバーにリクエストを送信します。サーバーは SetupIntent を作成し、Client Secret を返します。このプロセスについては、[SetupIntent の作成](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#submit-setup) を参照してください。 サーバー応答の Client Secret を返すか、エラーをスローします。EmbeddedPaymentElement は、クライアントシークレットを使用して PaymentIntent を確認するか、ローカライズされたエラーメッセージを UI に表示します ( [errorDescription](https://developer.apple.com/documentation/foundation/localizederror/2946895-errordescription) または [localizedDescription](https://developer.apple.com/documentation/foundation/nserror/1414418-localizeddescription)) 確認が完了すると、EmbeddedPaymentElement は使用できなくなります。代わりに、ユーザーを領収書画面などに誘導します。 ```swift extension MyEmbeddedCheckoutView { func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // Make a request to your own server. Pass confirmationToken.stripeId if using server-side confirmation. // Return the client secret or throw an error return try await MyAPIClient.shared.createIntent(confirmationTokenId: confirmationToken.stripeId) } } ``` ## Optional: 選択した決済オプションを消去する EmbeddedPaymentElement 以外の決済オプションがある場合は、選択した決済オプションの消去が必要になることがあります。これを行うには、`clearPaymentOption` API を使用して、選択した決済オプションの選択を解除します。 #### UIKit ```swift extension MyCheckoutVC: UIViewController { func deselectPaymentMethod() { embeddedPaymentElement?.clearPaymentOption() } } ``` #### SwiftUI ```swift @available(iOS 15.0, *) extension MyEmbeddedCheckoutView { func deselectPaymentMethod() { embeddedViewModel.clearPaymentOption() } } ``` ## Optional: 同意書を自分で表示する デフォルトでは、Embedded Mobile Payment Element は、規制遵守を維持するための同意書と法的免責事項を表示します。このテキストは購入ボタンの近くに配置する必要があります。必要に応じて、ビューでの表示を無効にし、代わりに自分で表示します。 > 規制を遵守するため、実装で同意書のテキストを表示する必要があります。テキスト内の URL を UITextView などを使用して開けることを確認してください。 #### UIKit ```swift var configuration = EmbeddedPaymentElement.Configuration(...) configuration.embeddedViewDisplaysMandateText = true ... let mandateTextView = UITextView() mandateTextView.attributedText = embeddedPaymentElement.paymentOption.mandateText ``` #### SwiftUI ```swift var configuration = EmbeddedPaymentElement.Configuration(...) configuration.embeddedViewDisplaysMandateText = true ... if let attributedText = embeddedViewModel.paymentOption?.mandateText { Text(AttributedString(attributedText)) } ``` ## Optional: 顧客がシートですぐに設定を確定できるようにする 設定をすぐに確定するようにフォーム画面のボタンを設定するには、`EmbeddedPaymentElement.Configuration` オブジェクトで `formSheetAction` を設定します。 画面が閉じられた後、設定結果によって完了ブロックが実行されます。埋め込み UI は設定完了後には使用できなくなるため、実装でユーザーを領収書画面など別の画面に誘導することをお勧めします。 ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.formSheetAction = .confirm(completion: { result in switch result { case .completed: // Setup completed. You can for example, show a confirmation screen. print("Completed") case .failed(let error): // Encountered an unrecoverable error. You can display the error to the user, log it, etc. print(error) case .canceled: // Customer canceled - you should probably do nothing. break } }) ``` ## SetupIntent を作成する [サーバー側] サーバー側で *SetupIntent* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) を作成し、これをします。決済手段は[ダッシュボード](https://dashboard.stripe.com/settings/payment_methods)から管理できます。Stripe は、決済手段の制約とその他のパラメーターを評価して、利用可能な決済手段のリストを決定します。 コールが成功した場合は、SetupIntent *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)) を返します。コールが失敗した場合は、[エラーを処理](https://docs.stripe.com/error-handling.md)して、エラーメッセージと顧客向けの簡単な説明を返します。 > すべての IntentConfiguration プロパティが SetupIntent ([usage](https://docs.stripe.com/api/setup_intents/object.md#setup_intent_object-usage) など) と一致していることを確認します。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { customer: ..., # The Customer ID you previously created automatic_payment_methods: {enabled: true}, } begin intent = client.v1.setup_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` ## 戻り先 URL を設定する [クライアント側] 顧客はお客様のアプリから離れて、(Safari やバンキングアプリなどで) 認証する場合があります。ユーザーが認証後にアプリに自動的に戻れるようにするには、[カスタム URL スキームを構成](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app)し、URL を SDK に転送するようにアプリのデリゲートを設定します。Stripe は[ユニバーサルリンク](https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content)には対応していません。 #### SceneDelegate #### Swift ```swift // This method handles opening custom URL schemes (for example, "your-app://stripe-redirect") func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { guard let url = URLContexts.first?.url else { return } let stripeHandled = StripeAPI.handleURLCallback(with: url) if (!stripeHandled) { // This was not a Stripe url – handle the URL normally as you would } } ``` #### AppDelegate #### Swift ```swift // This method handles opening custom URL schemes (for example, "your-app://stripe-redirect") func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { let stripeHandled = StripeAPI.handleURLCallback(with: url) if (stripeHandled) { return true } else { // This was not a Stripe url – handle the URL normally as you would } return false } ``` #### SwiftUI #### Swift ```swift @main struct MyApp: App { var body: some Scene { WindowGroup { Text("Hello, world!").onOpenURL { incomingURL in let stripeHandled = StripeAPI.handleURLCallback(with: incomingURL) if (!stripeHandled) { // This was not a Stripe url – handle the URL normally as you would } } } } } ``` さらに、[EmbeddedPaymentElement.Configuration](https://github.com/stripe/stripe-ios/blob/aa3234a7fafde98c9203b6ed77e0278c04310eb0/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L12) オブジェクトの [returnURL](https://github.com/stripe/stripe-ios/blob/aa3234a7fafde98c9203b6ed77e0278c04310eb0/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L70) をアプリの URL に設定します。 ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.returnURL = "your-app://stripe-redirect" ``` ## 保存された支払い方法に後で請求する [サーバー側] > `bancontact` と `ideal` は、デフォルトでは 1 回限りの支払い方法です。以降も使用できるように設定すると、再利用可能な支払い方法タイプ `sepa_debit` が生成されます。このため、保存された支払い方法を照会するには `sepa_debit` を使用する必要があります。 > #### 法令遵守 > > 顧客の支払いの詳細を保存する際、お客様は適用されるすべての法律、規制、ネットワークの規則に準拠する責任があります。将来の購入に備えて顧客に過去の決済手段を提供する際は、必ず、特定の将来の使用に備えて決済手段の詳細を保存することについての同意を顧客から収集した決済手段をリストアップします。顧客に関連付けられた決済手段のうち、将来の購入用の保存済みの決済手段として顧客に提示できるものと提示できないものを区別するには、[allow_redisplay](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-allow_redisplay) パラメーターを使用します。 請求する決済手段を見つけるには、顧客に関連付けられている決済手段を一覧表示します。この例ではカードを一覧表示していますが、サポートされている任意の [type](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-type) を一覧表示できます。 > #### Accounts v2 API を使用した顧客の表現 > > Accounts v2 API では、Connect ユーザーには一般提供され、その他の Stripe ユーザーには公開プレビューで提供されます。Accounts v2 プレビューの一部である場合は、コードで[プレビューバージョン](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning)を指定する必要があります。 > > Accounts v2 プレビューへのアクセスをリクエストするには、 > > ほとんどのユースケースでは、[Customer](https://docs.stripe.com/api/customers.md) オブジェクトを使用するのではなく、[顧客を顧客設定済みの Account オブジェクトとしてモデル化する](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md)ことをお勧めします。 #### Accounts v2 ```curl curl -G https://api.stripe.com/v1/payment_methods \ -u "<>:" \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d type=card ``` #### Customers v1 ```curl curl -G https://api.stripe.com/v1/payment_methods \ -u "<>:" \ -d "customer={{CUSTOMER_ID}}" \ -d type=card ``` 顧客に *オフセッション* (A payment is described as off-session if it occurs without the direct involvement of the customer, using previously-collected payment information) で請求する準備ができたら、`Customer`または顧客設定の`Account`の ID と `PaymentMethod` ID を使用して、決済の金額と通貨を指定した `PaymentIntent` を作成します。オフセッション決済を行うには、他にもいくつかのパラメーターを設定します。 - 顧客が決済フローにおらず、カード発行会社や銀行などのパートナーからの認証リクエストに対応できないことを示すには、[off_session](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-off_session) を true に設定します。決済フロー中にパートナー (カード発行会社や銀行など) が認証をリクエストした場合、Stripe は以前の *オンセッション* (A payment is described as on-session if it occurs while the customer is actively in your checkout flow and able to authenticate the payment method) 取引の顧客情報を使用して免除をリクエストします。免除の条件が満たされない場合、`PaymentIntent` がエラーを返すことがあります。 - [confirm](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-confirm) を true に設定すると、`PaymentIntent` の作成時に即座に確定がトリガーされる。 - [payment_method](https://docs.stripe.com/api.md#create_payment_intent-payment_method) を `PaymentMethod` の ID に設定します。 - 導入で顧客をどのように表すかに応じて、[customer_account](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer_account) を顧客設定の `Account` の ID に設定するか、[customer](https://docs.stripe.com/api.md#create_payment_intent-customer) を `Customer` の ID に設定します。 #### Accounts v2 ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=usd \ -d "automatic_payment_methods[enabled]=true" \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d payment_method={{PAYMENT_METHOD_ID}} \ --data-urlencode "return_url=https://example.com/order/123/complete" \ -d off_session=true \ -d confirm=true ``` #### Customers v1 ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=usd \ -d "automatic_payment_methods[enabled]=true" \ -d "customer={{CUSTOMER_ID}}" \ -d payment_method={{PAYMENT_METHOD_ID}} \ --data-urlencode "return_url=https://example.com/order/123/complete" \ -d off_session=true \ -d confirm=true ``` ## 実装をテストする #### カード | カード番号 | シナリオ | テスト方法 | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | | 4242424242424242 | カード支払いは成功し、認証は必要とされません。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000002500003155 | カード支払いには*認証* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase)が必要です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000000000009995 | カードは、`insufficient_funds` などの拒否コードで拒否されます。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 6205500000000000004 | UnionPay カードは、13 ~ 19 桁の可変長です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | #### 銀行へのリダイレクト | 決済手段 | シナリオ | テスト方法 | | ---------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Bancontact、iDEAL | 顧客は、リダイレクトベースの即時通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | リダイレクトベースの任意の支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払い失敗)** をクリックします。 | | Pay by Bank | 顧客はリダイレクトベースの、[遅延通知型](https://docs.stripe.com/payments/payment-methods.md#payment-notification)の支払い方法で支払いに成功します。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Complete test payment (テスト支払い完了)** をクリックします。 | | Pay by Bank | 顧客は、リダイレクトベースの遅延通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払いを失敗させる)** をクリックします。 | | BLIK | BLIK による支払いはさまざまな理由で失敗します。即時の失敗 (コードの有効期限切れや無効など)、遅延型のエラー (銀行による拒否など)、またはタイムアウト (顧客が時間内に応答しなかったなど) などがあります。 | メールパターンを使用して[さまざまな失敗をシミュレーションします。](https://docs.stripe.com/payments/blik/accept-a-payment.md#simulate-failures) | #### 口座引き落とし | 決済手段 | シナリオ | テスト方法 | | -------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | SEPA ダイレクトデビット | 顧客が SEPA ダイレクトデビットによる支払いに成功しました。 | 口座番号 `AT321904300235473204` を使用して、フォームに入力します。確定された PaymentIntent のステータスはまず、processing に移行し、3 分後に succeeded ステータスに移行します。 | | SEPA ダイレクトデビット | 顧客の Payment Intent のステータスが、`processing` から `requires_payment_method` に移行します。 | 口座番号 `AT861904300235473202` を使用して、フォームに入力します。 | 実装内容をテストするためのその他の情報については、[テスト](https://docs.stripe.com/testing.md)をご覧ください。 ## カードのスキャンを有効にする iOS をサポートするカードスキャン機能を有効にするには、アプリケーションの `Info.plist` の `NSCameraUsageDescription` (**プライバシー - カメラ利用の詳細**)を設定し、カメラにアクセスする理由を入力して下さい (例:「カードをスキャンするため」)。 ## Optional: 保存済みのカードを有効にする [サーバー側] [クライアント側] `EmbeddedPaymentElement` では、顧客がカードを保存でき、利用可能な決済手段に顧客が保存済みのカードを含めることもできます。顧客は、サーバー上で関連付けられた顧客設定済みの [Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) オブジェクトまたは [Customer](https://docs.stripe.com/api/customers/create.md) オブジェクトを持っている必要があります。顧客がカードを保存できるようにするチェックボックスを有効にするには、[CustomerSession](https://docs.stripe.com/api/customer_sessions.md) を作成し、`payment_method_save` を `enabled` に設定します。 #### Accounts v2 ```javascript const stripe = require('stripe')('sk_test_your_secret_key'); app.post('/mobile-payment-element', async (req, res) => { // Use an existing Account ID if this is a returning customer. const customer_account = await stripe.v2.core.accounts.create(); const customerSession = await stripe.customerSessions.create({ customer_account: customer_account.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer_account: customer_account.id, }); }); ``` 次に、顧客の ID と CustomerSession の client secret を使用して EmbeddedPaymentElement を設定します。 #### UIKit ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerAccountId, customerSessionClientSecret: customerSessionClientSecret) self.embeddedPaymentElement = try await EmbeddedPaymentElement.create(..., configuration: configuration) ``` #### SwiftUI ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerAccountId, customerSessionClientSecret: customerSessionClientSecret) try await embeddedViewModel.load(..., configuration: configuration) ``` #### Customers v1 ```javascript const stripe = require('stripe')('sk_test_your_secret_key'); app.post('/mobile-payment-element', async (req, res) => { // Use an existing Customer ID if this is a returning customer. const customer = await stripe.customers.create(); const customerSession = await stripe.customerSessions.create({ customer: customer.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer: customer.id, }); }); ``` 次に、顧客の ID と CustomerSession の client secret を使用して EmbeddedPaymentElement を設定します。 #### UIKit ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerId, customerSessionClientSecret: customerSessionClientSecret) self.embeddedPaymentElement = try await EmbeddedPaymentElement.create(..., configuration: configuration) ``` #### SwiftUI ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerId, customerSessionClientSecret: customerSessionClientSecret) try await embeddedViewModel.load(..., configuration: configuration) ``` ## Optional: 遅延型の支払い方法を許可する [クライアント側] *遅延型の決済手段* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods)では、購入の終了時に顧客から売上を受け取ることが保証されません。これは、決済に時間がかかる (アメリカの銀行口座、SEPA デビット、iDEAL、Bancontact など) 場合や、完了に顧客の対応を必要とする (OXXO、コンビニ決済、Boleto など) 場合があるためです。 デフォルトでは、EmbeddedPaymentElement は遅延型の決済手段を表示しません。オプトインするには、`EmbeddedPaymentElement.Configuration` で `allowsDelayedPaymentMethods` を true に設定します。このステップのみでは、特定の決済手段を有効にすることはできませんが、アプリがその決済手段に対応できることが示されます。たとえば、現在 OXXO は EmbeddedPaymentElement でサポートされていませんが、サポートされるようになり、最新の SDK バージョンに更新すると、実装に関する追加の変更なしで、アプリに決済オプションとして OXXO を表示できます。 #### UIKit ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.allowsDelayedPaymentMethods = true self.embeddedPaymentElement = try await EmbeddedPaymentElement.create(..., configuration: configuration) ``` #### SwiftUI ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.allowsDelayedPaymentMethods = true try await embeddedViewModel.load(..., configuration: configuration) ``` 顧客が EmbeddedPaymentElement でこれらの遅延型の決済手段のいずれかを適切に使用すると、`.completed` の支払い結果が返されます。 ## Optional: Apple Pay を有効にする > 決済画面に専用の **Apple Pay** ボタンがある場合は、[Apple Pay ガイド](https://docs.stripe.com/apple-pay.md#present-payment-sheet)に従い、`ApplePayContext` を使用して **Apple Pay** ボタンから支払いを回収します。`EmbeddedPaymentElement` を使用して、他のタイプの決済手段に対応することも可能です。 ### Apple 加盟店 ID を登録する Apple Developer Web サイトで [新規 ID を登録](https://developer.apple.com/account/resources/identifiers/add/merchant) して、Apple 加盟店 ID を取得します。 フォームに説明と ID を入力します。説明はお客様の記録用であり、後で変更できます。アプリの名前を ID として使用することをお勧めします (`merchant.com.{{YOUR_APP_NAME}}` など)。 ### 新しい Apple Pay 証明書を作成する 支払いデータを暗号化するためのアプリの証明書を作成します。 ダッシュボードの [iOS certificate settings (iOS 証明書の設定)](https://dashboard.stripe.com/settings/ios_certificates) に移動して、**新規アプリケーションを追加**をクリックし、表示されるガイドに従います。 証明書署名リクエスト (CSR) ファイルをダウンロードして、Apple Pay の利用を可能にする安全な証明書を Apple から取得します。 1 つの CSR ファイルを使用して証明書を 1 つだけ発行する必要があります。Apple 加盟店 ID を切り替えた場合、ダッシュボードの [iOS Certificate Settings (iOS 証明書の設定)](https://dashboard.stripe.com/settings/ios_certificates) に移動して、新しい CSR と証明書を取得する必要があります。 ### Xcode を導入する Apple Pay ケイパビリティをアプリに追加します。Xcode でプロジェクト設定を開き、**Signing & Capabilities (署名およびケイパビリティ)** タブを選択して、**Apple Pay** ケイパビリティを追加します。この段階で開発者アカウントへのログインを要求される場合があります。前の手順で作成した加盟店 ID を選択すると、アプリで Apple Pay を受け付けられるようになります。 ![](https://b.stripecdn.com/docs-statics-srv/assets/xcode.a701d4c1922d19985e9c614a6f105bf1.png) Xcode で Apple Pay ケイパビリティを有効化する ### Apple Pay を追加する #### 1 回限りの支払い EmbeddedPaymentElement に Apple Pay を追加するには、Apple マーチャント ID とビジネスの[国コード](https://dashboard.stripe.com/settings/account)を使用して `EmbeddedPaymentElement.Configuration` を初期化してから、[applePay](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L28) を設定します。 #### iOS (Swift) ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.applePay = .init( merchantId: "merchant.com.your_app_name", merchantCountryCode: "US" ) ``` #### 継続支払い EmbeddedPaymentElement に Apple Pay を追加するには、Apple マーチャント ID とビジネスの[国コード](https://dashboard.stripe.com/settings/account)を使用して `EmbeddedPaymentElement.Configuration` を初期化してから、[applePay](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L28) を設定します。 継続支払いに関する [Apple のガイドライン](https://developer.apple.com/design/human-interface-guidelines/apple-pay#Supporting-subscriptions)に従い、`PKPaymentRequest` で追加の属性を設定する必要もあります。[ApplePayConfiguration.paymentRequestHandlers](https://stripe.dev/stripe-ios/stripepaymentsheet/documentation/stripepaymentsheet/paymentsheet/applepayconfiguration/handlers/paymentrequesthandler) にハンドラーを追加して、請求する予定の金額 (たとえば月額 9.95 USD) を指定して [PKPaymentRequest.paymentSummaryItems](https://developer.apple.com/documentation/passkit/pkpaymentrequest/1619231-paymentsummaryitems) を設定します。 `PKPaymentRequest` で `recurringPaymentRequest` または `automaticReloadPaymentRequest` プロパティを設定することで、[加盟店トークン](https://developer.apple.com/apple-pay/merchant-tokens/)を導入することもできます。 Apple Pay で継続支払いを使用する方法について、詳細は [Apple の PassKit に関するドキュメント](https://developer.apple.com/documentation/passkit/pkpaymentrequest)をご覧ください。 #### iOS (Swift) ```swift let customHandlers = EmbeddedPaymentElement.ApplePayConfiguration.Handlers( paymentRequestHandler: { request in // PKRecurringPaymentSummaryItem is available on iOS 15 or later if #available(iOS 15.0, *) { let billing = PKRecurringPaymentSummaryItem(label: "My Subscription", amount: NSDecimalNumber(string: "59.99")) // Payment starts today billing.startDate = Date() // Payment ends in one year billing.endDate = Date().addingTimeInterval(60 * 60 * 24 * 365) // Pay once a month. billing.intervalUnit = .month billing.intervalCount = 1 // recurringPaymentRequest is only available on iOS 16 or later if #available(iOS 16.0, *) { request.recurringPaymentRequest = PKRecurringPaymentRequest(paymentDescription: "Recurring", regularBilling: billing, managementURL: URL(string: "https://my-backend.example.com/customer-portal")!) request.recurringPaymentRequest?.billingAgreement = "You'll be billed $59.99 every month for the next 12 months. To cancel at any time, go to Account and click 'Cancel Membership.'" } request.paymentSummaryItems = [billing] request.currencyCode = "USD" } else { // On older iOS versions, set alternative summary items. request.paymentSummaryItems = [PKPaymentSummaryItem(label: "Monthly plan starting July 1, 2022", amount: NSDecimalNumber(string: "59.99"), type: .final)] } return request } ) var configuration = EmbeddedPaymentElement.Configuration() configuration.applePay = .init(merchantId: "merchant.com.your_app_name", merchantCountryCode: "US", customHandlers: customHandlers) ``` ### 注文の追跡 iOS 16 以降で[注文の追跡](https://developer.apple.com/design/human-interface-guidelines/technologies/wallet/designing-order-tracking)情報を追加するには、`PaymentSheet.ApplePayConfiguration.Handlers` で [authorizationResultHandler](https://stripe.dev/stripe-ios/stripepaymentsheet/documentation/stripepaymentsheet/paymentsheet/applepayconfiguration/handlers/authorizationresulthandler) を設定します。支払いの完了後、Stripe は iOS が Apple Pay の決済画面を閉じる前に実装を呼び出します。 `authorizationResultHandler` の実装で、完了した注文の注文の詳細をサーバーから取得します。提供された [PKPaymentAuthorizationResult](https://developer.apple.com/documentation/passkit/pkpaymentauthorizationresult) に詳細を追加し、変更された結果を返します。 注文の追跡の詳細については、[Apple のウォレットでの注文に関するドキュメント](https://developer.apple.com/documentation/walletorders)をご覧ください。 #### iOS (Swift) ```swift let customHandlers = PaymentSheet.ApplePayConfiguration.Handlers( authorizationResultHandler: { result in do { // Fetch the order details from your service let myOrderDetails = try await MyAPIClient.shared.fetchOrderDetails(orderID: orderID) result.orderDetails = PKPaymentOrderDetails( orderTypeIdentifier: myOrderDetails.orderTypeIdentifier, // "com.myapp.order" orderIdentifier: myOrderDetails.orderIdentifier, // "ABC123-AAAA-1111" webServiceURL: myOrderDetails.webServiceURL, // "https://my-backend.example.com/apple-order-tracking-backend" authenticationToken: myOrderDetails.authenticationToken) // "abc123" // Return your modified PKPaymentAuthorizationResult return result } catch { return PKPaymentAuthorizationResult(status: .failure, errors: [error]) } } ) var configuration = EmbeddedPaymentElement.Configuration() configuration.applePay = .init(merchantId: "merchant.com.your_app_name", merchantCountryCode: "US", customHandlers: customHandlers) ``` ## Optional: 構成要素をカスタマイズする すべてのカスタマイズは、[EmbeddedPaymentElement.Configuration](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L12) オブジェクトで設定されます。 ### デザイン [Appearance API](https://docs.stripe.com/elements/appearance-api/embedded-mobile.md?platform=ios) を使用して、アプリのデザインに合うように色やフォントなどをカスタマイズします。 ### ユーザーの住所を収集する [Address Element](https://docs.stripe.com/elements/address-element.md?platform=ios) を使用して、顧客から国内および国外の配送先住所や請求先住所を収集します。 ### 加盟店の表示名 [merchantDisplayName](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L66) を設定し、顧客に表示するビジネス名を指定します。デフォルトではアプリ名になります。 #### Swift ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.merchantDisplayName = "My app, Inc." ``` ### ダークモード `EmbeddedPaymentElement` は、ユーザーのシステム全体の表示設定 (ライトモードとダークモード) に合わせて自動的に調整されます。アプリがダークモードに対応していない場合は、[スタイル](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L51)を `alwaysLight` または `alwaysDark` モードに設定できます。 ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.style = .alwaysLight ``` # 支払いを受け付けて決済手段を保存する > This is a 支払いを受け付けて決済手段を保存する for when platform is ios and type is paymentsfu. View the full page at https://docs.stripe.com/payments/mobile/accept-payment-embedded?platform=ios&type=paymentsfu. [Payment Intents API](https://docs.stripe.com/api/payment_intents.md) を使用して、購入から支払い詳細を保存します。以下のようなユースケースがあります。 - 顧客に EC ストアの注文の請求を行い、以降の購入に備えて情報を保存します。 - 継続支払いの初回の支払いを開始します。 - 頭金を請求し、情報を保存して後から全額を請求できるようにします。 > #### カード提示取引 > > Stripe Terminal を介した支払いなどのカード提示取引では、別のプロセスを使用して決済手段を保存します。詳細については、[Terminal のドキュメント](https://docs.stripe.com/terminal/features/saving-payment-details/save-after-payment.md)をご覧ください。 ## 法令遵守 顧客の決済情報を将来の利用に備えて保存する際 (将来の購入に向けた決済フローでの決済手段の表示、または顧客がウェブサイトやアプリを積極的に使用していないときの請求など) は、適用されるすべての法令、規制、ネットワーク規則を遵守する責任があります。顧客の決済手段を保存または請求する前に、以下を確認してください。 - 決済手段の詳細を保存する方法を示す、次のような規約をウェブサイトまたはアプリに追加します。 - 指定された取引において顧客に代わって決済または一連の決済を開始することに対する顧客の同意。 - 予期される支払い時期と支払い頻度 (たとえば、請求が予定されている分割払いなのか、サブスクリプションの決済なのか、あるいは予定されていないトップアップなのか)。 - 支払い金額の決定方法 - 支払い方法をサブスクリプションサービスに使用する場合は、キャンセルに関するポリシー - 規約に記載されている目的にのみ、保存された決済手段を使用してください。 - この特定の使用について顧客から明示的な同意を取得します。たとえば、「将来の使用に備えて決済手段を保存する」チェックボックスを含めます。 - 顧客の規約への書面による同意を記録します。 ## Stripe を設定する [サーバー側] [クライアント側] ### サーバー側 この組み込みは、Stripe API と通信するサーバー上にエンドポイントを必要とします。サーバーから Stripe API にアクセスするには、次のように Stripe の公式ライブラリーを使用します。 #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### クライアント側 [Stripe iOS SDK](https://github.com/stripe/stripe-ios) はオープンソースです。[詳細なドキュメントが提供されており](https://stripe.dev/stripe-ios/index.html)、iOS 13 以降をサポートするアプリと互換性があります。 #### Swift Package Manager SDK をインストールするには、以下のステップに従います。 1. Xcode で、**File (ファイル)** > **Add Package Dependencies… (パッケージ依存関係を追加)** を選択し、リポジトリー URL として `https://github.com/stripe/stripe-ios-spm` を入力します。 1. [リリースページ](https://github.com/stripe/stripe-ios/releases)から最新のバージョン番号を選択します。 1. **StripePaymentSheet** 製品を[アプリのターゲット](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app)に追加します。 #### CocoaPods 1. まだインストールしていない場合は、[CocoaPods](https://guides.cocoapods.org/using/getting-started.html) の最新バージョンをインストールします。 1. 既存の [Podfile](https://guides.cocoapods.org/syntax/podfile.html) がない場合は、以下のコマンドを実行して作成します。 ```bash pod init ``` 1. この行を `Podfile` に追加します。 ```podfile pod 'StripePaymentSheet' ``` 1. 以下のコマンドを実行します。 ```bash pod install ``` 1. これ以降は、Xcode でプロジェクトを開く際に、`.xcodeproj` ファイルではなく、必ず `.xcworkspace` ファイルを使用するということを忘れないでください。 1. 今後、SDK の最新バージョンに更新するには、以下を実行します。 ```bash pod update StripePaymentSheet ``` #### Carthage 1. まだインストールしていない場合は、[Carthage](https://github.com/Carthage/Carthage#installing-carthage) の最新バージョンをインストールします。 1. この行を `Cartfile` に追加します。 ```cartfile github "stripe/stripe-ios" ``` 1. [Carthage のインストール手順](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos)に従います。必ず、[こちら](https://github.com/stripe/stripe-ios/tree/master/StripePaymentSheet/README.md#manual-linking)にリストされている必要なフレームワークのすべてを埋め込んでください。 1. 今後、SDK の最新バージョンに更新するには、以下のコマンドを実行します。 ```bash carthage update stripe-ios --platform ios ``` #### 手動のフレームワーク 1. Stripe の [GitHub リリースページ](https://github.com/stripe/stripe-ios/releases/latest)に移動して、**Stripe.xcframework.zip** をダウンロードして解凍します。 1. **StripePaymentSheet.xcframework** を、Xcode プロジェクトの **General (一般) ** 設定の **Embedded Binaries (埋め込みバイナリー)** セクションにドラッグします。**Copy items if needed (必要に応じてアイテムをコピーする)** を必ず選択してください。 1. [こちら](https://github.com/stripe/stripe-ios/tree/master/StripePaymentSheet/README.md#manual-linking)にリストされている必要なフレームワークのすべてに対して、ステップ 2 を繰り返します。 1. 今後、Stripe の SDK の最新バージョンに更新するには、ステップ 1 から 3 を繰り返します。 > SDK の最新リリースおよび過去バージョンの詳細については、GitHub の [Releases (リリース)](https://github.com/stripe/stripe-ios/releases) ページをご覧ください。リポジトリの[リリースをウォッチ](https://help.github.com/en/articles/watching-and-unwatching-releases-for-a-repository#watching-releases-for-a-repository)して、新しいリリースの公開時に通知を受け取ることも可能です。 また、SDK が Stripe への API コールを実行できるように、[公開可能キー](https://dashboard.stripe.com/apikeys)を設定する必要もあります。開始するには、導入中にクライアント側で公開可能キーをハードコード化できますが、本番環境ではサーバーから公開可能キーを取得します。 ```swift // Set your publishable key: remember to change this to your live publishable key in production // See your keys here: https://dashboard.stripe.com/apikeys STPAPIClient.shared.publishableKey = "<>" ``` ## 支払い方法を有効にする [支払い方法の設定](https://dashboard.stripe.com/settings/payment_methods)を表示して、サポートする支払い方法を有効にします。*PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成するには、少なくとも 1 つは支払い方法を有効にする必要があります。 多くの顧客から決済を受け付けられるよう、Stripe では、カードやその他一般的な決済手段がデフォルトで有効になっていますが、ビジネスや顧客に適した追加の決済手段を有効にすることをお勧めします。プロダクトと決済手段のサポートについては[決済手段のサポート](https://docs.stripe.com/payments/payment-methods/payment-method-support.md)を、手数料については[料金体系ページ](https://stripe.com/pricing/local-payment-methods)をご覧ください。 ## 顧客を作成する [サーバー側] 以降の決済のために決済手段を設定するには、顧客を表すオブジェクトに関連付ける必要があります。顧客がアカウントを作成するか、ビジネスと最初の取引を行うときに、[Customer](https://docs.stripe.com/api/customers/create.md) オブジェクトを作成します。 ```curl curl -X POST https://api.stripe.com/v1/customers \ -u "<>:" ``` ## 支払いの詳細を収集する [クライアント側] Embedded Mobile Payment Element は、ネイティブモバイルアプリの決済ページに配置するように設計されています。この Element には決済手段の一覧が表示され、アプリのデザインに合わせてカスタマイズできます。 顧客が**カード**行をタップすると、決済手段の詳細を入力できる画面が表示されます。画面にデフォルトで**続行**というボタンが表示され、タップすると画面が閉じます。これにより、顧客は決済フローで支払いを完了できます。 ![Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/ios-embedded.b5867c116d537ffcb920dd80ebdfc741.png) また、続行するのではなく、すぐに支払いを完了するようにボタンを設定することもできます。これを行うには、ガイドに沿って手順を実行した後で、[このステップ](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#embedded-let-customer-pay-immediately)を完了します。 #### UIKit ### Payment Element を初期化する `create` を呼び出し、`EmbeddedPaymentElement.Configuration` と [PaymentSheet.IntentConfiguration](https://github.com/stripe/stripe-ios/blob/master/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetIntentConfiguration.swift) を使用して EmbeddedPaymentElement をインスタンス化します。 Configuration オブジェクトには、`returnURL` のような、決済間で変更されない EmbeddedPaymentElement の汎用設定オプションが含まれます。`IntentConfiguration` オブジェクトには、金額や通貨などの特定の決済に関する詳細と、`confirmationTokenConfirmHandler` コールバックが含まれます。今のところ、その実装は空のままにしておきます。初期化に成功したら、`presentingViewController` と `delegate` プロパティを設定します。 > EmbeddedPaymentElement に決済手段を保存するには、`setupFutureUsage` を設定します。`IntentConfiguration` の [setupFutureUsage](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-setup_future_usage) パラメーターを `onSession` または `offSession` に設定できます。この値は、利用可能なすべての決済手段に自動的に適用されます。 ```swift import StripePaymentSheet class MyCheckoutVC: UIViewController { func createEmbeddedPaymentElement() async throws -> EmbeddedPaymentElement { let intentConfig = PaymentSheet.IntentConfiguration( mode: .payment(amount: 1099, currency: "USD", setupFutureUsage: .offSession) ) { [weak self] confirmationToken in return await self?.handleConfirmationToken(confirmationToken) } var configuration = EmbeddedPaymentElement.Configuration() configuration.returnURL = "your-app://stripe-redirect" // Use the return url you set up in the previous step let embeddedPaymentElement = try await EmbeddedPaymentElement.create(intentConfiguration: intentConfig, configuration: configuration) embeddedPaymentElement.presentingViewController = self embeddedPaymentElement.delegate = self return embeddedPaymentElement } func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // You'll implement this in the "Confirm the payment" section below } } ``` ### Payment Element ビューを追加する EmbeddedPaymentElement が正常に初期化されたら、そのビューを決済 UI に配置します。 > ビューは固定サイズではなく、最初にレンダリングされた後に高さが変わる可能性があるため、UIScrollView などのスクロール可能なビュー内に含める必要があります。 ```swift class MyCheckoutVC: UIViewController { // ... private(set) var embeddedPaymentElement: EmbeddedPaymentElement? private lazy var checkoutButton: UIButton = { let checkoutButton = UIButton(type: .system) checkoutButton.backgroundColor = .systemBlue checkoutButton.layer.cornerRadius = 5.0 checkoutButton.clipsToBounds = true checkoutButton.setTitle("Checkout", for: .normal) checkoutButton.setTitleColor(.white, for: .normal) checkoutButton.translatesAutoresizingMaskIntoConstraints = false checkoutButton.isEnabled = embeddedPaymentElement?.paymentOption != nil checkoutButton.addTarget(self, action: #selector(didTapConfirmButton), for: .touchUpInside) return checkoutButton }() // ... @objc func didTapConfirmButton() { // You'll implement this in the "Confirm the payment" section below } override func viewDidLoad() { super.viewDidLoad() Task { @MainActor in do { // Create a UIScrollView let scrollView = UIScrollView() scrollView.translatesAutoresizingMaskIntoConstraints = false self.view.addSubview(scrollView) // Create the Payment Element let embeddedPaymentElement = try await createEmbeddedPaymentElement() embeddedPaymentElement.delegate = self embeddedPaymentElement.presentingViewController = self self.embeddedPaymentElement = embeddedPaymentElement // Add its view to the scroll view scrollView.addSubview(embeddedPaymentElement.view) // Add your own checkout button to the scroll view scrollView.addSubview(checkoutButton) // Set up layout constraints embeddedPaymentElement.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), embeddedPaymentElement.view.topAnchor.constraint(equalTo: scrollView.topAnchor), embeddedPaymentElement.view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), embeddedPaymentElement.view.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), checkoutButton.topAnchor.constraint(equalTo: embeddedPaymentElement.view.bottomAnchor, constant: 4.0), checkoutButton.leadingAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.leadingAnchor, constant: 4.0), checkoutButton.trailingAnchor.constraint(equalTo: scrollView.safeAreaLayoutGuide.trailingAnchor, constant: -4.0), ]) } catch { // Handle view not being added to view } } } } ``` この時点で、アプリを実行して、Embedded Mobile Payment Element を確認できます。 ### 高さの変更に対応する EmbeddedPaymentElement のビューは、サイズが大きくなり、または小さくなる可能性があり、ビューのレイアウトに影響を与える可能性があります。 高さの変更に対応するには、`embeddedPaymentElementDidUpdateHeight` デリゲートメソッドを実装します。EmbeddedPaymentElement のビューは、高さを更新するアニメーションブロック内でこのメソッドを呼び出します。実装では、高さ変更のアニメーションをスムーズに行うために、EmbeddedPaymentElement のビューを含むスクロールビューで `setNeedsLayout()` と `layoutIfNeeded()` を呼び出すことが想定されます。 ```swift extension MyCheckoutVC: EmbeddedPaymentElementDelegate { func embeddedPaymentElementDidUpdateHeight(embeddedPaymentElement: StripePaymentSheet.EmbeddedPaymentElement) { // Handle layout appropriately self.view.setNeedsLayout() self.view.layoutIfNeeded() } } ``` ビューが高さの変更に適切に応答することをテストすることを推奨します。これを行うには、`testHeightChange()` を EmbeddedPaymentElement 上で呼び出し、エレメント内の同意書の表示と非表示をシミュレートします。`testHeightChange()` を呼び出した後、スクロールビューがスムーズに調整されることを確認してください。 ```swift class MyCheckoutVC: UIViewController { override func viewDidLoad() { // This is only for testing purposes: #if DEBUG Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in Task { @MainActor in self?.embeddedPaymentElement?.testHeightChange() } } #endif } } ``` ### (任意)選択した支払いオプションを表示する ラベル (「····4242」など)、画像 (VISA ロゴなど)、UI に表示する請求先情報など、顧客が選択した決済オプションの詳細にアクセスする必要がある場合は、EmbeddedPaymentElement の `paymentOption` プロパティを使用します。 `paymentOption` が変更されたときに通知を受けるには、`embeddedPaymentElementDidUpdatePaymentOption` デリゲートメソッドを実装します。 ```swift extension MyCheckoutVC: EmbeddedPaymentElementDelegate { func embeddedPaymentElementDidUpdatePaymentOption(embeddedPaymentElement: EmbeddedPaymentElement) { print("The payment option changed: \(embeddedPaymentElement.paymentOption)") checkoutButton.isEnabled = embeddedPaymentElement.paymentOption != nil } } ``` ### (任意)支払いの詳細を更新する 顧客が決済情報を変更する操作 (割引コードの適用など) を実行したときに、`update` メソッドを呼び出して `EmbeddedPaymentElement` インスタンスを更新し、新しい値を反映します。Apple Pay や Google Pay など、一部の決済手段では UI に金額が表示されるため、常に正確で最新の状態を維持するようにしてください。 更新コールが完了したら、UI を更新します。更新コールにより、顧客が現在選択している決済オプションが変更される場合もあります。 ```swift extension MyCheckoutVC { func update() { Task { @MainActor in var updatedIntentConfig = oldIntentConfig // Update the amount to reflect the price after applying the discount code updatedIntentConfig.mode = PaymentSheet.IntentConfiguration.Mode.payment(amount: 999, currency: "USD", setupFutureUsage: .offSession) let result = await embeddedPaymentElement?.update(intentConfiguration: updatedIntentConfig) switch result { case .canceled, nil: // Do nothing; this happens when a subsequent `update` call cancels this one break case .failed(let error): // Display error to user in an alert, let users retry case .succeeded: // Update your UI in case the payment option changed } } } } ``` ### 支払いを確定する 顧客が決済ボタンをタップしたら、`embeddedPaymentElement.confirm()` を呼び出して支払いを完了します。確定する際は、ユーザーの操作を必ず無効にしてください。 ```swift extension MyCheckoutVC { @objc func didTapConfirmButton() { Task { @MainActor in guard let embeddedPaymentElement else { return } self.view.isUserInteractionEnabled = false // Disable user interaction, show a spinner, and so on before calling confirm. let result = await embeddedPaymentElement.confirm() switch result { case .completed: // Payment completed - show a confirmation screen. case .failed(let error): self.view.isUserInteractionEnabled = true // Encountered an unrecoverable error. You can display the error to the user, log it, and so on. case .canceled: self.view.isUserInteractionEnabled = true // Customer canceled - you should probably do nothing. break } } } } ``` 次に、`PaymentSheet.IntentConfiguration` に渡した `confirmationTokenConfirmHandler` コールバックを実装し、サーバーにリクエストを送信します。サーバーは PaymentIntent を作成し、Client Secret を返します。このプロセスについては、[PaymentIntent の作成](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#submit-payment) を参照してください。 リクエストが返されたら、サーバー応答のクライアントシークレットを返すか、エラーをスローします。EmbeddedPaymentElement は、クライアントシークレットを使用して PaymentIntent を確認するか、ローカライズされたエラーメッセージを UI に表示します ([errorDescription](https://developer.apple.com/documentation/foundation/localizederror/2946895-errordescription) または [localizedDescription](https://developer.apple.com/documentation/foundation/nserror/1414418-localizeddescription))。確認が完了すると、EmbeddedPaymentElement は使用できなくなります。代わりに、ユーザーを領収書画面などに誘導します。 ```swift extension MyCheckoutVC { func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // Make a request to your own server. Pass `confirmationToken.stripeId` if using server-side confirmation, and return the client secret or throw an error. let myServerClientSecret = try await fetchIntentClientSecret(...) } } ``` #### SwiftUI ### Payment Element を初期化する `load` を呼び出し、`EmbeddedPaymentElement.Configuration` と [PaymentSheet.IntentConfiguration](https://github.com/stripe/stripe-ios/blob/master/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetIntentConfiguration.swift) を指定して EmbeddedPaymentElementViewModel を読み込みます。 Configuration オブジェクトには、`returnURL` のような、決済間で変更されない EmbeddedPaymentElement の汎用設定オプションが含まれます。`IntentConfiguration` オブジェクトには、金額や通貨などの特定の決済に関する詳細と、`confirmationTokenConfirmHandler` コールバックが含まれます。今のところ、その実装は空のままにしておきます。 > EmbeddedPaymentElement に決済手段を保存するには、`setupFutureUsage` を設定します。`IntentConfiguration` の [setupFutureUsage](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-setup_future_usage) パラメータを `onSession` または `offSession` に設定できます。この値は、利用可能なすべての決済手段に自動的に適用されます。 ```swift import SwiftUI import StripePaymentSheet struct MyEmbeddedCheckoutView: View { // Store an `EmbeddedPaymentElementViewModel` as a `@StateObject` @StateObject var embeddedViewModel = EmbeddedPaymentElementViewModel() var body: some View { ScrollView { // Empty scroll view for now } .task { do { if !embeddedViewModel.isLoaded { // Load the view model with your configuration try await loadEmbeddedViewModel() } } catch { // On load failure, implement retry logic (automatic retry or user-triggered through UI). } } } private func loadEmbeddedViewModel() async throws { let intentConfiguration = PaymentSheet.IntentConfiguration( mode: .payment(amount: 1099, currency: "USD", setupFutureUsage: .offSession) ) { confirmationToken in try await self.handleConfirmationToken(confirmationToken) } var configuration = EmbeddedPaymentElement.Configuration() configuration.returnURL = "your-app://stripe-redirect" // Use the return url you set up in the previous step try await embeddedViewModel.load(intentConfiguration: intentConfiguration, configuration: configuration) } func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // You'll implement this in the "Confirm the payment" section below } } ``` ### Payment Element ビューを追加する EmbeddedPaymentElementViewModel が正常にロードされたら、決済 UI に EmbeddedPaymentElementView を追加します。 > ビューは固定サイズではなく、最初にレンダリングされた後に高さが変わる可能性があるため、ScrollView などのスクロール可能なビュー内に含める必要があります。 ```swift struct MyEmbeddedCheckoutView: View { var body: some View { ScrollView { // isLoaded becomes true only after a successful call to `embeddedViewModel.load()` if embeddedViewModel.isLoaded { EmbeddedPaymentElementView(viewModel: embeddedViewModel) } } } } ``` この時点で、アプリを実行して、Embedded Mobile Payment Element を確認できます。 ### 高さの変更に対応する EmbeddedPaymentElementView のサイズが大きくなり、または小さくなると、ビューのレイアウトに影響を与える可能性があります。EmbeddedPaymentElementView は、ScrollView 内にある場合、これを自動的に処理します。 ビューが高さの変更に適切に応答することをテストすることを推奨します。これを行うには、`testHeightChange()` を EmbeddedPaymentElementViewModel 上で呼び出し、エレメント内の同意書の表示と非表示をシミュレートします。`testHeightChange()` を呼び出した後、スクロールビューがスムーズに調整されることを確認してください。 ```swift struct MyEmbeddedCheckoutView: View { var body: some View { ScrollView { if embeddedViewModel.isLoaded { EmbeddedPaymentElementView(viewModel: embeddedViewModel) // For testing only #if DEBUG Button("Test height change") { embeddedViewModel.testHeightChange() } #endif } } } } ``` ### (任意)選択した支払いオプションを表示する ラベル (「····4242」など)、画像 (VISA ロゴなど)、UI に表示する請求先情報など、顧客が選択した決済オプションの詳細にアクセスする必要がある場合は、EmbeddedPaymentElementViewModel の `paymentOption` プロパティを使用します。顧客が、フォーム画面を開く決済手段を選択した場合は、画面で**続行**をタップすると、決済オプションが更新されます。 ```swift struct MyEmbeddedCheckoutView: View { var body: some View { ScrollView { if embeddedViewModel.isLoaded { // ... // A real integration probably wouldn't show the selected payment option on the same screen as the embedded payment element. We display it as an example. if let paymentOption = embeddedViewModel.paymentOption { HStack { Image(uiImage: paymentOption.image) .resizable() .aspectRatio(contentMode: .fit) .frame(height: 30) Text(paymentOption.label) Spacer() } .padding() } } } } } ``` ### (任意)支払いの詳細を更新する 顧客が決済情報を変更する操作 (割引コードの適用など) を実行したときに、`update` メソッドを呼び出して `EmbeddedPaymentElementViewModel` インスタンスを更新し、新しい値を反映します。Apple Pay や Google Pay など、一部の決済手段では UI に金額が表示されるため、常に正確で最新の状態を維持するようにしてください。 更新が完了すると、それにより顧客が現在選択している決済オプションが変更される場合もあります。 ```swift extension MyEmbeddedCheckoutView { func update() { Task { @MainActor in var updatedIntentConfig = oldIntentConfig updatedIntentConfig.mode = PaymentSheet.IntentConfiguration.Mode.payment(amount: 999, currency: "USD", setupFutureUsage: .offSession) let result = await embeddedViewModel.update(intentConfiguration: updatedIntentConfig) switch result { case .canceled: // Do nothing; this happens when a subsequent `update` call cancels this one break case .failed(let error): // Display error to user in an alert, let users retry case .succeeded: // Update your UI in case the payment option changed } } } } ``` ### 支払いを確定する 顧客が決済ボタンをタップしたら、`embeddedViewModel.confirm()` を呼び出して支払いを完了します。確定する際は、ユーザーの操作を必ず無効にしてください。 ```swift extension MyEmbeddedCheckoutView { func didTapConfirmButton() { Task { @MainActor in // Be sure to disable user interaction during confirm (not shown in this example) let result = await embeddedViewModel.confirm() switch result { case .completed: // Payment completed - show a confirmation screen. case .failed(let error): // Encountered an unrecoverable error. Re-enable user interaction, display the error to the user, log it, and so on. case .canceled: // Customer canceled - re-enable user interaction and let them try again. break } } } } ``` 次に、`PaymentSheet.IntentConfiguration` に渡した `confirmationTokenConfirmHandler` コールバックを実装し、サーバーにリクエストを送信します。サーバーは PaymentIntent を作成し、Client Secret を返します。このプロセスについては、[PaymentIntent の作成](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#submit-payment) を参照してください。 サーバー応答の Client Secret を返すか、エラーをスローします。EmbeddedPaymentElement は、クライアントシークレットを使用して PaymentIntent を確認するか、ローカライズされたエラーメッセージを UI に表示します ( [errorDescription](https://developer.apple.com/documentation/foundation/localizederror/2946895-errordescription) または [localizedDescription](https://developer.apple.com/documentation/foundation/nserror/1414418-localizeddescription)) 確認が完了すると、EmbeddedPaymentElement は使用できなくなります。代わりに、ユーザーを領収書画面などに誘導します。 ```swift extension MyEmbeddedCheckoutView { func handleConfirmationToken(_ confirmationToken: STPConfirmationToken) async throws -> String { // Make a request to your own server. Pass confirmationToken.stripeId if using server-side confirmation. // Return the client secret or throw an error return try await MyAPIClient.shared.createIntent(confirmationTokenId: confirmationToken.stripeId) } } ``` ## Optional: 選択した決済オプションを消去する EmbeddedPaymentElement 以外の決済オプションがある場合は、選択した決済オプションの消去が必要になることがあります。これを行うには、`clearPaymentOption` API を使用して、選択した決済オプションの選択を解除します。 #### UIKit ```swift extension MyCheckoutVC: UIViewController { func deselectPaymentMethod() { embeddedPaymentElement?.clearPaymentOption() } } ``` #### SwiftUI ```swift @available(iOS 15.0, *) extension MyEmbeddedCheckoutView { func deselectPaymentMethod() { embeddedViewModel.clearPaymentOption() } } ``` ## Optional: 同意書を自分で表示する デフォルトでは、Embedded Mobile Payment Element は、規制遵守を維持するための同意書と法的免責事項を表示します。このテキストは購入ボタンの近くに配置する必要があります。必要に応じて、ビューでの表示を無効にし、代わりに自分で表示します。 > 規制を遵守するため、実装で同意書のテキストを表示する必要があります。テキスト内の URL を UITextView などを使用して開けることを確認してください。 #### UIKit ```swift var configuration = EmbeddedPaymentElement.Configuration(...) configuration.embeddedViewDisplaysMandateText = true ... let mandateTextView = UITextView() mandateTextView.attributedText = embeddedPaymentElement.paymentOption.mandateText ``` #### SwiftUI ```swift var configuration = EmbeddedPaymentElement.Configuration(...) configuration.embeddedViewDisplaysMandateText = true ... if let attributedText = embeddedViewModel.paymentOption?.mandateText { Text(AttributedString(attributedText)) } ``` ## Optional: 顧客が画面ですぐに支払えるようにする ![Embedded Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/embedded-pay-immediate.057c691220d43158ac8000de10815ed9.png) 支払いをすぐに確定するようにフォーム画面のボタンを設定するには、`EmbeddedPaymentElement.Configuration` オブジェクトで `formSheetAction` を設定します。 画面が閉じられた後、支払い結果によって完了ブロックが実行されます。埋め込み UI は支払い完了後には使用できなくなるため、実装でユーザーを領収書画面など別の画面に誘導することをお勧めします。 ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.formSheetAction = .confirm(completion: { result in switch result { case .completed: // Payment completed. You can for example, show a confirmation screen. print("Completed") case .failed(let error): // Encountered an unrecoverable error. You can display the error to the user, log it, etc. print(error) case .canceled: // Customer canceled - you should probably do nothing. break } }) ``` ## PaymentIntent を作成する [サーバー側] サーバー側で、金額と通貨を指定して *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成します。支払い方法は[ダッシュボード](https://dashboard.stripe.com/settings/payment_methods)で管理できます。Stripe は取引額、通貨、決済フローなどの要素に基づいて、適切な支払い方法が返されるように処理します。悪意のある顧客が金額を恣意的に選択できないようにするために、請求額はクライアント側ではなく、常にサーバー側 (信頼性の高い環境) で指定してください。 コールが成功した場合は、PaymentIntent *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)) を返します。コールが失敗した場合は、[エラーを処理](https://docs.stripe.com/error-handling.md)して、エラーメッセージと顧客向けの簡単な説明を返します。 > すべての IntentConfiguration プロパティが PaymentIntent (`setup_future_usage`、`amount`、`currency` など) と一致していることを確認します。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { customer: ..., # The Customer ID you previously created amount: 1099, currency: 'usd', setup_future_usage: 'off_session', automatic_payment_methods: {enabled: true}, } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` ## 戻り先 URL を設定する [クライアント側] 顧客はお客様のアプリから離れて、(Safari やバンキングアプリなどで) 認証する場合があります。ユーザーが認証後にアプリに自動的に戻れるようにするには、[カスタム URL スキームを構成](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app)し、URL を SDK に転送するようにアプリのデリゲートを設定します。Stripe は[ユニバーサルリンク](https://developer.apple.com/documentation/xcode/allowing-apps-and-websites-to-link-to-your-content)には対応していません。 #### SceneDelegate #### Swift ```swift // This method handles opening custom URL schemes (for example, "your-app://stripe-redirect") func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { guard let url = URLContexts.first?.url else { return } let stripeHandled = StripeAPI.handleURLCallback(with: url) if (!stripeHandled) { // This was not a Stripe url – handle the URL normally as you would } } ``` #### AppDelegate #### Swift ```swift // This method handles opening custom URL schemes (for example, "your-app://stripe-redirect") func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { let stripeHandled = StripeAPI.handleURLCallback(with: url) if (stripeHandled) { return true } else { // This was not a Stripe url – handle the URL normally as you would } return false } ``` #### SwiftUI #### Swift ```swift @main struct MyApp: App { var body: some Scene { WindowGroup { Text("Hello, world!").onOpenURL { incomingURL in let stripeHandled = StripeAPI.handleURLCallback(with: incomingURL) if (!stripeHandled) { // This was not a Stripe url – handle the URL normally as you would } } } } } ``` さらに、[EmbeddedPaymentElement.Configuration](https://github.com/stripe/stripe-ios/blob/aa3234a7fafde98c9203b6ed77e0278c04310eb0/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L12) オブジェクトの [returnURL](https://github.com/stripe/stripe-ios/blob/aa3234a7fafde98c9203b6ed77e0278c04310eb0/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L70) をアプリの URL に設定します。 ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.returnURL = "your-app://stripe-redirect" ``` ## 支払い後のイベントを処理する [サーバー側] 支払いが完了すると、Stripe は [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) イベントを送信します。[ダッシュボードの Webhook ツール](https://dashboard.stripe.com/webhooks)を使用するか [Webhook のガイド](https://docs.stripe.com/webhooks/quickstart.md)に従ってこれらのイベントを受信し、顧客への注文確認メールの送信、データベースでの売上の記録、配送ワークフローの開始などのアクションを実行します。 クライアントからのコールバックを待つのではなく、これらのイベントをリッスンします。クライアントでは、コールバックが実行される前に顧客がブラウザーのウィンドウを閉じたり、アプリを終了する場合、また悪意を持つクライアントがレスポンスを不正操作する場合もあります。非同期型のイベントをリッスンするよう組み込みを設定すると、単一の組み込みで[複数の異なるタイプの支払い方法](https://stripe.com/payments/payment-methods-guide)を受け付けることができます。 Payment Element を使用して支払いを回収する場合は、`payment_intent.succeeded` イベントのほかにこれらのイベントを処理することをお勧めします。 | イベント | 説明 | アクション | | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.succeeded) | 顧客が正常に支払いを完了したときに送信されます。 | 顧客に注文の確定を送信し、顧客の注文の*フルフィルメント* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected)を実行します。 | | [payment_intent.processing](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.processing) | 顧客が正常に支払いを開始したが、支払いがまだ完了していない場合に送信されます。このイベントは、多くの場合、顧客が口座引き落としを開始するときに送信されます。その後、`payment_intent.succeeded` イベント、また、失敗の場合は `payment_intent.payment_failed` イベントが送信されます。 | 顧客に注文確認メールを送信し、支払いが保留中であることを示します。デジタル商品では、支払いの完了を待たずに注文のフルフィルメントを行うことが必要になる場合があります。 | | [payment_intent.payment_failed](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.payment_failed) | 顧客が支払いを試みたが、支払いに失敗する場合に送信されます。 | 支払いが `processing` から `payment_failed` に変わった場合は、顧客に再度支払いを試すように促します。 | ## 保存された支払い方法に後で請求する [サーバー側] > `bancontact` と `ideal` は、デフォルトでは 1 回限りの支払い方法です。以降も使用できるように設定すると、再利用可能な支払い方法タイプ `sepa_debit` が生成されます。このため、保存された支払い方法を照会するには `sepa_debit` を使用する必要があります。 > #### 法令遵守 > > 顧客の支払いの詳細を保存する際、お客様は適用されるすべての法律、規制、ネットワークの規則に準拠する責任があります。将来の購入に備えて顧客に過去の決済手段を提供する際は、必ず、特定の将来の使用に備えて決済手段の詳細を保存することについての同意を顧客から収集した決済手段をリストアップします。顧客に関連付けられた決済手段のうち、将来の購入用の保存済みの決済手段として顧客に提示できるものと提示できないものを区別するには、[allow_redisplay](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-allow_redisplay) パラメーターを使用します。 請求する決済手段を見つけるには、顧客に関連付けられている決済手段を一覧表示します。この例ではカードを一覧表示していますが、サポートされている任意の [type](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-type) を一覧表示できます。 > #### Accounts v2 API を使用した顧客の表現 > > Accounts v2 API では、Connect ユーザーには一般提供され、その他の Stripe ユーザーには公開プレビューで提供されます。Accounts v2 プレビューの一部である場合は、コードで[プレビューバージョン](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning)を指定する必要があります。 > > Accounts v2 プレビューへのアクセスをリクエストするには、 > > ほとんどのユースケースでは、[Customer](https://docs.stripe.com/api/customers.md) オブジェクトを使用するのではなく、[顧客を顧客設定済みの Account オブジェクトとしてモデル化する](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md)ことをお勧めします。 #### Accounts v2 ```curl curl -G https://api.stripe.com/v1/payment_methods \ -u "<>:" \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d type=card ``` #### Customers v1 ```curl curl -G https://api.stripe.com/v1/payment_methods \ -u "<>:" \ -d "customer={{CUSTOMER_ID}}" \ -d type=card ``` 顧客に *オフセッション* (A payment is described as off-session if it occurs without the direct involvement of the customer, using previously-collected payment information) で請求する準備ができたら、`Customer`または顧客設定の`Account`の ID と `PaymentMethod` ID を使用して、決済の金額と通貨を指定した `PaymentIntent` を作成します。オフセッション決済を行うには、他にもいくつかのパラメーターを設定します。 - 顧客が決済フローにおらず、カード発行会社や銀行などのパートナーからの認証リクエストに対応できないことを示すには、[off_session](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-off_session) を true に設定します。決済フロー中にパートナー (カード発行会社や銀行など) が認証をリクエストした場合、Stripe は以前の *オンセッション* (A payment is described as on-session if it occurs while the customer is actively in your checkout flow and able to authenticate the payment method) 取引の顧客情報を使用して免除をリクエストします。免除の条件が満たされない場合、`PaymentIntent` がエラーを返すことがあります。 - [confirm](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-confirm) を true に設定すると、`PaymentIntent` の作成時に即座に確定がトリガーされる。 - [payment_method](https://docs.stripe.com/api.md#create_payment_intent-payment_method) を `PaymentMethod` の ID に設定します。 - 導入で顧客をどのように表すかに応じて、[customer_account](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer_account) を顧客設定の `Account` の ID に設定するか、[customer](https://docs.stripe.com/api.md#create_payment_intent-customer) を `Customer` の ID に設定します。 #### Accounts v2 ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=usd \ -d "automatic_payment_methods[enabled]=true" \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d payment_method={{PAYMENT_METHOD_ID}} \ --data-urlencode "return_url=https://example.com/order/123/complete" \ -d off_session=true \ -d confirm=true ``` #### Customers v1 ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=usd \ -d "automatic_payment_methods[enabled]=true" \ -d "customer={{CUSTOMER_ID}}" \ -d payment_method={{PAYMENT_METHOD_ID}} \ --data-urlencode "return_url=https://example.com/order/123/complete" \ -d off_session=true \ -d confirm=true ``` ## 実装をテストする #### カード | カード番号 | シナリオ | テスト方法 | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | | 4242424242424242 | カード支払いは成功し、認証は必要とされません。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000002500003155 | カード支払いには*認証* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase)が必要です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000000000009995 | カードは、`insufficient_funds` などの拒否コードで拒否されます。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 6205500000000000004 | UnionPay カードは、13 ~ 19 桁の可変長です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | #### 銀行へのリダイレクト | 決済手段 | シナリオ | テスト方法 | | ---------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Bancontact、iDEAL | 顧客は、リダイレクトベースの即時通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | リダイレクトベースの任意の支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払い失敗)** をクリックします。 | | Pay by Bank | 顧客はリダイレクトベースの、[遅延通知型](https://docs.stripe.com/payments/payment-methods.md#payment-notification)の支払い方法で支払いに成功します。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Complete test payment (テスト支払い完了)** をクリックします。 | | Pay by Bank | 顧客は、リダイレクトベースの遅延通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払いを失敗させる)** をクリックします。 | | BLIK | BLIK による支払いはさまざまな理由で失敗します。即時の失敗 (コードの有効期限切れや無効など)、遅延型のエラー (銀行による拒否など)、またはタイムアウト (顧客が時間内に応答しなかったなど) などがあります。 | メールパターンを使用して[さまざまな失敗をシミュレーションします。](https://docs.stripe.com/payments/blik/accept-a-payment.md#simulate-failures) | #### 口座引き落とし | 決済手段 | シナリオ | テスト方法 | | -------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | SEPA ダイレクトデビット | 顧客が SEPA ダイレクトデビットによる支払いに成功しました。 | 口座番号 `AT321904300235473204` を使用して、フォームに入力します。確定された PaymentIntent のステータスはまず、processing に移行し、3 分後に succeeded ステータスに移行します。 | | SEPA ダイレクトデビット | 顧客の Payment Intent のステータスが、`processing` から `requires_payment_method` に移行します。 | 口座番号 `AT861904300235473202` を使用して、フォームに入力します。 | 実装内容をテストするためのその他の情報については、[テスト](https://docs.stripe.com/testing.md)をご覧ください。 ## Optional: 個人の決済手段にSetupFutureUsageを設定する (Preview) [サーバー側] [クライアント側] より細かく設定するには、[PaymentSheet.IntentConfiguration.Mode.PaymentMethodOptions](https://github.com/stripe/stripe-ios/blob/master/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/PaymentSheetIntentConfiguration.swift#L116) を使用して特定の決済手段に `setupFutureUsage` を設定します。 ```swift @_spi(PaymentMethodOptionsSetupFutureUsagePreview) import StripePaymentSheet class MyCheckoutVC: UIViewController { func createEmbeddedPaymentElement() async throws -> EmbeddedPaymentElement { let intentConfig = PaymentSheet.IntentConfiguration( mode: .payment( amount: 1099, currency: "USD",paymentMethodOptions: PaymentSheet.IntentConfiguration.Mode.PaymentMethodOptions( setupFutureUsageValues: [ .card: .offSession, .cashApp: .onSession ] ) ) ) { [weak self] confirmationToken in return try await self?.handleConfirmationToken(confirmationToken) } var configuration = EmbeddedPaymentElement.Configuration() configuration.returnURL = "your-app://stripe-redirect" // Use the return url you set up in the previous step let embeddedPaymentElement = try await EmbeddedPaymentElement.create(intentConfiguration: intentConfig, configuration: configuration) embeddedPaymentElement.presentingViewController = self embeddedPaymentElement.delegate = self return embeddedPaymentElement } } ``` 各決済手段でサポートされている [`setupFutureUsage` の値](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-payment_method_options)の詳細をご確認ください。 次に、サーバーが PaymentIntent に `setup_future_usage` または `payment_method_options[X][setup_future_usage]` を設定していないことを確認します。この設定は SDK が `IntentConfiguration` に基づいて自動的に処理します。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', confirmation_token: data['confirmation_token'], # In the latest version of the API, specifying the `automatic_payment_methods` parameter is optional because Stripe enables its functionality by default. automatic_payment_methods: {enabled: true}, } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` ## カードのスキャンを有効にする iOS をサポートするカードスキャン機能を有効にするには、アプリケーションの `Info.plist` の `NSCameraUsageDescription` (**プライバシー - カメラ利用の詳細**)を設定し、カメラにアクセスする理由を入力して下さい (例:「カードをスキャンするため」)。 ## Optional: 保存済みのカードを有効にする [サーバー側] [クライアント側] `EmbeddedPaymentElement` では、顧客がカードを保存でき、利用可能な決済手段に顧客が保存済みのカードを含めることもできます。顧客は、サーバー上で関連付けられた顧客設定済みの [Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) オブジェクトまたは [Customer](https://docs.stripe.com/api/customers/create.md) オブジェクトを持っている必要があります。顧客がカードを保存できるようにするチェックボックスを有効にするには、[CustomerSession](https://docs.stripe.com/api/customer_sessions.md) を作成し、`payment_method_save` を `enabled` に設定します。 #### Accounts v2 ```javascript const stripe = require('stripe')('sk_test_your_secret_key'); app.post('/mobile-payment-element', async (req, res) => { // Use an existing Account ID if this is a returning customer. const customer_account = await stripe.v2.core.accounts.create(); const customerSession = await stripe.customerSessions.create({ customer_account: customer_account.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer_account: customer_account.id, }); }); ``` 次に、顧客の ID と CustomerSession の client secret を使用して EmbeddedPaymentElement を設定します。 #### UIKit ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerAccountId, customerSessionClientSecret: customerSessionClientSecret) self.embeddedPaymentElement = try await EmbeddedPaymentElement.create(..., configuration: configuration) ``` #### SwiftUI ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerAccountId, customerSessionClientSecret: customerSessionClientSecret) try await embeddedViewModel.load(..., configuration: configuration) ``` #### Customers v1 ```javascript const stripe = require('stripe')('sk_test_your_secret_key'); app.post('/mobile-payment-element', async (req, res) => { // Use an existing Customer ID if this is a returning customer. const customer = await stripe.customers.create(); const customerSession = await stripe.customerSessions.create({ customer: customer.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer: customer.id, }); }); ``` 次に、顧客の ID と CustomerSession の client secret を使用して EmbeddedPaymentElement を設定します。 #### UIKit ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerId, customerSessionClientSecret: customerSessionClientSecret) self.embeddedPaymentElement = try await EmbeddedPaymentElement.create(..., configuration: configuration) ``` #### SwiftUI ```swift @_spi(CustomerSessionBetaAccess) import StripePaymentSheet var configuration = EmbeddedPaymentElement.Configuration() configuration.customer = .init(id: customerId, customerSessionClientSecret: customerSessionClientSecret) try await embeddedViewModel.load(..., configuration: configuration) ``` ## Optional: 遅延型の支払い方法を許可する [クライアント側] *遅延型の決済手段* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods)では、購入の終了時に顧客から売上を受け取ることが保証されません。これは、決済に時間がかかる (アメリカの銀行口座、SEPA デビット、iDEAL、Bancontact など) 場合や、完了に顧客の対応を必要とする (OXXO、コンビニ決済、Boleto など) 場合があるためです。 デフォルトでは、EmbeddedPaymentElement は遅延型の決済手段を表示しません。オプトインするには、`EmbeddedPaymentElement.Configuration` で `allowsDelayedPaymentMethods` を true に設定します。このステップのみでは、特定の決済手段を有効にすることはできませんが、アプリがその決済手段に対応できることが示されます。たとえば、現在 OXXO は EmbeddedPaymentElement でサポートされていませんが、サポートされるようになり、最新の SDK バージョンに更新すると、実装に関する追加の変更なしで、アプリに決済オプションとして OXXO を表示できます。 #### UIKit ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.allowsDelayedPaymentMethods = true self.embeddedPaymentElement = try await EmbeddedPaymentElement.create(..., configuration: configuration) ``` #### SwiftUI ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.allowsDelayedPaymentMethods = true try await embeddedViewModel.load(..., configuration: configuration) ``` 顧客が EmbeddedPaymentElement でこれらの遅延型の決済手段のいずれかを適切に使用すると、`.completed` の支払い結果が返されます。 ## Optional: Apple Pay を有効にする > 決済画面に専用の **Apple Pay** ボタンがある場合は、[Apple Pay ガイド](https://docs.stripe.com/apple-pay.md#present-payment-sheet)に従い、`ApplePayContext` を使用して **Apple Pay** ボタンから支払いを回収します。`EmbeddedPaymentElement` を使用して、他のタイプの決済手段に対応することも可能です。 ### Apple 加盟店 ID を登録する Apple Developer Web サイトで [新規 ID を登録](https://developer.apple.com/account/resources/identifiers/add/merchant) して、Apple 加盟店 ID を取得します。 フォームに説明と ID を入力します。説明はお客様の記録用であり、後で変更できます。アプリの名前を ID として使用することをお勧めします (`merchant.com.{{YOUR_APP_NAME}}` など)。 ### 新しい Apple Pay 証明書を作成する 支払いデータを暗号化するためのアプリの証明書を作成します。 ダッシュボードの [iOS certificate settings (iOS 証明書の設定)](https://dashboard.stripe.com/settings/ios_certificates) に移動して、**新規アプリケーションを追加**をクリックし、表示されるガイドに従います。 証明書署名リクエスト (CSR) ファイルをダウンロードして、Apple Pay の利用を可能にする安全な証明書を Apple から取得します。 1 つの CSR ファイルを使用して証明書を 1 つだけ発行する必要があります。Apple 加盟店 ID を切り替えた場合、ダッシュボードの [iOS Certificate Settings (iOS 証明書の設定)](https://dashboard.stripe.com/settings/ios_certificates) に移動して、新しい CSR と証明書を取得する必要があります。 ### Xcode を導入する Apple Pay ケイパビリティをアプリに追加します。Xcode でプロジェクト設定を開き、**Signing & Capabilities (署名およびケイパビリティ)** タブを選択して、**Apple Pay** ケイパビリティを追加します。この段階で開発者アカウントへのログインを要求される場合があります。前の手順で作成した加盟店 ID を選択すると、アプリで Apple Pay を受け付けられるようになります。 ![](https://b.stripecdn.com/docs-statics-srv/assets/xcode.a701d4c1922d19985e9c614a6f105bf1.png) Xcode で Apple Pay ケイパビリティを有効化する ### Apple Pay を追加する #### 1 回限りの支払い EmbeddedPaymentElement に Apple Pay を追加するには、Apple マーチャント ID とビジネスの[国コード](https://dashboard.stripe.com/settings/account)を使用して `EmbeddedPaymentElement.Configuration` を初期化してから、[applePay](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L28) を設定します。 #### iOS (Swift) ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.applePay = .init( merchantId: "merchant.com.your_app_name", merchantCountryCode: "US" ) ``` #### 継続支払い EmbeddedPaymentElement に Apple Pay を追加するには、Apple マーチャント ID とビジネスの[国コード](https://dashboard.stripe.com/settings/account)を使用して `EmbeddedPaymentElement.Configuration` を初期化してから、[applePay](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L28) を設定します。 継続支払いに関する [Apple のガイドライン](https://developer.apple.com/design/human-interface-guidelines/apple-pay#Supporting-subscriptions)に従い、`PKPaymentRequest` で追加の属性を設定する必要もあります。[ApplePayConfiguration.paymentRequestHandlers](https://stripe.dev/stripe-ios/stripepaymentsheet/documentation/stripepaymentsheet/paymentsheet/applepayconfiguration/handlers/paymentrequesthandler) にハンドラーを追加して、請求する予定の金額 (たとえば月額 9.95 USD) を指定して [PKPaymentRequest.paymentSummaryItems](https://developer.apple.com/documentation/passkit/pkpaymentrequest/1619231-paymentsummaryitems) を設定します。 `PKPaymentRequest` で `recurringPaymentRequest` または `automaticReloadPaymentRequest` プロパティを設定することで、[加盟店トークン](https://developer.apple.com/apple-pay/merchant-tokens/)を導入することもできます。 Apple Pay で継続支払いを使用する方法について、詳細は [Apple の PassKit に関するドキュメント](https://developer.apple.com/documentation/passkit/pkpaymentrequest)をご覧ください。 #### iOS (Swift) ```swift let customHandlers = EmbeddedPaymentElement.ApplePayConfiguration.Handlers( paymentRequestHandler: { request in // PKRecurringPaymentSummaryItem is available on iOS 15 or later if #available(iOS 15.0, *) { let billing = PKRecurringPaymentSummaryItem(label: "My Subscription", amount: NSDecimalNumber(string: "59.99")) // Payment starts today billing.startDate = Date() // Payment ends in one year billing.endDate = Date().addingTimeInterval(60 * 60 * 24 * 365) // Pay once a month. billing.intervalUnit = .month billing.intervalCount = 1 // recurringPaymentRequest is only available on iOS 16 or later if #available(iOS 16.0, *) { request.recurringPaymentRequest = PKRecurringPaymentRequest(paymentDescription: "Recurring", regularBilling: billing, managementURL: URL(string: "https://my-backend.example.com/customer-portal")!) request.recurringPaymentRequest?.billingAgreement = "You'll be billed $59.99 every month for the next 12 months. To cancel at any time, go to Account and click 'Cancel Membership.'" } request.paymentSummaryItems = [billing] request.currencyCode = "USD" } else { // On older iOS versions, set alternative summary items. request.paymentSummaryItems = [PKPaymentSummaryItem(label: "Monthly plan starting July 1, 2022", amount: NSDecimalNumber(string: "59.99"), type: .final)] } return request } ) var configuration = EmbeddedPaymentElement.Configuration() configuration.applePay = .init(merchantId: "merchant.com.your_app_name", merchantCountryCode: "US", customHandlers: customHandlers) ``` ### 注文の追跡 iOS 16 以降で[注文の追跡](https://developer.apple.com/design/human-interface-guidelines/technologies/wallet/designing-order-tracking)情報を追加するには、`PaymentSheet.ApplePayConfiguration.Handlers` で [authorizationResultHandler](https://stripe.dev/stripe-ios/stripepaymentsheet/documentation/stripepaymentsheet/paymentsheet/applepayconfiguration/handlers/authorizationresulthandler) を設定します。支払いの完了後、Stripe は iOS が Apple Pay の決済画面を閉じる前に実装を呼び出します。 `authorizationResultHandler` の実装で、完了した注文の注文の詳細をサーバーから取得します。提供された [PKPaymentAuthorizationResult](https://developer.apple.com/documentation/passkit/pkpaymentauthorizationresult) に詳細を追加し、変更された結果を返します。 注文の追跡の詳細については、[Apple のウォレットでの注文に関するドキュメント](https://developer.apple.com/documentation/walletorders)をご覧ください。 #### iOS (Swift) ```swift let customHandlers = PaymentSheet.ApplePayConfiguration.Handlers( authorizationResultHandler: { result in do { // Fetch the order details from your service let myOrderDetails = try await MyAPIClient.shared.fetchOrderDetails(orderID: orderID) result.orderDetails = PKPaymentOrderDetails( orderTypeIdentifier: myOrderDetails.orderTypeIdentifier, // "com.myapp.order" orderIdentifier: myOrderDetails.orderIdentifier, // "ABC123-AAAA-1111" webServiceURL: myOrderDetails.webServiceURL, // "https://my-backend.example.com/apple-order-tracking-backend" authenticationToken: myOrderDetails.authenticationToken) // "abc123" // Return your modified PKPaymentAuthorizationResult return result } catch { return PKPaymentAuthorizationResult(status: .failure, errors: [error]) } } ) var configuration = EmbeddedPaymentElement.Configuration() configuration.applePay = .init(merchantId: "merchant.com.your_app_name", merchantCountryCode: "US", customHandlers: customHandlers) ``` ## Optional: 構成要素をカスタマイズする すべてのカスタマイズは、[EmbeddedPaymentElement.Configuration](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L12) オブジェクトで設定されます。 ### デザイン [Appearance API](https://docs.stripe.com/elements/appearance-api/embedded-mobile.md?platform=ios) を使用して、アプリのデザインに合うように色やフォントなどをカスタマイズします。 ### ユーザーの住所を収集する [Address Element](https://docs.stripe.com/elements/address-element.md?platform=ios) を使用して、顧客から国内および国外の配送先住所や請求先住所を収集します。 ### 加盟店の表示名 [merchantDisplayName](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L66) を設定し、顧客に表示するビジネス名を指定します。デフォルトではアプリ名になります。 #### Swift ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.merchantDisplayName = "My app, Inc." ``` ### ダークモード `EmbeddedPaymentElement` は、ユーザーのシステム全体の表示設定 (ライトモードとダークモード) に合わせて自動的に調整されます。アプリがダークモードに対応していない場合は、[スタイル](https://github.com/stripe/stripe-ios/blob/8be959abc141e360080efa88980afb325737a935/StripePaymentSheet/StripePaymentSheet/Source/PaymentSheet/Embedded/EmbeddedPaymentElementConfiguration.swift#L51)を `alwaysLight` または `alwaysDark` モードに設定できます。 ```swift var configuration = EmbeddedPaymentElement.Configuration() configuration.style = .alwaysLight ``` ## Optional: 確定時のセキュリティコードの再収集を有効にする PaymentIntent の確定時に保存されたカードのセキュリティコードを再徴収するには、統合で PaymentIntent を作成する前に決済詳細を徴収する必要があります。 ### インテントの設定を更新する `PaymentSheet.IntentConfiguration` では、保存されたカードのセキュリティコードを再収集する時期を管理するオプションパラメーターを使用できます。 ```swift let intentConfig = PaymentSheet.IntentConfiguration( mode: .payment(amount: 1099, currency: "USD"), confirmHandler: { confirmationToken in // Handle ConfirmationToken...}, requireCVCRecollection: true) ``` ### インテント作成のパラメーターを更新する 支払いの確定時にセキュリティコードを再収集するには、PaymentIntent の作成時に `customerId` パラメーターと `require_cvc_recollection` パラメーターの両方を含めます。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. # Find your keys at https://dashboard.stripe.com/apikeys. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', automatic_payment_methods: {enabled: true},customer: customer.id, payment_method_options: { card: {require_cvc_recollection: true} } } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` # 決済を受け付ける > This is a 決済を受け付ける for when platform is android and type is payment. View the full page at https://docs.stripe.com/payments/mobile/accept-payment-embedded?platform=android&type=payment. Payment Element を使用すると、1 つの導入を使用して複数の決済手段を受け付けることができます。この導入では、Payment Element をレンダリングし、*PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成して、アプリで決済を確定するカスタム決済フローを構築します。 ## Stripe を設定する [サーバー側] [クライアント側] まず、Stripe アカウントが必要です。[今すぐ登録してください](https://dashboard.stripe.com/register)。 ### サーバー側 この組み込みは、Stripe API と通信するサーバー上にエンドポイントを必要とします。サーバーから Stripe API へのアクセスには、Stripe の公式ライブラリを使用します。 #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### クライアント側 [Stripe Android SDK](https://github.com/stripe/stripe-android) はオープンソースであり、[詳細なドキュメントが提供されています](https://stripe.dev/stripe-android/)。 SDK をインストールするには、[app/build.gradle](https://developer.android.com/studio/build/dependencies) ファイルの `dependencies` ブロックに `stripe-android` を追加します。 #### Kotlin ```kotlin plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.plugin.compose") } android { ... kotlinOptions { jvmTarget = "11" } buildFeatures { compose true } } dependencies { // ... // Stripe Android SDK implementation("com.stripe:stripe-android:23.8.0") // Include the financial connections SDK to support US bank account as a payment method implementation("com.stripe:financial-connections:23.8.0") } ``` > SDK の最新リリースおよび過去バージョンの詳細については、GitHub の [Releases](https://github.com/stripe/stripe-android/releases) ページをご覧ください。新しいリリースの公開時に通知を受け取るには、[リポジトリのリリースを確認](https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository)してください。 また、SDK が Stripe への API コールを実行できるように、[公開可能キー](https://dashboard.stripe.com/apikeys)を設定する必要もあります。すぐに開始するには、導入中にクライアント側でこれをハードコード化できますが、本番環境ではサーバーから公開可能キーを取得します。 ```kotlin // Set your publishable key: remember to change this to your live publishable key in production // See your keys here: https://dashboard.stripe.com/apikeys PaymentConfiguration.init(context, publishableKey = "<>") ``` ## 支払い方法を有効にする [支払い方法の設定](https://dashboard.stripe.com/settings/payment_methods)を表示して、サポートする支払い方法を有効にします。*PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成するには、少なくとも 1 つは支払い方法を有効にする必要があります。 多くの顧客から決済を受け付けられるよう、Stripe では、カードやその他一般的な決済手段がデフォルトで有効になっていますが、ビジネスや顧客に適した追加の決済手段を有効にすることをお勧めします。プロダクトと決済手段のサポートについては[決済手段のサポート](https://docs.stripe.com/payments/payment-methods/payment-method-support.md)を、手数料については[料金体系ページ](https://stripe.com/pricing/local-payment-methods)をご覧ください。 ## 支払い情報を収集する [クライアント側] Embedded Mobile Payment Element は、ネイティブモバイルアプリの決済ページで使用するために設計されています。この Element には決済手段の一覧が表示され、アプリのデザインに合わせてカスタマイズできます。 顧客が**カード**行をタップすると、決済手段の詳細を入力できる画面が開きます。画面にデフォルトで**続行**というボタンが表示され、タップすると入力画面が閉じます。これにより、顧客は決済フローで支払いを完了できます。 ![Embedded Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/android-embedded.1aefe2ca79ed073e350ec629a5af22d5.png) また、続行するのではなく、すぐに支払いを完了するようにボタンを設定することもできます。これを行うには、ガイドに沿って手順を実行した後で、[このステップ](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#embedded-let-customer-pay-immediately)を完了します。 ### Embedded Payment Element を初期化する #### Jetpack Compose `EmbeddedPaymentElement.Builder` で `rememberEmbeddedPaymentElement` 関数を使用して、`EmbeddedPaymentElement` のインスタンスを初期化します。 `CreateIntentWithConfirmationTokenCallback` を渡します。今のところ、実装は空のままにしておきます。 ```kotlin import com.stripe.android.paymentelement.EmbeddedPaymentElement import com.stripe.android.paymentelement.rememberEmbeddedPaymentElement @Composable fun CheckoutScreen() { val embeddedBuilder = remember { EmbeddedPaymentElement.Builder( createIntentCallback = { confirmationToken, -> TODO("You'll implement this in the "Confirm the payment" step") }, resultCallback = { result -> TODO("You'll implement this in the "Confirm the payment" step") }, ) } val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) } ``` #### ビュー (クラシック) `EmbeddedPaymentElement.Builder` で `rememberEmbeddedPaymentElement` 関数を使用して、`EmbeddedPaymentElement` のインスタンスを初期化します。 ```kotlin import com.stripe.android.paymentelement.EmbeddedPaymentElement import com.stripe.android.paymentelement.rememberEmbeddedPaymentElement @Composable fun CheckoutScreen() { val embeddedBuilder = remember { EmbeddedPaymentElement.Builder( createIntentCallback = { confirmationToken -> TODO("Completed in a later step.") }, resultCallback = { result -> TODO("Completed in a later step.") }, ) } val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) } ``` Embedded Mobile Payment Element は Jetpack Compose UI 用にビルドされ、`@Composable Content` メソッドを公開しています。クラシックビューを使用する場合は、`ComposeView` で組込み型決済要素をラップする必要があります。 ```xml ``` ```kotlin class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.checkout_screen_layout) val composeView = findViewById(R.id.compose_view) composeView.setContent { CheckoutScreen() } } } ``` ### Embedded Payment Element を設定する `Builder` オブジェクトには、`CreateIntentCallback` など、`EmbeddedPaymentElement` のインスタンス化に必要なコールバックが含まれています。今の時点では、実装は空のままにしておきます。 インスタンス化後、`EmbeddedPaymentElement.Configuration` および `PaymentSheet.IntentConfiguration` を使用して `configure` を呼び出します。`Configuration` オブジェクトには、`EmbeddedPaymentElement` の一般的な構成オプションが含まれています。`IntentConfiguration` オブジェクトには、金額や通貨など、特定の決済に関する詳細が含まれます。 ```kotlin import com.stripe.android.paymentsheet.PaymentSheet @Composable fun CheckoutScreen() { val embeddedBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) LaunchedEffect(embeddedPaymentElement) { embeddedPaymentElement.configure( intentConfiguration = PaymentSheet.IntentConfiguration(mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 1099, currency = "USD",),// Optional intent configuration options... ), configuration = EmbeddedPaymentElement.Configuration.Builder("Powdur").build() ) } } ``` ### Embedded Payment Element ビューを追加する `EmbeddedPaymentElement` が正常に初期化されたら、その `@Composable Content` を決済 UI に配置します。 > コンテンツは、最初に表示された後に高さが変更される可能性があるため、スクロール可能なコンテナー内に配置する必要があります。 ```kotlin @Composable fun CheckoutScreen() { val embeddedBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) ..... val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxSize() .verticalScroll(scrollState) .padding(16.dp) ) { embeddedPaymentElement.Content() } } ``` この時点で、アプリを実行して、Embedded Mobile Payment Element を確認できます。 ### (任意)支払いの詳細を更新する 顧客が決済の詳細を変更した場合 (例えば、割引コードを適用するなど)、`EmbeddedPaymentElement` インスタンスを更新します。`configure` メソッドを再度呼び出すと、新しい値が同期され、UI に表示されます。 > Google Pay のような一部の決済手段では、UI に金額が表示されます。顧客が決済を変更し、`EmbeddedPaymentElement` を更新しない場合、UI は正しくない値を表示します。 `configure` コールが完了すると、`@Composable Content` と `paymentOption` は、`configure` コールで指定された新しい値で自動的に更新されます。 ```kotlin val intentConfiguration = PaymentSheet.IntentConfiguration( // Update the amount to reflect the price after applying the discount code mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 999, currency = "USD", ), ) val configuration = EmbeddedPaymentElement.Configuration.Builder("Powdur") .build() LaunchedEffect(embeddedPaymentElement) { embeddedPaymentElement.configure( intentConfiguration = intentConfiguration, configuration = configuration, ) } ``` ### (任意)選択した支払いオプションを表示する Embedded Payment Element の `paymentOption` の observable `Flow` プロパティにアクセスすることで、決済オプションの詳細 (下 4 桁、カードロゴ、請求情報など) を表示できます。顧客がフォームシートを開く決済手段を選択すると、シートで **Continue** をタップした後に決済オプションが更新されます。 ```kotlin val selectedPaymentOption by embeddedPaymentElement.paymentOption.collectAsState() selectedPaymentOption?.let { paymentOption -> Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier .clickable( onClick = { }, ) .semantics { text = AnnotatedString(paymentOption.label) }, ) { Icon( painter = paymentOption.iconPainter, contentDescription = null, // decorative element modifier = Modifier.padding(horizontal = 4.dp), tint = Color.Unspecified, ) Text(text = paymentOption.label) } } ``` ### 支払いを確定する 顧客が決済ボタンをタップしたら、`embeddedPaymentElement.confirm()` を呼び出して支払いを開始します。確定する際は、ユーザーの操作を必ず無効にしてください。 フォームが表示されると、`EmbeddedPaymentElement` は、ユーザーが **Call to action** をクリックすると、`confirm` を呼び出します。選択された決済手段にフォームフィールドがない場合、`@Composable Content` の下にある **Call to action** をユーザーがクリックすると、`confirm` を呼び出します。 ```kotlin Button( onClick = { embeddedPaymentElement.confirm() } ) { Text("Confirm payment") } ``` 次に、すでに `EmbeddedPaymentElement.Builder` に渡している `createIntentCallback` コールバックを実装して、サーバーにリクエストを送信します。サーバーは `PaymentIntent` を作成し、その client secret を返します。詳細については、[PaymentIntent を作成する](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#submit-payment)をご覧ください。 リクエストを受信したら、`CreateIntentResult` を使用して、サーバーレスポンスのクライアントシークレットまたはエラーとともに、Intent の作成結果を返します。`EmbeddedPaymentElement` は、クライアントシークレットを使用して PaymentIntent を確認するか、UI にローカライズされたエラーメッセージを表示します。 ```kotlin import com.stripe.android.paymentsheet.CreateIntentResult val embeddedBuilder = remember { val embeddedBuilder = EmbeddedPaymentElement.Builder( createIntentCallback = { confirmationToken ->// Make a request to your own server and receive a client secret or an error. val networkResult = ... if (networkResult.isSuccess) { CreateIntentResult.Success(networkResult.clientSecret) } else { CreateIntentResult.Failure(networkResult.exception) } }, resultCallback = { result ->when (result) { is EmbeddedPaymentElement.Result.Completed -> { // Payment completed - show a confirmation screen. } is EmbeddedPaymentElement.Result.Failed -> { // Encountered an unrecoverable error. You can display the error to the user, log it, and so on } is EmbeddedPaymentElement.Result.Canceled -> { // Customer canceled - you should probably do nothing. } } }, ) } ``` ## Optional: 選択した決済オプションを消去する `EmbeddedPaymentElement` 以外の決済オプションがある場合は、選択した決済オプションの消去が必要になることがあります。これを行うには、`clearPaymentOption` API を使用して、選択した決済オプションの選択を解除します。 ```kotlin @Composable fun CheckoutScreen() { val embeddedPaymentElementBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedPaymentElementBuilder) .... Button( onClick = embeddedPaymentElement::clearPaymentOption ) { Text("Select external payment option") } } ``` ## Optional: 同意書を自分で表示する 法規制遵守を確保するため、Embedded Mobile Payment Element はデフォルトで同意書と法的免責事項を表示します。このテキストは**購入**ボタンの近くに配置する必要があります。ビューでこのテキストのデフォルト表示を無効にする場合は、自分で表示する必要があります。 > 規制を遵守するため、実装で同意書のテキストを正確に表示する必要があります。自分で表示する場合は、`Text` コンポーザブルにテキストを含めて、URL が正しく表示されることを確認してください。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder("Merchant, Inc") .embeddedViewDisplaysMandateText(false) .build() .... val selectedPaymentOption by embeddedPaymentElement.paymentOption.collectAsState() selectedPaymentOption?.mandateText?.let { mandateText -> Text(mandateText) } ``` ## Optional: 顧客が画面ですぐに支払えるようにする ![Embedded Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/embedded-pay-immediate.057c691220d43158ac8000de10815ed9.png) 支払いをすぐに確定するようにフォーム画面のボタンを設定するには、`EmbeddedPaymentElement.Configuration` オブジェクトで `formSheetAction` を設定します。 画面が閉じられた後、支払い結果によって完了ブロックが実行されます。埋め込み UI は支払い完了後には使用できなくなるため、実装でユーザーを領収書画面など別の画面に誘導することをお勧めします。 ```kotlin @Composable fun CheckoutScreen() { val embeddedBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) LaunchedEffect(embeddedPaymentElement) { embeddedPaymentElement.configure( intentConfiguration = ...., configuration = EmbeddedPaymentElement.Configuration.Builder("Powdur") .formSheetAction(EmbeddedPaymentElement.FormSheetAction.Confirm) .build() ) } } ``` ## PaymentIntent を作成する [サーバー側] サーバー側で、金額と通貨を指定して *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成します。支払い方法は[ダッシュボード](https://dashboard.stripe.com/settings/payment_methods)で管理できます。Stripe は取引額、通貨、決済フローなどの要素に基づいて、適切な支払い方法が返されるように処理します。悪意のある顧客が金額を恣意的に選択できないようにするために、請求額はクライアント側ではなく、常にサーバー側 (信頼性の高い環境) で指定してください。 コールが成功した場合は、PaymentIntent *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)) を返します。コールが失敗した場合は、[エラーを処理](https://docs.stripe.com/error-handling.md)して、エラーメッセージと顧客向けの簡単な説明を返します。 > すべての IntentConfiguration プロパティが PaymentIntent (`setup_future_usage`、`amount`、`currency` など) と一致していることを確認します。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', automatic_payment_methods: {enabled: true}, } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` ## 支払い後のイベントを処理する [サーバー側] 支払いが完了すると、Stripe は [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) イベントを送信します。[ダッシュボードの Webhook ツール](https://dashboard.stripe.com/webhooks)を使用するか [Webhook のガイド](https://docs.stripe.com/webhooks/quickstart.md)に従ってこれらのイベントを受信し、顧客への注文確認メールの送信、データベースでの売上の記録、配送ワークフローの開始などのアクションを実行します。 クライアントからのコールバックを待つのではなく、これらのイベントをリッスンします。クライアントでは、コールバックが実行される前に顧客がブラウザーのウィンドウを閉じたり、アプリを終了する場合、また悪意を持つクライアントがレスポンスを不正操作する場合もあります。非同期型のイベントをリッスンするよう組み込みを設定すると、単一の組み込みで[複数の異なるタイプの支払い方法](https://stripe.com/payments/payment-methods-guide)を受け付けることができます。 Payment Element を使用して支払いを回収する場合は、`payment_intent.succeeded` イベントのほかにこれらのイベントを処理することをお勧めします。 | イベント | 説明 | アクション | | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.succeeded) | 顧客が正常に支払いを完了したときに送信されます。 | 顧客に注文の確定を送信し、顧客の注文の*フルフィルメント* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected)を実行します。 | | [payment_intent.processing](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.processing) | 顧客が正常に支払いを開始したが、支払いがまだ完了していない場合に送信されます。このイベントは、多くの場合、顧客が口座引き落としを開始するときに送信されます。その後、`payment_intent.succeeded` イベント、また、失敗の場合は `payment_intent.payment_failed` イベントが送信されます。 | 顧客に注文確認メールを送信し、支払いが保留中であることを示します。デジタル商品では、支払いの完了を待たずに注文のフルフィルメントを行うことが必要になる場合があります。 | | [payment_intent.payment_failed](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.payment_failed) | 顧客が支払いを試みたが、支払いに失敗する場合に送信されます。 | 支払いが `processing` から `payment_failed` に変わった場合は、顧客に再度支払いを試すように促します。 | ## 実装をテストする #### カード | カード番号 | シナリオ | テスト方法 | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | | 4242424242424242 | カード支払いは成功し、認証は必要とされません。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000002500003155 | カード支払いには*認証* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase)が必要です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000000000009995 | カードは、`insufficient_funds` などの拒否コードで拒否されます。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 6205500000000000004 | UnionPay カードは、13 ~ 19 桁の可変長です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | #### 銀行へのリダイレクト | 決済手段 | シナリオ | テスト方法 | | ---------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Bancontact、iDEAL | 顧客は、リダイレクトベースの即時通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | リダイレクトベースの任意の支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払い失敗)** をクリックします。 | | Pay by Bank | 顧客はリダイレクトベースの、[遅延通知型](https://docs.stripe.com/payments/payment-methods.md#payment-notification)の支払い方法で支払いに成功します。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Complete test payment (テスト支払い完了)** をクリックします。 | | Pay by Bank | 顧客は、リダイレクトベースの遅延通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払いを失敗させる)** をクリックします。 | | BLIK | BLIK による支払いはさまざまな理由で失敗します。即時の失敗 (コードの有効期限切れや無効など)、遅延型のエラー (銀行による拒否など)、またはタイムアウト (顧客が時間内に応答しなかったなど) などがあります。 | メールパターンを使用して[さまざまな失敗をシミュレーションします。](https://docs.stripe.com/payments/blik/accept-a-payment.md#simulate-failures) | #### 口座引き落とし | 決済手段 | シナリオ | テスト方法 | | -------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | SEPA ダイレクトデビット | 顧客が SEPA ダイレクトデビットによる支払いに成功しました。 | 口座番号 `AT321904300235473204` を使用して、フォームに入力します。確定された PaymentIntent のステータスはまず、processing に移行し、3 分後に succeeded ステータスに移行します。 | | SEPA ダイレクトデビット | 顧客の Payment Intent のステータスが、`processing` から `requires_payment_method` に移行します。 | 口座番号 `AT861904300235473202` を使用して、フォームに入力します。 | 実装内容をテストするためのその他の情報については、[テスト](https://docs.stripe.com/testing.md)をご覧ください。 ## Optional: 保存済みのカードを有効にする [サーバー側] [クライアント側] `EmbeddedPaymentElement` では、顧客がカードを保存でき、利用可能な決済手段に顧客が保存済みのカードを含めることもできます。顧客は、サーバー上で関連付けられた顧客設定済みの [Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) オブジェクトまたは [Customer](https://docs.stripe.com/api/customers/create.md) オブジェクトを持っている必要があります。顧客がカードを保存できるようにするチェックボックスを有効にするには、[CustomerSession](https://docs.stripe.com/api/customer_sessions.md) を作成し、`payment_method_save` を `enabled` に設定します。 #### Accounts v2 ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. const stripe = require("stripe")("<>"); const express = require('express'); const app = express(); app.set('trust proxy', true); app.use(express.json()); app.post('/payment-sheet', async (req, res) => { // Use an existing Account ID if this is a returning customer. const customer_account = await stripe.v2.core.accounts.create(); const customerSession = await stripe.customerSessions.create({ customer_account: customer_account.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer_account: customer_account.id, }); }); ``` 次に、`EmbeddedPaymentElement` を、顧客の ID と `CustomerSession` の client secret を使用して設定または表示します。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Powdur") .customer( PaymentSheet.CustomerConfiguration.createWithCustomerSession( id = customerAccountId, clientSecret = customerSessionClientSecret, ) ) .build() embeddedPaymentElement.configure( intentConfiguration = // ... , configuration = configuration, ) ``` #### Customers v1 ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. const stripe = require("stripe")("<>"); const express = require('express'); const app = express(); app.set('trust proxy', true); app.use(express.json()); app.post('/payment-sheet', async (req, res) => { // Use an existing Customer ID if this is a returning customer. const customer = await stripe.customers.create(); const customerSession = await stripe.customerSessions.create({ customer: customer.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer: customer.id, }); }); ``` 次に、`EmbeddedPaymentElement` を、顧客の ID と `CustomerSession` の client secret を使用して設定または表示します。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Powdur") .customer( PaymentSheet.CustomerConfiguration.createWithCustomerSession( id = customerId, clientSecret = customerSessionClientSecret, ) ) .build() embeddedPaymentElement.configure( intentConfiguration = // ... , configuration = configuration, ) ``` ## Optional: 遅延型の支払い方法を許可する [クライアント側] *遅延型の決済手段* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods)では、購入の終了時に顧客から売上を受け取ることが保証されません。これは、決済に時間がかかる (アメリカの銀行口座、SEPA デビット、iDEAL、Bancontact、Sofort など) か、完了に顧客の対応を必要とする (OXXO、コンビニ決済、Boleto など) という理由によります。 デフォルトでは、`EmbeddedPaymentElement` は、遅延型の決済手段を受け入れている場合でも表示しません。有効にした遅延型の決済手段を表示するには、`EmbeddedPaymentElement.Configuration` で `allowsDelayedPaymentMethods` を true に設定します。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Powdur") .allowsDelayedPaymentMethods(true) .build() ``` 顧客が `EmbeddedPaymentElement` で遅延型の決済手段を適切に使用すると、`EmbeddedPaymentElement.Result.Completed` の支払い結果が返されます。 ## Optional: Google Pay を有効にする > 決済画面に専用の **Google Pay** ボタンがある場合は、[Google Pay ガイド](https://docs.stripe.com/google-pay.md?platform=android)の内容に従ってください。Embedded Payment Element を使用して、他のタイプの決済手段を処理することも可能です。 ### 実装方法を設定する Google Pay を使用するには、まず以下を **AndroidManifest.xml** の `` に追加し、Google Pay API を有効化します。 ```xml ... ``` 詳細は、Google Pay の Android 向け [Google Pay API を設定する](https://developers.google.com/pay/api/android/guides/setup) を参照してください。 ### Google Pay を追加する 構築済みのシステムに Google Pay を追加するには、`EmbeddedPaymentElement.Configuration` を初期化する際に、ご利用の Google Pay 環境 (本番またはテスト) と[ビジネスの国コード](https://dashboard.stripe.com/settings/account)を指定して、[PaymentSheet.GooglePayConfiguration](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-google-pay-configuration/index.html) を渡します。 ```kotlin val googlePayConfiguration = PaymentSheet.GooglePayConfiguration( environment = PaymentSheet.GooglePayConfiguration.Environment.Test, countryCode = "US", currencyCode = "USD" // Required for Setup Intents, optional for Payment Intents ) val configuration = EmbeddedPaymentElement.Configuration.Builder("Merchant, Inc.") .googlePay(googlePayConfiguration) .build() ``` ### Google Pay をテストする Google では、[テストクレジットカードスイート](https://developers.google.com/pay/api/android/guides/resources/test-card-suite) を使用してテスト決済を行うことができます。Stripe [テストカード](https://docs.stripe.com/testing.md) をテストスイートと併用できます。 Google Pay がサポートされている国では、シミュレーションされたデバイスではなく、物理的な Android デバイスを使用して Google Pay をテストする必要があります。Google ウォレットに保存した実際のクレジットカードを使用して、テスト用デバイスの Google アカウントにログインします。 ## Optional: カードのスキャンを有効にする カードスキャンサポートを有効にするには、[Google Pay & Wallet Console](https://pay.google.com/business/console?utm_source=devsite&utm_medium=devsite&utm_campaign=devsite) から Google Pay API への[本番アクセスをリクエスト](https://developers.google.com/pay/api/android/guides/test-and-deploy/request-prod-access)します。 - Google Pay を有効にしている場合、対象のデバイスの UI でカードスキャン機能が自動的に利用可能になります。対象のデバイスの詳細については、[Google Pay API の制約](https://developers.google.com/pay/payment-card-recognition/debit-credit-card-recognition)をご覧ください。 - **重要:** カードスキャン機能は、[Google Pay & Wallet Console](https://pay.google.com/business/console) に登録された同じ署名キーで署名されたビルドにのみ表示されます。異なる署名キーを使用したテストまたはデバッグビルド (Firebase App Tester を通じて配布されたビルドなど) では、 **カードをスキャン** オプションは表示されません。プレリリースビルドでカードスキャンをテストするには、以下のいずれかを行う必要があります。 - 本番環境の署名キーを使用してテストビルドに署名する - Google Pay & Wallet Console にテスト署名キーのフィンガープリントを追加する お客様のアプリが Google Pay に対応していない場合は、Stripe カードスキャナーを使用できます。 > Stripe カードスキャナーは公開プレビュー版です。 カードスキャンに対応する機能を有効にするには、`stripecardscan` を `dependencies` ブロック (お客様の [app/build.gradle](https://developer.android.com/studio/build/dependencies) ファイル内) に追加します: #### Groovy ```groovy implementation 'com.stripe:stripecardscan:23.8.0' ``` ## Optional: 画面をカスタマイズする カスタマイズはすべて `EmbeddedPaymentElement.Configuration` オブジェクトで設定されます。 ### デザイン [Appearance API](https://docs.stripe.com/elements/appearance-api/embedded-mobile.md?platform=android) を使用して、アプリのデザインに合うように色やフォントなどをカスタマイズします。 ### 顧客の住所を収集 [Address Element](https://docs.stripe.com/elements/address-element.md?platform=android) を使用して、顧客から配送先住所と請求先住所を収集します。 ### 表示するビジネス名をカスタマイズする `merchantDisplayName` を設定し、顧客に表示するビジネス名を指定します。名前を指定しない場合、既定値はアプリ名になります。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .build() ``` ### デフォルトの請求先情報を指定する `EmbeddedPaymentElement` で収集される請求先情報のデフォルト値を設定するには、`defaultBillingDetails` プロパティを設定します。`EmbeddedPaymentElement` の各フィールドに、指定した値が事前に読み込まれます。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .defaultBillingDetails( PaymentSheet.BillingDetails( address = PaymentSheet.Address( country = "US", ), email = "foo@bar.com" ) ) .build() ``` ### 請求先情報の収集を設定する `BillingDetailsCollectionConfiguration` を使用して、`EmbeddedPaymentElement` で請求詳細を収集する方法を指定します。 顧客の名前、メールアドレス、電話番号、住所を収集できます。 UI でデフォルトの請求詳細が収集されない場合でも、それらの詳細を PaymentMethod オブジェクトに関連付けるには、`billingDetailsCollectionConfiguration.attachDefaultsToPaymentMethod` を `true` に設定します。 ```kotlin import com.stripe.android.paymentsheet.PaymentSheet.BillingDetailsCollectionConfiguration val billingDetails = PaymentSheet.BillingDetails( email = "foo@bar.com" ) val billingDetailsCollectionConfiguration = BillingDetailsCollectionConfiguration( attachDefaultsToPaymentMethod = true, name = BillingDetailsCollectionConfiguration.CollectionMode.Always, email = BillingDetailsCollectionConfiguration.CollectionMode.Never, address = BillingDetailsCollectionConfiguration.AddressCollectionMode.Full, ) val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .defaultBillingDetails(billingDetails) .billingDetailsCollectionConfiguration(billingDetailsCollectionConfiguration) .build() ``` > 情報の収集に適用される法律については、弁護士に相談してください。電話番号は、取引に必要な場合にのみ収集してください。 ## Optional: 確定時のセキュリティコードの再収集を有効にする PaymentIntent の確定時に保存されたカードのセキュリティコードを再徴収するには、統合で PaymentIntent を作成する前に決済詳細を徴収する必要があります。 ### インテントの設定を更新する `PaymentSheet.IntentConfiguration` では、保存されたカードのセキュリティコードを再収集する時期を管理するオプションパラメーターを使用できます。 ```kotlin val intentConfig = PaymentSheet.IntentConfiguration( mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 1099, currency = "usd", ),requireCvcRecollection = true, ) ``` ### インテント作成のパラメーターを更新する 支払いの確定時にセキュリティコードを再収集するには、PaymentIntent の作成時に `customerId` パラメーターと `require_cvc_recollection` パラメーターの両方を含めます。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. # Find your keys at https://dashboard.stripe.com/apikeys. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', automatic_payment_methods: {enabled: true},customer: customer.id, payment_method_options: { card: {require_cvc_recollection: true} }, } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` # 決済手段を収集して保存する > This is a 決済手段を収集して保存する for when platform is android and type is setup. View the full page at https://docs.stripe.com/payments/mobile/accept-payment-embedded?platform=android&type=setup. SetupIntent フローを使用すると、決済を作成せずに決済手段の詳細を収集し、将来の決済に備えて保存できます。この導入では、Payment Element をレンダリングし、*SetupIntent* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) を作成し、アプリで決済手段の保存を確定するカスタムフローを構築します。 ## Stripe を設定する [サーバー側] [クライアント側] まず、Stripe アカウントが必要です。[今すぐ登録してください](https://dashboard.stripe.com/register)。 ### サーバー側 この組み込みは、Stripe API と通信するサーバー上にエンドポイントを必要とします。サーバーから Stripe API へのアクセスには、Stripe の公式ライブラリを使用します。 #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### クライアント側 [Stripe Android SDK](https://github.com/stripe/stripe-android) はオープンソースであり、[詳細なドキュメントが提供されています](https://stripe.dev/stripe-android/)。 SDK をインストールするには、[app/build.gradle](https://developer.android.com/studio/build/dependencies) ファイルの `dependencies` ブロックに `stripe-android` を追加します。 #### Kotlin ```kotlin plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.plugin.compose") } android { ... kotlinOptions { jvmTarget = "11" } buildFeatures { compose true } } dependencies { // ... // Stripe Android SDK implementation("com.stripe:stripe-android:23.8.0") // Include the financial connections SDK to support US bank account as a payment method implementation("com.stripe:financial-connections:23.8.0") } ``` > SDK の最新リリースおよび過去バージョンの詳細については、GitHub の [Releases](https://github.com/stripe/stripe-android/releases) ページをご覧ください。新しいリリースの公開時に通知を受け取るには、[リポジトリのリリースを確認](https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository)してください。 また、SDK が Stripe への API コールを実行できるように、[公開可能キー](https://dashboard.stripe.com/apikeys)を設定する必要もあります。すぐに開始するには、導入中にクライアント側でこれをハードコード化できますが、本番環境ではサーバーから公開可能キーを取得します。 ```kotlin // Set your publishable key: remember to change this to your live publishable key in production // See your keys here: https://dashboard.stripe.com/apikeys PaymentConfiguration.init(context, publishableKey = "<>") ``` ## 支払い方法を有効にする [支払い方法の設定](https://dashboard.stripe.com/settings/payment_methods)を表示して、サポートする支払い方法を有効にします。*SetupIntent* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) を作成するには、少なくとも 1 つは支払い方法を有効にする必要があります。 多くの顧客から決済を受け付けられるよう、Stripe では、カードやその他一般的な決済手段がデフォルトで有効になっていますが、ビジネスや顧客に適した追加の決済手段を有効にすることをお勧めします。プロダクトと決済手段のサポートについては[決済手段のサポート](https://docs.stripe.com/payments/payment-methods/payment-method-support.md)を、手数料については[料金体系ページ](https://stripe.com/pricing/local-payment-methods)をご覧ください。 ## Customer を作成する [サーバー側] 将来の決済に備えて決済手段を設定するには、顧客を表すオブジェクトに関連付ける必要があります。顧客がアカウントを作成したとき、または初めて取引したときに、Accounts v2 API を使用して顧客設定の [Account](https://docs.stripe.com/api/v2/core/accounts/create.md) オブジェクト、または Customers API を使用して [Customer](https://docs.stripe.com/api/customers/create.md) オブジェクトを作成します。 > #### Accounts v2 API を使用した顧客の表現 > > Accounts v2 API では、Connect ユーザーには一般提供され、その他の Stripe ユーザーには公開プレビューで提供されます。Accounts v2 プレビューの一部である場合は、コードで[プレビューバージョン](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning)を指定する必要があります。 > > Accounts v2 プレビューへのアクセスをリクエストするには、 > > ほとんどのユースケースでは、[Customer](https://docs.stripe.com/api/customers.md) オブジェクトを使用するのではなく、[顧客を顧客設定済みの Account オブジェクトとしてモデル化する](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md)ことをお勧めします。 ```curl curl -X POST https://api.stripe.com/v1/customers \ -u "<>:" ``` ## 支払い情報を収集する [クライアント側] Embedded Mobile Payment Element は、ネイティブモバイルアプリの決済ページで使用するために設計されています。この Element には決済手段の一覧が表示され、アプリのデザインに合わせてカスタマイズできます。 顧客が**カード**行をタップすると、決済手段の詳細を入力できる画面が開きます。画面にデフォルトで**続行**というボタンが表示され、タップすると画面が閉じます。これにより、顧客は決済フローで設定を完了できます。 ![Embedded Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/android-embedded.1aefe2ca79ed073e350ec629a5af22d5.png) また、続行するのではなく、すぐに設定を完了するようにボタンを設定することもできます。これを行うには、ガイドに沿って手順を実行した後で、[このステップ](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#embedded-let-customer-pay-immediately)を完了します。 ### Embedded Payment Element を初期化する #### Jetpack Compose `EmbeddedPaymentElement.Builder` で `rememberEmbeddedPaymentElement` 関数を使用して、`EmbeddedPaymentElement` のインスタンスを初期化します。 `CreateIntentWithConfirmationTokenCallback` を渡します。今のところ、実装は空のままにしておきます。 ```kotlin import com.stripe.android.paymentelement.EmbeddedPaymentElement import com.stripe.android.paymentelement.rememberEmbeddedPaymentElement @Composable fun CheckoutScreen() { val embeddedBuilder = remember { EmbeddedPaymentElement.Builder( createIntentCallback = { confirmationToken, -> TODO("You'll implement this in the "Confirm the payment" step") }, resultCallback = { result -> TODO("You'll implement this in the "Confirm the payment" step") }, ) } val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) } ``` #### ビュー (クラシック) `EmbeddedPaymentElement.Builder` で `rememberEmbeddedPaymentElement` 関数を使用して、`EmbeddedPaymentElement` のインスタンスを初期化します。 ```kotlin import com.stripe.android.paymentelement.EmbeddedPaymentElement import com.stripe.android.paymentelement.rememberEmbeddedPaymentElement @Composable fun CheckoutScreen() { val embeddedBuilder = remember { EmbeddedPaymentElement.Builder( createIntentCallback = { confirmationToken -> TODO("Completed in a later step.") }, resultCallback = { result -> TODO("Completed in a later step.") }, ) } val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) } ``` Embedded Mobile Payment Element は Jetpack Compose UI 用にビルドされ、`@Composable Content` メソッドを公開しています。クラシックビューを使用する場合は、`ComposeView` で組込み型決済要素をラップする必要があります。 ```xml ``` ```kotlin class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.checkout_screen_layout) val composeView = findViewById(R.id.compose_view) composeView.setContent { CheckoutScreen() } } } ``` ### Embedded Payment Element を設定する `Builder` オブジェクトには、`CreateIntentCallback` など、`EmbeddedPaymentElement` のインスタンス化に必要なコールバックが含まれています。今の時点では、実装は空のままにしておきます。 インスタンス化後、`EmbeddedPaymentElement.Configuration` および `PaymentSheet.IntentConfiguration` を使用して `configure` を呼び出します。`Configuration` オブジェクトには、`EmbeddedPaymentElement` の一般的な構成オプションが含まれています。`IntentConfiguration` オブジェクトには、金額や通貨など、特定の決済に関する詳細が含まれます。 ```kotlin import com.stripe.android.paymentsheet.PaymentSheet @Composable fun CheckoutScreen() { val embeddedBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) LaunchedEffect(embeddedPaymentElement) { embeddedPaymentElement.configure( intentConfiguration = PaymentSheet.IntentConfiguration(mode = PaymentSheet.IntentConfiguration.Mode.Setup( currency = "USD", ),// Optional intent configuration options... ), configuration = EmbeddedPaymentElement.Configuration.Builder("Powdur").build() ) } } ``` ### Embedded Payment Element ビューを追加する `EmbeddedPaymentElement` が正常に初期化されたら、その `@Composable Content` を決済 UI に配置します。 > コンテンツは、最初に表示された後に高さが変更される可能性があるため、スクロール可能なコンテナー内に配置する必要があります。 ```kotlin @Composable fun CheckoutScreen() { val embeddedBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) ..... val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxSize() .verticalScroll(scrollState) .padding(16.dp) ) { embeddedPaymentElement.Content() } } ``` この時点で、アプリを実行して、Embedded Mobile Payment Element を確認できます。 ### (任意)支払いの詳細を更新する 顧客が決済の詳細を変更した場合 (例えば、割引コードを適用するなど)、`EmbeddedPaymentElement` インスタンスを更新します。`configure` メソッドを再度呼び出すと、新しい値が同期され、UI に表示されます。 > Google Pay のような一部の決済手段では、UI に金額が表示されます。顧客が決済を変更し、`EmbeddedPaymentElement` を更新しない場合、UI は正しくない値を表示します。 `configure` コールが完了すると、`@Composable Content` と `paymentOption` は、`configure` コールで指定された新しい値で自動的に更新されます。 ```kotlin val intentConfiguration = PaymentSheet.IntentConfiguration( // Change paymentMethodTypes to customize the payment methods you want to accept mode = PaymentSheet.IntentConfiguration.Mode.Setup( currency = "USD", ), paymentMethodTypes = listOf("card") ) val configuration = EmbeddedPaymentElement.Configuration.Builder("Powdur") .build() LaunchedEffect(embeddedPaymentElement) { embeddedPaymentElement.configure( intentConfiguration = intentConfiguration, configuration = configuration, ) } ``` ### (任意)選択した支払いオプションを表示する Embedded Payment Element の `paymentOption` の observable `Flow` プロパティにアクセスすることで、決済オプションの詳細 (下 4 桁、カードロゴ、請求情報など) を表示できます。顧客がフォームシートを開く決済手段を選択すると、シートで **Continue** をタップした後に決済オプションが更新されます。 ```kotlin val selectedPaymentOption by embeddedPaymentElement.paymentOption.collectAsState() selectedPaymentOption?.let { paymentOption -> Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier .clickable( onClick = { }, ) .semantics { text = AnnotatedString(paymentOption.label) }, ) { Icon( painter = paymentOption.iconPainter, contentDescription = null, // decorative element modifier = Modifier.padding(horizontal = 4.dp), tint = Color.Unspecified, ) Text(text = paymentOption.label) } } ``` ### 設定を確定する 顧客が決済ボタンをタップしたら、`embeddedPaymentElement.confirm()` を呼び出して SetupIntent を確定します。確定する際は、ユーザーの操作を必ず無効にしてください。 フォームが表示されると、`EmbeddedPaymentElement` は、ユーザーが **Call to action** をクリックすると、`confirm` を呼び出します。選択された決済手段にフォームフィールドがない場合、`@Composable Content` の下にある **Call to action** をユーザーがクリックすると、`confirm` を呼び出します。 ```kotlin Button( onClick = { embeddedPaymentElement.confirm() } ) { Text("Confirm payment") } ``` 次に、`EmbeddedPaymentElement.Builder` に渡した `createIntentCallback` コールバックを実装し、サーバーにリクエストを送信します。サーバーは SetupIntent を作成し、[create a SetupIntent](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#submit-setup) ステップで説明する Client Secret を返します。 リクエストを受信したら、`CreateIntentResult` を使用して、サーバーレスポンスのクライアントシークレットまたはエラーとともに、Intent の作成結果を返します。`EmbeddedPaymentElement` は、クライアントシークレットを使用して SetupIntent を確認するか、UI にローカライズされたエラーメッセージを表示します。 ```kotlin import com.stripe.android.paymentsheet.CreateIntentResult val embeddedBuilder = remember { val embeddedBuilder = EmbeddedPaymentElement.Builder( createIntentCallback = { confirmationToken ->// Make a request to your own server and receive a client secret or an error. val networkResult = ... if (networkResult.isSuccess) { CreateIntentResult.Success(networkResult.clientSecret) } else { CreateIntentResult.Failure(networkResult.exception) } }, resultCallback = { result ->when (result) { is EmbeddedPaymentElement.Result.Completed -> { // Payment completed - show a confirmation screen. } is EmbeddedPaymentElement.Result.Failed -> { // Encountered an unrecoverable error. You can display the error to the user, log it, and so on } is EmbeddedPaymentElement.Result.Canceled -> { // Customer canceled - you should probably do nothing. } } }, ) } ``` ## Optional: 選択した決済オプションを消去する `EmbeddedPaymentElement` 以外の決済オプションがある場合は、選択した決済オプションの消去が必要になることがあります。これを行うには、`clearPaymentOption` API を使用して、選択した決済オプションの選択を解除します。 ```kotlin @Composable fun CheckoutScreen() { val embeddedPaymentElementBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedPaymentElementBuilder) .... Button( onClick = embeddedPaymentElement::clearPaymentOption ) { Text("Select external payment option") } } ``` ## Optional: 同意書を自分で表示する 法規制遵守を確保するため、Embedded Mobile Payment Element はデフォルトで同意書と法的免責事項を表示します。このテキストは**購入**ボタンの近くに配置する必要があります。ビューでこのテキストのデフォルト表示を無効にする場合は、自分で表示する必要があります。 > 規制を遵守するため、実装で同意書のテキストを正確に表示する必要があります。自分で表示する場合は、`Text` コンポーザブルにテキストを含めて、URL が正しく表示されることを確認してください。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder("Merchant, Inc") .embeddedViewDisplaysMandateText(false) .build() .... val selectedPaymentOption by embeddedPaymentElement.paymentOption.collectAsState() selectedPaymentOption?.mandateText?.let { mandateText -> Text(mandateText) } ``` ## Optional: 顧客がシートですぐに設定を確定できるようにする 設定をすぐに確定するようにフォーム画面のボタンを設定するには、`EmbeddedPaymentElement.Configuration` オブジェクトで `formSheetAction` を設定します。 画面が閉じられた後、設定結果によって完了ブロックが実行されます。埋め込み UI は設定完了後には使用できなくなるため、実装でユーザーを領収書画面など別の画面に誘導することをお勧めします。 ```kotlin @Composable fun CheckoutScreen() { val embeddedBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) LaunchedEffect(embeddedPaymentElement) { embeddedPaymentElement.configure( intentConfiguration = ...., configuration = EmbeddedPaymentElement.Configuration.Builder("Powdur") .formSheetAction(EmbeddedPaymentElement.FormSheetAction.Confirm) .build() ) } } ``` ## SetupIntent を作成する [サーバー側] サーバー側で *SetupIntent* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) を作成し、これをします。決済手段は[ダッシュボード](https://dashboard.stripe.com/settings/payment_methods)から管理できます。Stripe は、決済手段の制約とその他のパラメーターを評価して、利用可能な決済手段のリストを決定します。 コールが成功した場合は、SetupIntent *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)) を返します。コールが失敗した場合は、[エラーを処理](https://docs.stripe.com/error-handling.md)して、エラーメッセージと顧客向けの簡単な説明を返します。 > すべての IntentConfiguration プロパティが SetupIntent ([usage](https://docs.stripe.com/api/setup_intents/object.md#setup_intent_object-usage) など) と一致していることを確認します。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { customer: ..., # The Customer ID you previously created automatic_payment_methods: {enabled: true}, } begin intent = client.v1.setup_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` ## 保存された支払い方法に後で請求する [サーバー側] > `bancontact` と `ideal` は、デフォルトでは 1 回限りの支払い方法です。以降も使用できるように設定すると、再利用可能な支払い方法タイプ `sepa_debit` が生成されます。このため、保存された支払い方法を照会するには `sepa_debit` を使用する必要があります。 > #### 法令遵守 > > 顧客の支払いの詳細を保存する際、お客様は適用されるすべての法律、規制、ネットワークの規則に準拠する責任があります。将来の購入に備えて顧客に過去の決済手段を提供する際は、必ず、特定の将来の使用に備えて決済手段の詳細を保存することについての同意を顧客から収集した決済手段をリストアップします。顧客に関連付けられた決済手段のうち、将来の購入用の保存済みの決済手段として顧客に提示できるものと提示できないものを区別するには、[allow_redisplay](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-allow_redisplay) パラメーターを使用します。 請求する決済手段を見つけるには、顧客に関連付けられている決済手段を一覧表示します。この例ではカードを一覧表示していますが、サポートされている任意の [type](https://docs.stripe.com/api/payment_methods/object.md#payment_method_object-type) を一覧表示できます。 > #### Accounts v2 API を使用した顧客の表現 > > Accounts v2 API では、Connect ユーザーには一般提供され、その他の Stripe ユーザーには公開プレビューで提供されます。Accounts v2 プレビューの一部である場合は、コードで[プレビューバージョン](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning)を指定する必要があります。 > > Accounts v2 プレビューへのアクセスをリクエストするには、 > > ほとんどのユースケースでは、[Customer](https://docs.stripe.com/api/customers.md) オブジェクトを使用するのではなく、[顧客を顧客設定済みの Account オブジェクトとしてモデル化する](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md)ことをお勧めします。 #### Accounts v2 ```curl curl -G https://api.stripe.com/v1/payment_methods \ -u "<>:" \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d type=card ``` #### Customers v1 ```curl curl -G https://api.stripe.com/v1/payment_methods \ -u "<>:" \ -d "customer={{CUSTOMER_ID}}" \ -d type=card ``` 顧客に *オフセッション* (A payment is described as off-session if it occurs without the direct involvement of the customer, using previously-collected payment information) で請求する準備ができたら、`Customer`または顧客設定の`Account`の ID と `PaymentMethod` ID を使用して、決済の金額と通貨を指定した `PaymentIntent` を作成します。オフセッション決済を行うには、他にもいくつかのパラメーターを設定します。 - 顧客が決済フローにおらず、カード発行会社や銀行などのパートナーからの認証リクエストに対応できないことを示すには、[off_session](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-off_session) を true に設定します。決済フロー中にパートナー (カード発行会社や銀行など) が認証をリクエストした場合、Stripe は以前の *オンセッション* (A payment is described as on-session if it occurs while the customer is actively in your checkout flow and able to authenticate the payment method) 取引の顧客情報を使用して免除をリクエストします。免除の条件が満たされない場合、`PaymentIntent` がエラーを返すことがあります。 - [confirm](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-confirm) を true に設定すると、`PaymentIntent` の作成時に即座に確定がトリガーされる。 - [payment_method](https://docs.stripe.com/api.md#create_payment_intent-payment_method) を `PaymentMethod` の ID に設定します。 - 導入で顧客をどのように表すかに応じて、[customer_account](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer_account) を顧客設定の `Account` の ID に設定するか、[customer](https://docs.stripe.com/api.md#create_payment_intent-customer) を `Customer` の ID に設定します。 #### Accounts v2 ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=usd \ -d "automatic_payment_methods[enabled]=true" \ -d "customer_account={{CUSTOMERACCOUNT_ID}}" \ -d payment_method={{PAYMENT_METHOD_ID}} \ --data-urlencode "return_url=https://example.com/order/123/complete" \ -d off_session=true \ -d confirm=true ``` #### Customers v1 ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=usd \ -d "automatic_payment_methods[enabled]=true" \ -d "customer={{CUSTOMER_ID}}" \ -d payment_method={{PAYMENT_METHOD_ID}} \ --data-urlencode "return_url=https://example.com/order/123/complete" \ -d off_session=true \ -d confirm=true ``` ## 実装をテストする #### カード | カード番号 | シナリオ | テスト方法 | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | | 4242424242424242 | カード支払いは成功し、認証は必要とされません。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000002500003155 | カード支払いには*認証* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase)が必要です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000000000009995 | カードは、`insufficient_funds` などの拒否コードで拒否されます。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 6205500000000000004 | UnionPay カードは、13 ~ 19 桁の可変長です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | #### 銀行へのリダイレクト | 決済手段 | シナリオ | テスト方法 | | ---------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Bancontact、iDEAL | 顧客は、リダイレクトベースの即時通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | リダイレクトベースの任意の支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払い失敗)** をクリックします。 | | Pay by Bank | 顧客はリダイレクトベースの、[遅延通知型](https://docs.stripe.com/payments/payment-methods.md#payment-notification)の支払い方法で支払いに成功します。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Complete test payment (テスト支払い完了)** をクリックします。 | | Pay by Bank | 顧客は、リダイレクトベースの遅延通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払いを失敗させる)** をクリックします。 | | BLIK | BLIK による支払いはさまざまな理由で失敗します。即時の失敗 (コードの有効期限切れや無効など)、遅延型のエラー (銀行による拒否など)、またはタイムアウト (顧客が時間内に応答しなかったなど) などがあります。 | メールパターンを使用して[さまざまな失敗をシミュレーションします。](https://docs.stripe.com/payments/blik/accept-a-payment.md#simulate-failures) | #### 口座引き落とし | 決済手段 | シナリオ | テスト方法 | | -------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | SEPA ダイレクトデビット | 顧客が SEPA ダイレクトデビットによる支払いに成功しました。 | 口座番号 `AT321904300235473204` を使用して、フォームに入力します。確定された PaymentIntent のステータスはまず、processing に移行し、3 分後に succeeded ステータスに移行します。 | | SEPA ダイレクトデビット | 顧客の Payment Intent のステータスが、`processing` から `requires_payment_method` に移行します。 | 口座番号 `AT861904300235473202` を使用して、フォームに入力します。 | 実装内容をテストするためのその他の情報については、[テスト](https://docs.stripe.com/testing.md)をご覧ください。 ## Optional: 保存済みのカードを有効にする [サーバー側] [クライアント側] `EmbeddedPaymentElement` では、顧客がカードを保存でき、利用可能な決済手段に顧客が保存済みのカードを含めることもできます。顧客は、サーバー上で関連付けられた顧客設定済みの [Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) オブジェクトまたは [Customer](https://docs.stripe.com/api/customers/create.md) オブジェクトを持っている必要があります。顧客がカードを保存できるようにするチェックボックスを有効にするには、[CustomerSession](https://docs.stripe.com/api/customer_sessions.md) を作成し、`payment_method_save` を `enabled` に設定します。 #### Accounts v2 ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. const stripe = require("stripe")("<>"); const express = require('express'); const app = express(); app.set('trust proxy', true); app.use(express.json()); app.post('/payment-sheet', async (req, res) => { // Use an existing Account ID if this is a returning customer. const customer_account = await stripe.v2.core.accounts.create(); const customerSession = await stripe.customerSessions.create({ customer_account: customer_account.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer_account: customer_account.id, }); }); ``` 次に、`EmbeddedPaymentElement` を、顧客の ID と `CustomerSession` の client secret を使用して設定または表示します。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Powdur") .customer( PaymentSheet.CustomerConfiguration.createWithCustomerSession( id = customerAccountId, clientSecret = customerSessionClientSecret, ) ) .build() embeddedPaymentElement.configure( intentConfiguration = // ... , configuration = configuration, ) ``` #### Customers v1 ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. const stripe = require("stripe")("<>"); const express = require('express'); const app = express(); app.set('trust proxy', true); app.use(express.json()); app.post('/payment-sheet', async (req, res) => { // Use an existing Customer ID if this is a returning customer. const customer = await stripe.customers.create(); const customerSession = await stripe.customerSessions.create({ customer: customer.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer: customer.id, }); }); ``` 次に、`EmbeddedPaymentElement` を、顧客の ID と `CustomerSession` の client secret を使用して設定または表示します。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Powdur") .customer( PaymentSheet.CustomerConfiguration.createWithCustomerSession( id = customerId, clientSecret = customerSessionClientSecret, ) ) .build() embeddedPaymentElement.configure( intentConfiguration = // ... , configuration = configuration, ) ``` ## Optional: 遅延型の支払い方法を許可する [クライアント側] *遅延型の決済手段* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods)では、購入の終了時に顧客から売上を受け取ることが保証されません。これは、決済に時間がかかる (アメリカの銀行口座、SEPA デビット、iDEAL、Bancontact、Sofort など) か、完了に顧客の対応を必要とする (OXXO、コンビニ決済、Boleto など) という理由によります。 デフォルトでは、`EmbeddedPaymentElement` は、遅延型の決済手段を受け入れている場合でも表示しません。有効にした遅延型の決済手段を表示するには、`EmbeddedPaymentElement.Configuration` で `allowsDelayedPaymentMethods` を true に設定します。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Powdur") .allowsDelayedPaymentMethods(true) .build() ``` 顧客が `EmbeddedPaymentElement` で遅延型の決済手段を適切に使用すると、`EmbeddedPaymentElement.Result.Completed` の支払い結果が返されます。 ## Optional: Google Pay を有効にする > 決済画面に専用の **Google Pay** ボタンがある場合は、[Google Pay ガイド](https://docs.stripe.com/google-pay.md?platform=android)の内容に従ってください。Embedded Payment Element を使用して、他のタイプの決済手段を処理することも可能です。 ### 実装方法を設定する Google Pay を使用するには、まず以下を **AndroidManifest.xml** の `` に追加し、Google Pay API を有効化します。 ```xml ... ``` 詳細は、Google Pay の Android 向け [Google Pay API を設定する](https://developers.google.com/pay/api/android/guides/setup) を参照してください。 ### Google Pay を追加する 構築済みのシステムに Google Pay を追加するには、`EmbeddedPaymentElement.Configuration` を初期化する際に、ご利用の Google Pay 環境 (本番またはテスト) と[ビジネスの国コード](https://dashboard.stripe.com/settings/account)を指定して、[PaymentSheet.GooglePayConfiguration](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-google-pay-configuration/index.html) を渡します。 ```kotlin val googlePayConfiguration = PaymentSheet.GooglePayConfiguration( environment = PaymentSheet.GooglePayConfiguration.Environment.Test, countryCode = "US", currencyCode = "USD" // Required for Setup Intents, optional for Payment Intents ) val configuration = EmbeddedPaymentElement.Configuration.Builder("Merchant, Inc.") .googlePay(googlePayConfiguration) .build() ``` ### Google Pay をテストする Google では、[テストクレジットカードスイート](https://developers.google.com/pay/api/android/guides/resources/test-card-suite) を使用してテスト決済を行うことができます。Stripe [テストカード](https://docs.stripe.com/testing.md) をテストスイートと併用できます。 Google Pay がサポートされている国では、シミュレーションされたデバイスではなく、物理的な Android デバイスを使用して Google Pay をテストする必要があります。Google ウォレットに保存した実際のクレジットカードを使用して、テスト用デバイスの Google アカウントにログインします。 ## Optional: カードのスキャンを有効にする カードスキャンサポートを有効にするには、[Google Pay & Wallet Console](https://pay.google.com/business/console?utm_source=devsite&utm_medium=devsite&utm_campaign=devsite) から Google Pay API への[本番アクセスをリクエスト](https://developers.google.com/pay/api/android/guides/test-and-deploy/request-prod-access)します。 - Google Pay を有効にしている場合、対象のデバイスの UI でカードスキャン機能が自動的に利用可能になります。対象のデバイスの詳細については、[Google Pay API の制約](https://developers.google.com/pay/payment-card-recognition/debit-credit-card-recognition)をご覧ください。 - **重要:** カードスキャン機能は、[Google Pay & Wallet Console](https://pay.google.com/business/console) に登録された同じ署名キーで署名されたビルドにのみ表示されます。異なる署名キーを使用したテストまたはデバッグビルド (Firebase App Tester を通じて配布されたビルドなど) では、 **カードをスキャン** オプションは表示されません。プレリリースビルドでカードスキャンをテストするには、以下のいずれかを行う必要があります。 - 本番環境の署名キーを使用してテストビルドに署名する - Google Pay & Wallet Console にテスト署名キーのフィンガープリントを追加する お客様のアプリが Google Pay に対応していない場合は、Stripe カードスキャナーを使用できます。 > Stripe カードスキャナーは公開プレビュー版です。 カードスキャンに対応する機能を有効にするには、`stripecardscan` を `dependencies` ブロック (お客様の [app/build.gradle](https://developer.android.com/studio/build/dependencies) ファイル内) に追加します: #### Groovy ```groovy implementation 'com.stripe:stripecardscan:23.8.0' ``` ## Optional: 画面をカスタマイズする カスタマイズはすべて `EmbeddedPaymentElement.Configuration` オブジェクトで設定されます。 ### デザイン [Appearance API](https://docs.stripe.com/elements/appearance-api/embedded-mobile.md?platform=android) を使用して、アプリのデザインに合うように色やフォントなどをカスタマイズします。 ### 顧客の住所を収集 [Address Element](https://docs.stripe.com/elements/address-element.md?platform=android) を使用して、顧客から配送先住所と請求先住所を収集します。 ### 表示するビジネス名をカスタマイズする `merchantDisplayName` を設定し、顧客に表示するビジネス名を指定します。名前を指定しない場合、既定値はアプリ名になります。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .build() ``` ### デフォルトの請求先情報を指定する `EmbeddedPaymentElement` で収集される請求先情報のデフォルト値を設定するには、`defaultBillingDetails` プロパティを設定します。`EmbeddedPaymentElement` の各フィールドに、指定した値が事前に読み込まれます。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .defaultBillingDetails( PaymentSheet.BillingDetails( address = PaymentSheet.Address( country = "US", ), email = "foo@bar.com" ) ) .build() ``` ### 請求先情報の収集を設定する `BillingDetailsCollectionConfiguration` を使用して、`EmbeddedPaymentElement` で請求詳細を収集する方法を指定します。 顧客の名前、メールアドレス、電話番号、住所を収集できます。 UI でデフォルトの請求詳細が収集されない場合でも、それらの詳細を PaymentMethod オブジェクトに関連付けるには、`billingDetailsCollectionConfiguration.attachDefaultsToPaymentMethod` を `true` に設定します。 ```kotlin import com.stripe.android.paymentsheet.PaymentSheet.BillingDetailsCollectionConfiguration val billingDetails = PaymentSheet.BillingDetails( email = "foo@bar.com" ) val billingDetailsCollectionConfiguration = BillingDetailsCollectionConfiguration( attachDefaultsToPaymentMethod = true, name = BillingDetailsCollectionConfiguration.CollectionMode.Always, email = BillingDetailsCollectionConfiguration.CollectionMode.Never, address = BillingDetailsCollectionConfiguration.AddressCollectionMode.Full, ) val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .defaultBillingDetails(billingDetails) .billingDetailsCollectionConfiguration(billingDetailsCollectionConfiguration) .build() ``` > 情報の収集に適用される法律については、弁護士に相談してください。電話番号は、取引に必要な場合にのみ収集してください。 # 支払いを受け付けて決済手段を保存する > This is a 支払いを受け付けて決済手段を保存する for when platform is android and type is paymentsfu. View the full page at https://docs.stripe.com/payments/mobile/accept-payment-embedded?platform=android&type=paymentsfu. [Payment Intents API](https://docs.stripe.com/api/payment_intents.md) を使用して、購入から支払い詳細を保存します。以下のようなユースケースがあります。 - 顧客に EC ストアの注文の請求を行い、以降の購入に備えて情報を保存します。 - 継続支払いの初回の支払いを開始します。 - 頭金を請求し、情報を保存して後から全額を請求できるようにします。 > #### カード提示取引 > > Stripe Terminal を介した支払いなどのカード提示取引では、別のプロセスを使用して決済手段を保存します。詳細については、[Terminal のドキュメント](https://docs.stripe.com/terminal/features/saving-payment-details/save-after-payment.md)をご覧ください。 ## 法令遵守 顧客の決済情報を将来の利用に備えて保存する際 (将来の購入に向けた決済フローでの決済手段の表示、または顧客がウェブサイトやアプリを積極的に使用していないときの請求など) は、適用されるすべての法令、規制、ネットワーク規則を遵守する責任があります。顧客の決済手段を保存または請求する前に、以下を確認してください。 - 決済手段の詳細を保存する方法を示す、次のような規約をウェブサイトまたはアプリに追加します。 - 指定された取引において顧客に代わって決済または一連の決済を開始することに対する顧客の同意。 - 予期される支払い時期と支払い頻度 (たとえば、請求が予定されている分割払いなのか、サブスクリプションの決済なのか、あるいは予定されていないトップアップなのか)。 - 支払い金額の決定方法 - 支払い方法をサブスクリプションサービスに使用する場合は、キャンセルに関するポリシー - 規約に記載されている目的にのみ、保存された決済手段を使用してください。 - この特定の使用について顧客から明示的な同意を取得します。たとえば、「将来の使用に備えて決済手段を保存する」チェックボックスを含めます。 - 顧客の規約への書面による同意を記録します。 ## Stripe を設定する [サーバー側] [クライアント側] まず、Stripe アカウントが必要です。[今すぐ登録してください](https://dashboard.stripe.com/register)。 ### サーバー側 この組み込みは、Stripe API と通信するサーバー上にエンドポイントを必要とします。サーバーから Stripe API へのアクセスには、Stripe の公式ライブラリを使用します。 #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### クライアント側 [Stripe Android SDK](https://github.com/stripe/stripe-android) はオープンソースであり、[詳細なドキュメントが提供されています](https://stripe.dev/stripe-android/)。 SDK をインストールするには、[app/build.gradle](https://developer.android.com/studio/build/dependencies) ファイルの `dependencies` ブロックに `stripe-android` を追加します。 #### Kotlin ```kotlin plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.plugin.compose") } android { ... kotlinOptions { jvmTarget = "11" } buildFeatures { compose true } } dependencies { // ... // Stripe Android SDK implementation("com.stripe:stripe-android:23.8.0") // Include the financial connections SDK to support US bank account as a payment method implementation("com.stripe:financial-connections:23.8.0") } ``` > SDK の最新リリースおよび過去バージョンの詳細については、GitHub の [Releases](https://github.com/stripe/stripe-android/releases) ページをご覧ください。新しいリリースの公開時に通知を受け取るには、[リポジトリのリリースを確認](https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository)してください。 また、SDK が Stripe への API コールを実行できるように、[公開可能キー](https://dashboard.stripe.com/apikeys)を設定する必要もあります。すぐに開始するには、導入中にクライアント側でこれをハードコード化できますが、本番環境ではサーバーから公開可能キーを取得します。 ```kotlin // Set your publishable key: remember to change this to your live publishable key in production // See your keys here: https://dashboard.stripe.com/apikeys PaymentConfiguration.init(context, publishableKey = "<>") ``` ## 支払い方法を有効にする [支払い方法の設定](https://dashboard.stripe.com/settings/payment_methods)を表示して、サポートする支払い方法を有効にします。*PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成するには、少なくとも 1 つは支払い方法を有効にする必要があります。 多くの顧客から決済を受け付けられるよう、Stripe では、カードやその他一般的な決済手段がデフォルトで有効になっていますが、ビジネスや顧客に適した追加の決済手段を有効にすることをお勧めします。プロダクトと決済手段のサポートについては[決済手段のサポート](https://docs.stripe.com/payments/payment-methods/payment-method-support.md)を、手数料については[料金体系ページ](https://stripe.com/pricing/local-payment-methods)をご覧ください。 ## Customer を作成する [サーバー側] 将来の決済に備えて決済手段を設定するには、顧客を表すオブジェクトに関連付ける必要があります。顧客がアカウントを作成したとき、または初めて取引したときに、Accounts v2 API を使用して顧客設定の [Account](https://docs.stripe.com/api/v2/core/accounts/create.md) オブジェクト、または Customers API を使用して [Customer](https://docs.stripe.com/api/customers/create.md) オブジェクトを作成します。 > #### Accounts v2 API を使用した顧客の表現 > > Accounts v2 API では、Connect ユーザーには一般提供され、その他の Stripe ユーザーには公開プレビューで提供されます。Accounts v2 プレビューの一部である場合は、コードで[プレビューバージョン](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning)を指定する必要があります。 > > Accounts v2 プレビューへのアクセスをリクエストするには、 > > ほとんどのユースケースでは、[Customer](https://docs.stripe.com/api/customers.md) オブジェクトを使用するのではなく、[顧客を顧客設定済みの Account オブジェクトとしてモデル化する](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md)ことをお勧めします。 ```curl curl -X POST https://api.stripe.com/v1/customers \ -u "<>:" ``` ## 支払い情報を収集する [クライアント側] Embedded Mobile Payment Element は、ネイティブモバイルアプリの決済ページで使用するために設計されています。この Element には決済手段の一覧が表示され、アプリのデザインに合わせてカスタマイズできます。 顧客が**カード**行をタップすると、決済手段の詳細を入力できる画面が開きます。画面にデフォルトで**続行**というボタンが表示され、タップすると入力画面が閉じます。これにより、顧客は決済フローで支払いを完了できます。 ![Embedded Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/android-embedded.1aefe2ca79ed073e350ec629a5af22d5.png) また、続行するのではなく、すぐに支払いを完了するようにボタンを設定することもできます。これを行うには、ガイドに沿って手順を実行した後で、[このステップ](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#embedded-let-customer-pay-immediately)を完了します。 ### Embedded Payment Element を初期化する #### Jetpack Compose `EmbeddedPaymentElement.Builder` で `rememberEmbeddedPaymentElement` 関数を使用して、`EmbeddedPaymentElement` のインスタンスを初期化します。 `CreateIntentWithConfirmationTokenCallback` を渡します。今のところ、実装は空のままにしておきます。 ```kotlin import com.stripe.android.paymentelement.EmbeddedPaymentElement import com.stripe.android.paymentelement.rememberEmbeddedPaymentElement @Composable fun CheckoutScreen() { val embeddedBuilder = remember { EmbeddedPaymentElement.Builder( createIntentCallback = { confirmationToken, -> TODO("You'll implement this in the "Confirm the payment" step") }, resultCallback = { result -> TODO("You'll implement this in the "Confirm the payment" step") }, ) } val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) } ``` #### ビュー (クラシック) `EmbeddedPaymentElement.Builder` で `rememberEmbeddedPaymentElement` 関数を使用して、`EmbeddedPaymentElement` のインスタンスを初期化します。 ```kotlin import com.stripe.android.paymentelement.EmbeddedPaymentElement import com.stripe.android.paymentelement.rememberEmbeddedPaymentElement @Composable fun CheckoutScreen() { val embeddedBuilder = remember { EmbeddedPaymentElement.Builder( createIntentCallback = { confirmationToken -> TODO("Completed in a later step.") }, resultCallback = { result -> TODO("Completed in a later step.") }, ) } val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) } ``` Embedded Mobile Payment Element は Jetpack Compose UI 用にビルドされ、`@Composable Content` メソッドを公開しています。クラシックビューを使用する場合は、`ComposeView` で組込み型決済要素をラップする必要があります。 ```xml ``` ```kotlin class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.checkout_screen_layout) val composeView = findViewById(R.id.compose_view) composeView.setContent { CheckoutScreen() } } } ``` ### Embedded Payment Element を設定する `Builder` オブジェクトには、`CreateIntentCallback` など、`EmbeddedPaymentElement` のインスタンス化に必要なコールバックが含まれています。今の時点では、実装は空のままにしておきます。 インスタンス化後、`EmbeddedPaymentElement.Configuration` および `PaymentSheet.IntentConfiguration` を使用して `configure` を呼び出します。`Configuration` オブジェクトには、`EmbeddedPaymentElement` の一般的な構成オプションが含まれています。`IntentConfiguration` オブジェクトには、金額や通貨など、特定の決済に関する詳細が含まれます。 > `EmbeddedPaymentElement` に決済手段を保存するには、`setupFutureUsage` を設定します。`IntentConfiguration` の [setupFutureUsage](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-setup_future_usage) パラメータを `onSession` または `offSession` に設定できます。この値は、利用可能なすべての決済手段に自動的に適用されます。 ```kotlin import com.stripe.android.paymentsheet.PaymentSheet import com.stripe.android.paymentsheet.PaymentSheet.IntentConfiguration.SetupFutureUse @Composable fun CheckoutScreen() { val embeddedBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) LaunchedEffect(embeddedPaymentElement) { embeddedPaymentElement.configure( intentConfiguration = PaymentSheet.IntentConfiguration(mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 1099, currency = "USD",setupFutureUse = SetupFutureUse.OffSession,),// Optional intent configuration options... ), configuration = EmbeddedPaymentElement.Configuration.Builder("Powdur").build() ) } } ``` ### Embedded Payment Element ビューを追加する `EmbeddedPaymentElement` が正常に初期化されたら、その `@Composable Content` を決済 UI に配置します。 > コンテンツは、最初に表示された後に高さが変更される可能性があるため、スクロール可能なコンテナー内に配置する必要があります。 ```kotlin @Composable fun CheckoutScreen() { val embeddedBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) ..... val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxSize() .verticalScroll(scrollState) .padding(16.dp) ) { embeddedPaymentElement.Content() } } ``` この時点で、アプリを実行して、Embedded Mobile Payment Element を確認できます。 ### (任意)支払いの詳細を更新する 顧客が決済の詳細を変更した場合 (例えば、割引コードを適用するなど)、`EmbeddedPaymentElement` インスタンスを更新します。`configure` メソッドを再度呼び出すと、新しい値が同期され、UI に表示されます。 > Google Pay のような一部の決済手段では、UI に金額が表示されます。顧客が決済を変更し、`EmbeddedPaymentElement` を更新しない場合、UI は正しくない値を表示します。 `configure` コールが完了すると、`@Composable Content` と `paymentOption` は、`configure` コールで指定された新しい値で自動的に更新されます。 ```kotlin val intentConfiguration = PaymentSheet.IntentConfiguration( // Update the amount to reflect the price after applying the discount code mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 999, currency = "USD", setupFutureUse = SetupFutureUse.OffSession, ), ) val configuration = EmbeddedPaymentElement.Configuration.Builder("Powdur") .build() LaunchedEffect(embeddedPaymentElement) { embeddedPaymentElement.configure( intentConfiguration = intentConfiguration, configuration = configuration, ) } ``` ### (任意)選択した支払いオプションを表示する Embedded Payment Element の `paymentOption` の observable `Flow` プロパティにアクセスすることで、決済オプションの詳細 (下 4 桁、カードロゴ、請求情報など) を表示できます。顧客がフォームシートを開く決済手段を選択すると、シートで **Continue** をタップした後に決済オプションが更新されます。 ```kotlin val selectedPaymentOption by embeddedPaymentElement.paymentOption.collectAsState() selectedPaymentOption?.let { paymentOption -> Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier .clickable( onClick = { }, ) .semantics { text = AnnotatedString(paymentOption.label) }, ) { Icon( painter = paymentOption.iconPainter, contentDescription = null, // decorative element modifier = Modifier.padding(horizontal = 4.dp), tint = Color.Unspecified, ) Text(text = paymentOption.label) } } ``` ### 支払いを確定する 顧客が決済ボタンをタップしたら、`embeddedPaymentElement.confirm()` を呼び出して支払いを開始します。確定する際は、ユーザーの操作を必ず無効にしてください。 フォームが表示されると、`EmbeddedPaymentElement` は、ユーザーが **Call to action** をクリックすると、`confirm` を呼び出します。選択された決済手段にフォームフィールドがない場合、`@Composable Content` の下にある **Call to action** をユーザーがクリックすると、`confirm` を呼び出します。 ```kotlin Button( onClick = { embeddedPaymentElement.confirm() } ) { Text("Confirm payment") } ``` 次に、すでに `EmbeddedPaymentElement.Builder` に渡している `createIntentCallback` コールバックを実装して、サーバーにリクエストを送信します。サーバーは `PaymentIntent` を作成し、その client secret を返します。詳細については、[PaymentIntent を作成する](https://docs.stripe.com/payments/mobile/accept-payment-embedded.md#submit-payment)をご覧ください。 リクエストを受信したら、`CreateIntentResult` を使用して、サーバーレスポンスのクライアントシークレットまたはエラーとともに、Intent の作成結果を返します。`EmbeddedPaymentElement` は、クライアントシークレットを使用して PaymentIntent を確認するか、UI にローカライズされたエラーメッセージを表示します。 ```kotlin import com.stripe.android.paymentsheet.CreateIntentResult val embeddedBuilder = remember { val embeddedBuilder = EmbeddedPaymentElement.Builder( createIntentCallback = { confirmationToken ->// Make a request to your own server and receive a client secret or an error. val networkResult = ... if (networkResult.isSuccess) { CreateIntentResult.Success(networkResult.clientSecret) } else { CreateIntentResult.Failure(networkResult.exception) } }, resultCallback = { result ->when (result) { is EmbeddedPaymentElement.Result.Completed -> { // Payment completed - show a confirmation screen. } is EmbeddedPaymentElement.Result.Failed -> { // Encountered an unrecoverable error. You can display the error to the user, log it, and so on } is EmbeddedPaymentElement.Result.Canceled -> { // Customer canceled - you should probably do nothing. } } }, ) } ``` ## Optional: 選択した決済オプションを消去する `EmbeddedPaymentElement` 以外の決済オプションがある場合は、選択した決済オプションの消去が必要になることがあります。これを行うには、`clearPaymentOption` API を使用して、選択した決済オプションの選択を解除します。 ```kotlin @Composable fun CheckoutScreen() { val embeddedPaymentElementBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedPaymentElementBuilder) .... Button( onClick = embeddedPaymentElement::clearPaymentOption ) { Text("Select external payment option") } } ``` ## Optional: 同意書を自分で表示する 法規制遵守を確保するため、Embedded Mobile Payment Element はデフォルトで同意書と法的免責事項を表示します。このテキストは**購入**ボタンの近くに配置する必要があります。ビューでこのテキストのデフォルト表示を無効にする場合は、自分で表示する必要があります。 > 規制を遵守するため、実装で同意書のテキストを正確に表示する必要があります。自分で表示する場合は、`Text` コンポーザブルにテキストを含めて、URL が正しく表示されることを確認してください。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder("Merchant, Inc") .embeddedViewDisplaysMandateText(false) .build() .... val selectedPaymentOption by embeddedPaymentElement.paymentOption.collectAsState() selectedPaymentOption?.mandateText?.let { mandateText -> Text(mandateText) } ``` ## Optional: 顧客が画面ですぐに支払えるようにする ![Embedded Payment Element](https://b.stripecdn.com/docs-statics-srv/assets/embedded-pay-immediate.057c691220d43158ac8000de10815ed9.png) 支払いをすぐに確定するようにフォーム画面のボタンを設定するには、`EmbeddedPaymentElement.Configuration` オブジェクトで `formSheetAction` を設定します。 画面が閉じられた後、支払い結果によって完了ブロックが実行されます。埋め込み UI は支払い完了後には使用できなくなるため、実装でユーザーを領収書画面など別の画面に誘導することをお勧めします。 ```kotlin @Composable fun CheckoutScreen() { val embeddedBuilder = .... val embeddedPaymentElement = rememberEmbeddedPaymentElement(embeddedBuilder) LaunchedEffect(embeddedPaymentElement) { embeddedPaymentElement.configure( intentConfiguration = ...., configuration = EmbeddedPaymentElement.Configuration.Builder("Powdur") .formSheetAction(EmbeddedPaymentElement.FormSheetAction.Confirm) .build() ) } } ``` ## PaymentIntent を作成する [サーバー側] サーバー側で、金額と通貨を指定して *PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成します。支払い方法は[ダッシュボード](https://dashboard.stripe.com/settings/payment_methods)で管理できます。Stripe は取引額、通貨、決済フローなどの要素に基づいて、適切な支払い方法が返されるように処理します。悪意のある顧客が金額を恣意的に選択できないようにするために、請求額はクライアント側ではなく、常にサーバー側 (信頼性の高い環境) で指定してください。 コールが成功した場合は、PaymentIntent *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)) を返します。コールが失敗した場合は、[エラーを処理](https://docs.stripe.com/error-handling.md)して、エラーメッセージと顧客向けの簡単な説明を返します。 > すべての IntentConfiguration プロパティが PaymentIntent (`setup_future_usage`、`amount`、`currency` など) と一致していることを確認します。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { customer: ..., # The Customer ID you previously created amount: 1099, currency: 'usd', setup_future_usage: 'off_session', automatic_payment_methods: {enabled: true}, } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` ## 支払い後のイベントを処理する [サーバー側] 支払いが完了すると、Stripe は [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) イベントを送信します。[ダッシュボードの Webhook ツール](https://dashboard.stripe.com/webhooks)を使用するか [Webhook のガイド](https://docs.stripe.com/webhooks/quickstart.md)に従ってこれらのイベントを受信し、顧客への注文確認メールの送信、データベースでの売上の記録、配送ワークフローの開始などのアクションを実行します。 クライアントからのコールバックを待つのではなく、これらのイベントをリッスンします。クライアントでは、コールバックが実行される前に顧客がブラウザーのウィンドウを閉じたり、アプリを終了する場合、また悪意を持つクライアントがレスポンスを不正操作する場合もあります。非同期型のイベントをリッスンするよう組み込みを設定すると、単一の組み込みで[複数の異なるタイプの支払い方法](https://stripe.com/payments/payment-methods-guide)を受け付けることができます。 Payment Element を使用して支払いを回収する場合は、`payment_intent.succeeded` イベントのほかにこれらのイベントを処理することをお勧めします。 | イベント | 説明 | アクション | | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.succeeded) | 顧客が正常に支払いを完了したときに送信されます。 | 顧客に注文の確定を送信し、顧客の注文の*フルフィルメント* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected)を実行します。 | | [payment_intent.processing](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.processing) | 顧客が正常に支払いを開始したが、支払いがまだ完了していない場合に送信されます。このイベントは、多くの場合、顧客が口座引き落としを開始するときに送信されます。その後、`payment_intent.succeeded` イベント、また、失敗の場合は `payment_intent.payment_failed` イベントが送信されます。 | 顧客に注文確認メールを送信し、支払いが保留中であることを示します。デジタル商品では、支払いの完了を待たずに注文のフルフィルメントを行うことが必要になる場合があります。 | | [payment_intent.payment_failed](https://docs.stripe.com/api/events/types.md?lang=php#event_types-payment_intent.payment_failed) | 顧客が支払いを試みたが、支払いに失敗する場合に送信されます。 | 支払いが `processing` から `payment_failed` に変わった場合は、顧客に再度支払いを試すように促します。 | ## 実装をテストする #### カード | カード番号 | シナリオ | テスト方法 | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | | 4242424242424242 | カード支払いは成功し、認証は必要とされません。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000002500003155 | カード支払いには*認証* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase)が必要です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 4000000000009995 | カードは、`insufficient_funds` などの拒否コードで拒否されます。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | | 6205500000000000004 | UnionPay カードは、13 ~ 19 桁の可変長です。 | クレジットカード番号と、任意の有効期限、セキュリティコード、郵便番号を使用してクレジットカードフォームに入力します。 | #### 銀行へのリダイレクト | 決済手段 | シナリオ | テスト方法 | | ---------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | Bancontact、iDEAL | 顧客は、リダイレクトベースの即時通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | リダイレクトベースの任意の支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払い失敗)** をクリックします。 | | Pay by Bank | 顧客はリダイレクトベースの、[遅延通知型](https://docs.stripe.com/payments/payment-methods.md#payment-notification)の支払い方法で支払いに成功します。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Complete test payment (テスト支払い完了)** をクリックします。 | | Pay by Bank | 顧客は、リダイレクトベースの遅延通知型の支払い方法のリダイレクトページで、認証に失敗しました。 | 支払い方法を選択し、必要な情報を入力して、支払いを確定します。その後、リダイレクトページで **Fail test payment (テスト支払いを失敗させる)** をクリックします。 | | BLIK | BLIK による支払いはさまざまな理由で失敗します。即時の失敗 (コードの有効期限切れや無効など)、遅延型のエラー (銀行による拒否など)、またはタイムアウト (顧客が時間内に応答しなかったなど) などがあります。 | メールパターンを使用して[さまざまな失敗をシミュレーションします。](https://docs.stripe.com/payments/blik/accept-a-payment.md#simulate-failures) | #### 口座引き落とし | 決済手段 | シナリオ | テスト方法 | | -------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | SEPA ダイレクトデビット | 顧客が SEPA ダイレクトデビットによる支払いに成功しました。 | 口座番号 `AT321904300235473204` を使用して、フォームに入力します。確定された PaymentIntent のステータスはまず、processing に移行し、3 分後に succeeded ステータスに移行します。 | | SEPA ダイレクトデビット | 顧客の Payment Intent のステータスが、`processing` から `requires_payment_method` に移行します。 | 口座番号 `AT861904300235473202` を使用して、フォームに入力します。 | 実装内容をテストするためのその他の情報については、[テスト](https://docs.stripe.com/testing.md)をご覧ください。 ## Optional: 個人の決済手段にSetupFutureUsageを設定する (Preview) [サーバー側] [クライアント側] より細かくするには、[PaymentSheet.IntentConfiguration.Mode.PaymentMethodOptions](https://github.com/stripe/stripe-android/blob/master/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheet.kt#L433) を使用して特定の決済手段に `setupFutureUsage` を設定します。 ```kotlin import com.stripe.android.model.PaymentMethod import com.stripe.android.paymentelement.PaymentMethodOptionsSetupFutureUsagePreview @OptIn(PaymentMethodOptionsSetupFutureUsagePreview::class) @Composable fun MyCheckoutScreen() { val embeddedPaymentElement = rememberEmbeddedPaymentElement( EmbeddedPaymentElement.Builder( createIntentCallback = { confirmationToken -> // Handle ConfirmationToken... } ) ) Column { embeddedPaymentElement.Content( intentConfiguration = PaymentSheet.IntentConfiguration( mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 1099, currency = "usd",paymentMethodOptions = PaymentSheet.IntentConfiguration.Mode.PaymentMethodOptions( setupFutureUsageValues = mapOf( PaymentMethod.Type.Card to SetupFutureUse.OffSession, PaymentMethod.Type.CashAppPay to SetupFutureUse.OnSession ) ) ) ) ) } } ``` 各決済手段でサポートされている [`setupFutureUsage` の値](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-payment_method_options)の詳細をご確認ください。 次に、サーバーが PaymentIntent に `setup_future_usage` または `payment_method_options[X][setup_future_usage]` を設定していないことを確認します。この設定は SDK が `IntentConfiguration` に基づいて自動的に処理します。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', confirmation_token: data['confirmation_token'], # In the latest version of the API, specifying the `automatic_payment_methods` parameter is optional because Stripe enables its functionality by default. automatic_payment_methods: {enabled: true}, } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` ## Optional: 保存済みのカードを有効にする [サーバー側] [クライアント側] `EmbeddedPaymentElement` では、顧客がカードを保存でき、利用可能な決済手段に顧客が保存済みのカードを含めることもできます。顧客は、サーバー上で関連付けられた顧客設定済みの [Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) オブジェクトまたは [Customer](https://docs.stripe.com/api/customers/create.md) オブジェクトを持っている必要があります。顧客がカードを保存できるようにするチェックボックスを有効にするには、[CustomerSession](https://docs.stripe.com/api/customer_sessions.md) を作成し、`payment_method_save` を `enabled` に設定します。 #### Accounts v2 ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. const stripe = require("stripe")("<>"); const express = require('express'); const app = express(); app.set('trust proxy', true); app.use(express.json()); app.post('/payment-sheet', async (req, res) => { // Use an existing Account ID if this is a returning customer. const customer_account = await stripe.v2.core.accounts.create(); const customerSession = await stripe.customerSessions.create({ customer_account: customer_account.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer_account: customer_account.id, }); }); ``` 次に、`EmbeddedPaymentElement` を、顧客の ID と `CustomerSession` の client secret を使用して設定または表示します。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Powdur") .customer( PaymentSheet.CustomerConfiguration.createWithCustomerSession( id = customerAccountId, clientSecret = customerSessionClientSecret, ) ) .build() embeddedPaymentElement.configure( intentConfiguration = // ... , configuration = configuration, ) ``` #### Customers v1 ```javascript // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. const stripe = require("stripe")("<>"); const express = require('express'); const app = express(); app.set('trust proxy', true); app.use(express.json()); app.post('/payment-sheet', async (req, res) => { // Use an existing Customer ID if this is a returning customer. const customer = await stripe.customers.create(); const customerSession = await stripe.customerSessions.create({ customer: customer.id, components: { mobile_payment_element: { enabled: true, features: { payment_method_save: 'enabled', payment_method_redisplay: 'enabled', payment_method_remove: 'enabled' } }, }, }); res.json({ customerSessionClientSecret: customerSession.client_secret, customer: customer.id, }); }); ``` 次に、`EmbeddedPaymentElement` を、顧客の ID と `CustomerSession` の client secret を使用して設定または表示します。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Powdur") .customer( PaymentSheet.CustomerConfiguration.createWithCustomerSession( id = customerId, clientSecret = customerSessionClientSecret, ) ) .build() embeddedPaymentElement.configure( intentConfiguration = // ... , configuration = configuration, ) ``` ## Optional: 遅延型の支払い方法を許可する [クライアント側] *遅延型の決済手段* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods)では、購入の終了時に顧客から売上を受け取ることが保証されません。これは、決済に時間がかかる (アメリカの銀行口座、SEPA デビット、iDEAL、Bancontact、Sofort など) か、完了に顧客の対応を必要とする (OXXO、コンビニ決済、Boleto など) という理由によります。 デフォルトでは、`EmbeddedPaymentElement` は、遅延型の決済手段を受け入れている場合でも表示しません。有効にした遅延型の決済手段を表示するには、`EmbeddedPaymentElement.Configuration` で `allowsDelayedPaymentMethods` を true に設定します。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Powdur") .allowsDelayedPaymentMethods(true) .build() ``` 顧客が `EmbeddedPaymentElement` で遅延型の決済手段を適切に使用すると、`EmbeddedPaymentElement.Result.Completed` の支払い結果が返されます。 ## Optional: Google Pay を有効にする > 決済画面に専用の **Google Pay** ボタンがある場合は、[Google Pay ガイド](https://docs.stripe.com/google-pay.md?platform=android)の内容に従ってください。Embedded Payment Element を使用して、他のタイプの決済手段を処理することも可能です。 ### 実装方法を設定する Google Pay を使用するには、まず以下を **AndroidManifest.xml** の `` に追加し、Google Pay API を有効化します。 ```xml ... ``` 詳細は、Google Pay の Android 向け [Google Pay API を設定する](https://developers.google.com/pay/api/android/guides/setup) を参照してください。 ### Google Pay を追加する 構築済みのシステムに Google Pay を追加するには、`EmbeddedPaymentElement.Configuration` を初期化する際に、ご利用の Google Pay 環境 (本番またはテスト) と[ビジネスの国コード](https://dashboard.stripe.com/settings/account)を指定して、[PaymentSheet.GooglePayConfiguration](https://stripe.dev/stripe-android/paymentsheet/com.stripe.android.paymentsheet/-payment-sheet/-google-pay-configuration/index.html) を渡します。 ```kotlin val googlePayConfiguration = PaymentSheet.GooglePayConfiguration( environment = PaymentSheet.GooglePayConfiguration.Environment.Test, countryCode = "US", currencyCode = "USD" // Required for Setup Intents, optional for Payment Intents ) val configuration = EmbeddedPaymentElement.Configuration.Builder("Merchant, Inc.") .googlePay(googlePayConfiguration) .build() ``` ### Google Pay をテストする Google では、[テストクレジットカードスイート](https://developers.google.com/pay/api/android/guides/resources/test-card-suite) を使用してテスト決済を行うことができます。Stripe [テストカード](https://docs.stripe.com/testing.md) をテストスイートと併用できます。 Google Pay がサポートされている国では、シミュレーションされたデバイスではなく、物理的な Android デバイスを使用して Google Pay をテストする必要があります。Google ウォレットに保存した実際のクレジットカードを使用して、テスト用デバイスの Google アカウントにログインします。 ## Optional: カードのスキャンを有効にする カードスキャンサポートを有効にするには、[Google Pay & Wallet Console](https://pay.google.com/business/console?utm_source=devsite&utm_medium=devsite&utm_campaign=devsite) から Google Pay API への[本番アクセスをリクエスト](https://developers.google.com/pay/api/android/guides/test-and-deploy/request-prod-access)します。 - Google Pay を有効にしている場合、対象のデバイスの UI でカードスキャン機能が自動的に利用可能になります。対象のデバイスの詳細については、[Google Pay API の制約](https://developers.google.com/pay/payment-card-recognition/debit-credit-card-recognition)をご覧ください。 - **重要:** カードスキャン機能は、[Google Pay & Wallet Console](https://pay.google.com/business/console) に登録された同じ署名キーで署名されたビルドにのみ表示されます。異なる署名キーを使用したテストまたはデバッグビルド (Firebase App Tester を通じて配布されたビルドなど) では、 **カードをスキャン** オプションは表示されません。プレリリースビルドでカードスキャンをテストするには、以下のいずれかを行う必要があります。 - 本番環境の署名キーを使用してテストビルドに署名する - Google Pay & Wallet Console にテスト署名キーのフィンガープリントを追加する お客様のアプリが Google Pay に対応していない場合は、Stripe カードスキャナーを使用できます。 > Stripe カードスキャナーは公開プレビュー版です。 カードスキャンに対応する機能を有効にするには、`stripecardscan` を `dependencies` ブロック (お客様の [app/build.gradle](https://developer.android.com/studio/build/dependencies) ファイル内) に追加します: #### Groovy ```groovy implementation 'com.stripe:stripecardscan:23.8.0' ``` ## Optional: 画面をカスタマイズする カスタマイズはすべて `EmbeddedPaymentElement.Configuration` オブジェクトで設定されます。 ### デザイン [Appearance API](https://docs.stripe.com/elements/appearance-api/embedded-mobile.md?platform=android) を使用して、アプリのデザインに合うように色やフォントなどをカスタマイズします。 ### 顧客の住所を収集 [Address Element](https://docs.stripe.com/elements/address-element.md?platform=android) を使用して、顧客から配送先住所と請求先住所を収集します。 ### 表示するビジネス名をカスタマイズする `merchantDisplayName` を設定し、顧客に表示するビジネス名を指定します。名前を指定しない場合、既定値はアプリ名になります。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .build() ``` ### デフォルトの請求先情報を指定する `EmbeddedPaymentElement` で収集される請求先情報のデフォルト値を設定するには、`defaultBillingDetails` プロパティを設定します。`EmbeddedPaymentElement` の各フィールドに、指定した値が事前に読み込まれます。 ```kotlin val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .defaultBillingDetails( PaymentSheet.BillingDetails( address = PaymentSheet.Address( country = "US", ), email = "foo@bar.com" ) ) .build() ``` ### 請求先情報の収集を設定する `BillingDetailsCollectionConfiguration` を使用して、`EmbeddedPaymentElement` で請求詳細を収集する方法を指定します。 顧客の名前、メールアドレス、電話番号、住所を収集できます。 UI でデフォルトの請求詳細が収集されない場合でも、それらの詳細を PaymentMethod オブジェクトに関連付けるには、`billingDetailsCollectionConfiguration.attachDefaultsToPaymentMethod` を `true` に設定します。 ```kotlin import com.stripe.android.paymentsheet.PaymentSheet.BillingDetailsCollectionConfiguration val billingDetails = PaymentSheet.BillingDetails( email = "foo@bar.com" ) val billingDetailsCollectionConfiguration = BillingDetailsCollectionConfiguration( attachDefaultsToPaymentMethod = true, name = BillingDetailsCollectionConfiguration.CollectionMode.Always, email = BillingDetailsCollectionConfiguration.CollectionMode.Never, address = BillingDetailsCollectionConfiguration.AddressCollectionMode.Full, ) val configuration = EmbeddedPaymentElement.Configuration.Builder(merchantDisplayName = "Merchant, Inc.") .defaultBillingDetails(billingDetails) .billingDetailsCollectionConfiguration(billingDetailsCollectionConfiguration) .build() ``` > 情報の収集に適用される法律については、弁護士に相談してください。電話番号は、取引に必要な場合にのみ収集してください。 ## Optional: 確定時のセキュリティコードの再収集を有効にする PaymentIntent の確定時に保存されたカードのセキュリティコードを再徴収するには、統合で PaymentIntent を作成する前に決済詳細を徴収する必要があります。 ### インテントの設定を更新する `PaymentSheet.IntentConfiguration` では、保存されたカードのセキュリティコードを再収集する時期を管理するオプションパラメーターを使用できます。 ```kotlin val intentConfig = PaymentSheet.IntentConfiguration( mode = PaymentSheet.IntentConfiguration.Mode.Payment( amount = 1099, currency = "usd", ),requireCvcRecollection = true, ) ``` ### インテント作成のパラメーターを更新する 支払いの確定時にセキュリティコードを再収集するには、PaymentIntent の作成時に `customerId` パラメーターと `require_cvc_recollection` パラメーターの両方を含めます。 #### Ruby ```ruby require 'stripe' # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. # Find your keys at https://dashboard.stripe.com/apikeys. client = Stripe::StripeClient.new('<>') post '/create-intent' do data = JSON.parse request.body.read params = { amount: 1099, currency: 'usd', automatic_payment_methods: {enabled: true},customer: customer.id, payment_method_options: { card: {require_cvc_recollection: true} }, } begin intent = client.v1.payment_intents.create(params) {client_secret: intent.client_secret}.to_json rescue Stripe::StripeError => e {error: e.error.message}.to_json end end ``` # 決済を受け付ける > This is a 決済を受け付ける for when platform is react-native and type is payment. View the full page at https://docs.stripe.com/payments/mobile/accept-payment-embedded?platform=react-native&type=payment. Payment Element を使用すると、1 つの導入を使用して複数の決済手段を受け付けることができます。この導入では、Payment Element をレンダリングし、*PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成して、アプリで決済を確定するカスタム決済フローを構築します。 ## Stripe を設定する [サーバー側] [クライアント側] ### サーバー側 この接続方法では、Stripe API と通信するエンドポイントがサーバー上に必要です。サーバーから Stripe API にアクセスするには、Stripe の公式ライブラリーを使用します。 #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### クライアント側 [React Native SDK](https://github.com/stripe/stripe-react-native) はオープンソースであり、詳細なドキュメントが提供されています。内部では、[ネイティブの iOS](https://github.com/stripe/stripe-ios) および [Android](https://github.com/stripe/stripe-android) の SDK を使用します。Stripe の React Native SDK をインストールするには、プロジェクトのディレクトリーで (使用するパッケージマネージャーによって異なる) 次のいずれかのコマンドを実行します。 #### yarn ```bash yarn add @stripe/stripe-react-native ``` #### npm ```bash npm install @stripe/stripe-react-native ``` 次に、その他の必要な依存関係をインストールします。 - iOS の場合は、**ios** ディレクトリに移動して `pod install` を実行し、必要なネイティブ依存関係もインストールします。 - Android の場合は、依存関係をインストールする必要はありません。 > [公式の TypeScript ガイド](https://reactnative.dev/docs/typescript#adding-typescript-to-an-existing-project)に従って TypeScript のサポートを追加することをお勧めします。 ### Stripe の初期化 React Native アプリで Stripe を初期化するには、決済画面を `StripeProvider` コンポーネントでラップするか、`initStripe` 初期化メソッドを使用します。`publishableKey` の API [公開可能キー](https://docs.stripe.com/keys.md#obtain-api-keys)のみが必要です。次の例は、`StripeProvider` コンポーネントを使用して Stripe を初期化する方法を示しています。 ```jsx import { useState, useEffect } from 'react'; import { StripeProvider } from '@stripe/stripe-react-native'; function App() { const [publishableKey, setPublishableKey] = useState(''); const fetchPublishableKey = async () => { const key = await fetchKey(); // fetch key from your server here setPublishableKey(key); }; useEffect(() => { fetchPublishableKey(); }, []); return ( {/* Your app code here */} ); } ``` > テストおよび開発時には API の[テストキー](https://docs.stripe.com/keys.md#obtain-api-keys)を使用し、アプリの公開時には[本番環境](https://docs.stripe.com/keys.md#test-live-modes)キーを使用します。 ## 支払い方法を有効にする [支払い方法の設定](https://dashboard.stripe.com/settings/payment_methods)を表示して、サポートする支払い方法を有効にします。*PaymentIntent* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) を作成するには、少なくとも 1 つは支払い方法を有効にする必要があります。 多くの顧客から決済を受け付けられるよう、Stripe では、カードやその他一般的な決済手段がデフォルトで有効になっていますが、ビジネスや顧客に適した追加の決済手段を有効にすることをお勧めします。プロダクトと決済手段のサポートについては[決済手段のサポート](https://docs.stripe.com/payments/payment-methods/payment-method-support.md)を、手数料については[料金体系ページ](https://stripe.com/pricing/local-payment-methods)をご覧ください。 ## 支払いの詳細を収集する [クライアント側] ### Embedded Payment Element を初期化する `useEmbeddedPaymentElement` フックを使用して、React Native アプリに Embedded Payment Element を作成・表示します。このフックには、次の 2 つの設定オブジェクトが必要です。 - `EmbeddedPaymentElementConfiguration`:一般的な設定 (例: `returnURL`)。 - `IntentConfiguration`: 決済固有の詳細 (金額、通貨、`confirmationTokenConfirmHandler` コールバックなど)。 フックは、`embeddedPaymentElementView` React コンポーネントとその他のヘルパーメソッドを含むオブジェクトを返します。オプションと戻り値の一覧については、[Stripe React Native SDK のドキュメント](https://stripe.dev/stripe-react-native/api-reference/interfaces/UseEmbeddedPaymentElementResult.html)をご覧ください。 > `confirmationTokenConfirmHandler` の実装は必須ですが、このステップでは空の関数のままにして、後で実装できます。 ```jsx import { useState, useCallback, useEffect } from 'react'; import { useEmbeddedPaymentElement, IntentConfiguration, EmbeddedPaymentElementConfiguration, IntentCreationCallbackParams, } from '@stripe/stripe-react-native'; function MyCheckoutComponent() { const [intentConfig, setIntentConfig] = useState(null); const [elementConfig, setElementConfig] = useState(null); const initialize = useCallback(() => { const newIntentConfig: IntentConfiguration = { mode: { amount: 1099, currencyCode: 'USD', }, confirmationTokenConfirmHandler: async ( confirmationToken, callback: (params: IntentCreationCallbackParams) => void ) => { // You'll implement this in the "Confirm the payment" section below }, }; const newElementConfig: EmbeddedPaymentElementConfiguration = { merchantDisplayName: 'Your Business Name', returnURL: 'your-app://stripe-redirect', }; setIntentConfig(newIntentConfig); setElementConfig(newElementConfig); }, []); const { embeddedPaymentElementView, paymentOption, confirm, update, clearPaymentOption, loadingError, isLoaded, } = useEmbeddedPaymentElement( intentConfig!, elementConfig! ); useEffect(() => { initialize(); }, [initialize]); } ``` ### Embedded Payment Element ビューを追加する `useEmbeddedPaymentElement` フックが正常に初期化されたら、`embeddedPaymentElementView` をコンポーネントに含めて、決済 UI に Embedded Payment Element を表示します。 > `isLoaded` が `true` にならない場合は、コンポーネントのレンダーツリーに `embeddedPaymentElementView` が常に残っていることを確認してください。Android では、初期化を完了するためにネイティブビューをマウントする必要があります。以下の例に示すように、ビューを条件付きでレンダリングするのではなく、不透明度を使用して可視性を制御してください。 ```jsx import { View, Text, ActivityIndicator } from 'react-native'; function MyCheckoutComponent() { // Other component code remains the same return ( {/* Handle loading errors through the loadingError property */} {loadingError && ( Failed to load payment form: {loadingError.message || String(loadingError)} )} {/* Keep the view in the render tree for Android, control visibility with opacity */} {embeddedPaymentElementView} {/* Show loading indicator while the view is loading */} {!loadingError && !isLoaded && ( )} ); } ``` ここで、アプリを実行して、Embedded Mobile Payment Element を確認できます。 ### (任意)選択した支払いオプションを表示する `useEmbeddedPaymentElement` フックは、返されるオブジェクトで `paymentOption` プロパティを提供します。これを使用して、ラベル (「····4242」など)、画像 (VISA ロゴなど)、UI に表示する請求先情報など、顧客が選択した決済オプションの詳細にアクセスできます。 `paymentOption` プロパティはリアクティブ型であり、選択された決済オプションが変更されると自動的に更新されます。デリゲートメソッドを別途実装する必要はありません。代わりに、このプロパティをコンポーネントで直接使用できます。これにより、`paymentOption` が変更されるたびに React によってコンポーネントが再度レンダリングされます。 ```jsx import { View, Text, Image } from 'react-native'; function MyCheckoutComponent() { // Other component code remains the same return ( // Other component code remains the same // Display the currently selected payment option (label and image) {paymentOption?.image && ( )} Selected: {paymentOption?.label ?? 'None'} ); } ``` ### (任意)支払いの詳細を更新する 顧客が決済情報を変更する操作 (割引コードの適用など) を実行したときに、update メソッドを呼び出して `EmbeddedPaymentElement` インスタンスを更新し、新しい値を反映します。Apple Pay や Google Pay など、一部の決済手段では UI に金額が表示されるため、常に正確で最新の状態を維持するようにしてください。 更新コールが完了したら、UI を更新します。更新コールにより、顧客が現在選択している決済オプションが変更される場合もあります。 > `update` APIを呼び出すときは、必ず `await` を使用して、読み込み状態が適切に順序付けされていることを確認します。`await` を使用しないと、更新が完了する前に読み込みインジケーターが非表示になることがあります。 ```jsx import { useState, useCallback } from 'react'; import { View, Button, ActivityIndicator } from 'react-native'; function MyCheckoutComponent() { // Other component code remains the same const [isUpdating, setIsUpdating] = useState(false); const handleUpdate = useCallback(async () => { // Create a new IntentConfiguration object with updated values const updatedIntentConfig: IntentConfiguration = { ...intentConfig!, mode: { amount: 999, // Updated amount after applying discount code currencyCode: 'USD', }, }; setIsUpdating(true); try { await update(updatedIntentConfig); } catch (error) { // Handle any unexpected errors console.error('Unexpected error during update:', error); } finally { setIsUpdating(false); } }, [intentConfig, update]); // Example of how to use the handleUpdate function const applyDiscountCode = useCallback(async (discountCode: string) => { // Validate discount code with your server try { const response = await fetch('https://your-server.com/apply-discount', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ discountCode }), }); if (response.ok) { // Update the intent configuration with the new amount await handleUpdate(); } } catch (error) { console.error('Failed to apply discount:', error); } }, [handleUpdate]); return ( {/* Hide view and show loading indicator during update */} {embeddedPaymentElementView} {isUpdating && ( )}