From 7987b243703ce1112d1010c10e18c27da0badf22 Mon Sep 17 00:00:00 2001 From: Tyler Clawson Date: Thu, 3 Oct 2024 18:31:21 -0400 Subject: [PATCH] Add check to make sure both customerEphemeralKey and customerSessionClientSecret are not set --- .../PaymentSheetFragment.kt | 44 +++++++++++++------ ios/StripeSdk+PaymentSheet.swift | 11 +++-- src/types/PaymentSheet.ts | 17 +++++-- 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt b/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt index f04426f52..15b4ea591 100644 --- a/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +++ b/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt @@ -55,7 +55,6 @@ class PaymentSheetFragment( } } - @OptIn(ExperimentalCustomerSessionApi::class) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val merchantDisplayName = arguments?.getString("merchantDisplayName").orEmpty() @@ -64,9 +63,6 @@ class PaymentSheetFragment( return } val primaryButtonLabel = arguments?.getString("primaryButtonLabel") - val customerId = arguments?.getString("customerId").orEmpty() - val customerEphemeralKeySecret = arguments?.getString("customerEphemeralKeySecret").orEmpty() - val customerSessionClientSecret = arguments?.getString("customerSessionClientSecret").orEmpty() val googlePayConfig = buildGooglePayConfig(arguments?.getBundle("googlePay")) val allowsDelayedPaymentMethods = arguments?.getBoolean("allowsDelayedPaymentMethods") val billingDetailsBundle = arguments?.getBundle("defaultBillingDetails") @@ -88,6 +84,13 @@ class PaymentSheetFragment( return } + val customerConfiguration = try { + buildCustomerConfiguration(arguments) + } catch (error: PaymentSheetException) { + initPromise.resolve(createError(ErrorType.Failed.toString(), error)) + return + } + val shippingDetails = arguments?.getBundle("defaultShippingDetails")?.let { AddressSheetView.buildAddressDetails(it) } @@ -192,16 +195,7 @@ class PaymentSheetFragment( val configurationBuilder = PaymentSheet.Configuration.Builder(merchantDisplayName) .allowsDelayedPaymentMethods(allowsDelayedPaymentMethods ?: false) .defaultBillingDetails(defaultBillingDetails) - .customer( - if (customerId.isNotEmpty() && customerSessionClientSecret.isNotEmpty()) PaymentSheet.CustomerConfiguration.createWithCustomerSession( - id = customerId, - clientSecret = customerSessionClientSecret - ) - else if (customerId.isNotEmpty() && customerEphemeralKeySecret.isNotEmpty()) PaymentSheet.CustomerConfiguration( - id = customerId, - ephemeralKeySecret = customerEphemeralKeySecret - ) else null - ) + .customer(customerConfiguration) .googlePay(googlePayConfig) .appearance(appearance) .shippingDetails(shippingDetails) @@ -434,6 +428,28 @@ class PaymentSheetFragment( ) } } + + @OptIn(ExperimentalCustomerSessionApi::class) + @Throws(PaymentSheetException::class) + private fun buildCustomerConfiguration(bundle: Bundle?): PaymentSheet.CustomerConfiguration? { + val customerId = bundle?.getString("customerId").orEmpty() + val customerEphemeralKeySecret = bundle?.getString("customerEphemeralKeySecret").orEmpty() + val customerSessionClientSecret = bundle?.getString("customerSessionClientSecret").orEmpty() + return if (customerSessionClientSecret.isNotEmpty() && customerEphemeralKeySecret.isNotEmpty()) { + throw PaymentSheetException("`customerEphemeralKeySecret` and `customerSessionClientSecret` cannot both be set") + } else if (customerId.isNotEmpty() && customerSessionClientSecret.isNotEmpty()) { + PaymentSheet.CustomerConfiguration.createWithCustomerSession( + id = customerId, + clientSecret = customerSessionClientSecret + ) + } + else if (customerId.isNotEmpty() && customerEphemeralKeySecret.isNotEmpty()) { + PaymentSheet.CustomerConfiguration( + id = customerId, + ephemeralKeySecret = customerEphemeralKeySecret + ) + } else null + } } } diff --git a/ios/StripeSdk+PaymentSheet.swift b/ios/StripeSdk+PaymentSheet.swift index b1b8d33a8..99ba2b784 100644 --- a/ios/StripeSdk+PaymentSheet.swift +++ b/ios/StripeSdk+PaymentSheet.swift @@ -91,14 +91,17 @@ extension StripeSdk { } if let customerId = params["customerId"] as? String { - if let customerEphemeralKeySecret = params["customerEphemeralKeySecret"] as? String { + var customerEphemeralKeySecret = params["customerEphemeralKeySecret"] as? String + var customerClientSecret = params["customerSessionClientSecret"] as? String + if let customerEphemeralKeySecret, let customerClientSecret { + return(error: Errors.createError(ErrorType.Failed, "`customerEphemeralKeySecret` and `customerSessionClientSecret cannot both be set"), configuration: nil) + } else if let customerEphemeralKeySecret { if (!Errors.isEKClientSecretValid(clientSecret: customerEphemeralKeySecret)) { return(error: Errors.createError(ErrorType.Failed, "`customerEphemeralKeySecret` format does not match expected client secret formatting."), configuration: nil) } configuration.customer = .init(id: customerId, ephemeralKeySecret: customerEphemeralKeySecret) - } - else if let customerClientSecret = params["customerSessionClientSecret"] as? String { - configuration.customer = .init(id: customerId, customerSessionClientSecret: customerClientSecret) + } else if let customerClientSecret { + configuration.customer = .init(id: customerId, customerSessionClientSecret: customerClientSecret) } } diff --git a/src/types/PaymentSheet.ts b/src/types/PaymentSheet.ts index 0a4dadfc5..b13560433 100644 --- a/src/types/PaymentSheet.ts +++ b/src/types/PaymentSheet.ts @@ -10,13 +10,11 @@ import type { FutureUsage } from './PaymentIntent'; import type { Result } from './PaymentMethod'; import type { StripeError } from './Errors'; -export type SetupParams = IntentParams & { +type SetupParamsBase = IntentParams & { /** Your customer-facing business name. On Android, this is required and cannot be an empty string. */ merchantDisplayName: string; /** The identifier of the Stripe Customer object. See https://stripe.com/docs/api/customers/object#customer_object-id */ customerId?: string; - /** A short-lived token that allows the SDK to access a Customer’s payment methods. */ - customerEphemeralKeySecret?: string; /** When set to true, separates out the payment method selection & confirmation steps. * If true, you must call `confirmPaymentSheetPayment` on your own. Defaults to false. */ customFlow?: boolean; @@ -69,6 +67,19 @@ export type SetupParams = IntentParams & { allowsRemovalOfLastSavedPaymentMethod?: boolean; }; +export type SetupParams = + | (SetupParamsBase & { + /** A short-lived token that allows the SDK to access a Customer’s payment methods. */ + customerEphemeralKeySecret: string; + customerSessionClientSecret?: never; + }) + | (SetupParamsBase & { + customerEphemeralKeySecret?: never; + /** The client secret of this Customer Session. Used on the client to set up secure access to the given customer. */ + customerSessionClientSecret: string; + }) + | SetupParamsBase; + export type IntentParams = | { paymentIntentClientSecret: string;