From 21b03008d017f4ab3a15ee76971dd0b578c2e286 Mon Sep 17 00:00:00 2001 From: Jonas Bark Date: Thu, 31 Oct 2024 12:16:21 +0100 Subject: [PATCH] Sync with Stripe React Native 0.39.0 --- packages/stripe_android/android/build.gradle | 2 +- .../com/reactnativestripesdk/CardFieldView.kt | 4 ++ .../CardFieldViewManager.kt | 5 ++ .../GooglePayButtonManager.kt | 2 +- .../PaymentSheetFragment.kt | 38 ++++++++++--- .../reactnativestripesdk/StripeSdkModule.kt | 4 +- .../customersheet/CustomerSheetFragment.kt | 6 +- .../ReactNativeCustomerAdapter.kt | 2 - .../AddToWalletButtonManager.kt | 2 +- .../Classes/Stripe Sdk/CardFieldView.swift | 56 ++++++++++--------- .../Stripe Sdk/StripeSdk+PaymentSheet.swift | 10 +++- packages/stripe_ios/ios/stripe_ios.podspec | 2 +- 12 files changed, 87 insertions(+), 46 deletions(-) diff --git a/packages/stripe_android/android/build.gradle b/packages/stripe_android/android/build.gradle index cb87ff3a6..e2bc113aa 100644 --- a/packages/stripe_android/android/build.gradle +++ b/packages/stripe_android/android/build.gradle @@ -3,7 +3,7 @@ version '1.0-SNAPSHOT' buildscript { ext.kotlin_version = '1.8.0' - ext.stripe_version = '20.48.+' + ext.stripe_version = '20.52.+' repositories { google() diff --git a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldView.kt b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldView.kt index fb782ee65..505fe05fe 100644 --- a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldView.kt +++ b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldView.kt @@ -220,6 +220,10 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) { mCardWidget.setPreferredNetworks(mapToPreferredNetworks(preferredNetworks)) } + fun setOnBehalfOf(onBehalfOf: String?) { + mCardWidget.onBehalfOf = onBehalfOf + } + /** * We can reliable assume that setPostalCodeEnabled is called before * setCountryCode because of the order of the props in CardField.tsx diff --git a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldViewManager.kt b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldViewManager.kt index 6f870f52e..ee1451fbc 100644 --- a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldViewManager.kt +++ b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldViewManager.kt @@ -54,6 +54,11 @@ class CardFieldViewManager : SimpleViewManager() { view.setCountryCode(countryCode) } + @ReactProp(name = "onBehalfOf") + fun setOnBehalfOf(view: CardFieldView, onBehalfOf: String?) { + view.setOnBehalfOf(onBehalfOf) + } + @ReactProp(name = "placeholders") fun setPlaceHolders(view: CardFieldView, placeholders: ReadableMap) { view.setPlaceHolders(placeholders) diff --git a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayButtonManager.kt b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayButtonManager.kt index 39dc32f85..db558c4a2 100644 --- a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayButtonManager.kt +++ b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayButtonManager.kt @@ -4,7 +4,7 @@ import com.facebook.react.uimanager.SimpleViewManager import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.annotations.ReactProp -class GooglePayButtonManager : SimpleViewManager() { +class GooglePayButtonManager : SimpleViewManager() { override fun getName(): String { return REACT_CLASS } diff --git a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentSheetFragment.kt b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentSheetFragment.kt index a49ff6e45..15b4ea591 100644 --- a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentSheetFragment.kt +++ b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentSheetFragment.kt @@ -63,8 +63,6 @@ class PaymentSheetFragment( return } val primaryButtonLabel = arguments?.getString("primaryButtonLabel") - val customerId = arguments?.getString("customerId").orEmpty() - val customerEphemeralKeySecret = arguments?.getString("customerEphemeralKeySecret").orEmpty() val googlePayConfig = buildGooglePayConfig(arguments?.getBundle("googlePay")) val allowsDelayedPaymentMethods = arguments?.getBoolean("allowsDelayedPaymentMethods") val billingDetailsBundle = arguments?.getBundle("defaultBillingDetails") @@ -86,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) } @@ -190,12 +195,7 @@ class PaymentSheetFragment( val configurationBuilder = PaymentSheet.Configuration.Builder(merchantDisplayName) .allowsDelayedPaymentMethods(allowsDelayedPaymentMethods ?: false) .defaultBillingDetails(defaultBillingDetails) - .customer( - if (customerId.isNotEmpty() && customerEphemeralKeySecret.isNotEmpty()) PaymentSheet.CustomerConfiguration( - id = customerId, - ephemeralKeySecret = customerEphemeralKeySecret - ) else null - ) + .customer(customerConfiguration) .googlePay(googlePayConfig) .appearance(appearance) .shippingDetails(shippingDetails) @@ -428,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/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeSdkModule.kt b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeSdkModule.kt index f11aeeb4a..97f51af70 100644 --- a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeSdkModule.kt +++ b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeSdkModule.kt @@ -883,7 +883,7 @@ class StripeSdkModule(val reactContext: ReactApplicationContext) : ReactContextB @ReactMethod fun customerAdapterAttachPaymentMethodCallback(paymentMethodJson: ReadableMap, promise: Promise) { customerSheetFragment?.let { - val paymentMethod = PaymentMethod.fromJson(JSONObject(paymentMethodJson.toHashMap())) + val paymentMethod = PaymentMethod.fromJson(JSONObject(paymentMethodJson.toHashMap() as HashMap<*, *>)) if (paymentMethod == null) { Log.e("StripeReactNative", "There was an error converting Payment Method JSON to a Stripe Payment Method") return @@ -898,7 +898,7 @@ class StripeSdkModule(val reactContext: ReactApplicationContext) : ReactContextB @ReactMethod fun customerAdapterDetachPaymentMethodCallback(paymentMethodJson: ReadableMap, promise: Promise) { customerSheetFragment?.let { - val paymentMethod = PaymentMethod.fromJson(JSONObject(paymentMethodJson.toHashMap())) + val paymentMethod = PaymentMethod.fromJson(JSONObject(paymentMethodJson.toHashMap() as HashMap<*, *>)) if (paymentMethod == null) { Log.e("StripeReactNative", "There was an error converting Payment Method JSON to a Stripe Payment Method") return diff --git a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/CustomerSheetFragment.kt b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/CustomerSheetFragment.kt index 0e1fdf743..9fe2b4241 100644 --- a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/CustomerSheetFragment.kt +++ b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/CustomerSheetFragment.kt @@ -20,7 +20,6 @@ import com.stripe.android.customersheet.CustomerAdapter import com.stripe.android.customersheet.CustomerEphemeralKey import com.stripe.android.customersheet.CustomerSheet import com.stripe.android.customersheet.CustomerSheetResult -import com.stripe.android.customersheet.ExperimentalCustomerSheetApi import com.stripe.android.customersheet.PaymentOptionSelection import com.stripe.android.model.PaymentMethod import com.stripe.android.paymentsheet.* @@ -29,7 +28,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -@OptIn(ExperimentalCustomerSheetApi::class, ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi::class) +@OptIn(ExperimentalAllowsRemovalOfLastSavedPaymentMethodApi::class) class CustomerSheetFragment : Fragment() { private var customerSheet: CustomerSheet? = null internal var customerAdapter: ReactNativeCustomerAdapter? = null @@ -111,11 +110,12 @@ class CustomerSheetFragment : Fragment() { customerSheet = CustomerSheet.create( fragment = this, - configuration = configuration.build(), customerAdapter = customerAdapter, callback = ::handleResult ) + customerSheet?.configure(configuration.build()) + initPromise.resolve(WritableNativeMap()) } diff --git a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/ReactNativeCustomerAdapter.kt b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/ReactNativeCustomerAdapter.kt index 46260b6be..0ae53d18b 100644 --- a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/ReactNativeCustomerAdapter.kt +++ b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/ReactNativeCustomerAdapter.kt @@ -7,11 +7,9 @@ import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.WritableMap import com.reactnativestripesdk.StripeSdkModule import com.stripe.android.customersheet.CustomerAdapter -import com.stripe.android.customersheet.ExperimentalCustomerSheetApi import com.stripe.android.model.PaymentMethod import kotlinx.coroutines.CompletableDeferred -@OptIn(ExperimentalCustomerSheetApi::class) class ReactNativeCustomerAdapter ( private val context: ReactApplicationContext, private val adapter: CustomerAdapter, diff --git a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonManager.kt b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonManager.kt index e932b27fe..22df1d5ac 100644 --- a/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonManager.kt +++ b/packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonManager.kt @@ -9,7 +9,7 @@ import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.annotations.ReactProp -class AddToWalletButtonManager(applicationContext: Context) : SimpleViewManager() { +class AddToWalletButtonManager(applicationContext: Context) : SimpleViewManager() { private val requestManager = Glide.with(applicationContext) override fun getName() = "AddToWalletButton" diff --git a/packages/stripe_ios/ios/Classes/Stripe Sdk/CardFieldView.swift b/packages/stripe_ios/ios/Classes/Stripe Sdk/CardFieldView.swift index ebcb0677e..200fbba94 100644 --- a/packages/stripe_ios/ios/Classes/Stripe Sdk/CardFieldView.swift +++ b/packages/stripe_ios/ios/Classes/Stripe Sdk/CardFieldView.swift @@ -17,19 +17,25 @@ class CardFieldView: UIView, STPPaymentCardTextFieldDelegate { cardField.isUserInteractionEnabled = !disabled } } - + @objc var postalCodeEnabled: Bool = true { didSet { cardField.postalCodeEntryEnabled = postalCodeEnabled } } - + @objc var countryCode: String? { didSet { cardField.countryCode = countryCode } } - + + @objc var onBehalfOf: String? { + didSet { + cardField.onBehalfOf = onBehalfOf + } + } + @objc var preferredNetworks: Array? { didSet { if let preferredNetworks = preferredNetworks { @@ -37,7 +43,7 @@ class CardFieldView: UIView, STPPaymentCardTextFieldDelegate { } } } - + @objc var placeholders: NSDictionary = NSDictionary() { didSet { if let numberPlaceholder = placeholders["number"] as? String { @@ -56,7 +62,7 @@ class CardFieldView: UIView, STPPaymentCardTextFieldDelegate { } } } - + @objc var autofocus: Bool = false { didSet { if autofocus == true { @@ -64,7 +70,7 @@ class CardFieldView: UIView, STPPaymentCardTextFieldDelegate { } } } - + @objc var cardStyle: NSDictionary = NSDictionary() { didSet { if let borderWidth = cardStyle["borderWidth"] as? Int { @@ -91,7 +97,7 @@ class CardFieldView: UIView, STPPaymentCardTextFieldDelegate { cardField.textErrorColor = UIColor(hexString: textErrorColor) } let fontSize = cardStyle["fontSize"] as? Int ?? 14 - + if let fontFamily = cardStyle["fontFamily"] as? String { cardField.font = UIFont(name: fontFamily, size: CGFloat(fontSize)) ?? UIFont.systemFont(ofSize: CGFloat(fontSize)) } else { @@ -104,46 +110,46 @@ class CardFieldView: UIView, STPPaymentCardTextFieldDelegate { } } } - + override init(frame: CGRect) { super.init(frame: frame) cardField.delegate = self - + self.addSubview(cardField) } - + func focus() { cardField.becomeFirstResponder() } - + func blur() { cardField.resignFirstResponder() } - + func clear() { cardField.clear() } - + func paymentCardTextFieldDidEndEditing(_ textField: STPPaymentCardTextField) { onFocusChange?(["focusedField": NSNull()]) } - + func paymentCardTextFieldDidBeginEditingNumber(_ textField: STPPaymentCardTextField) { onFocusChange?(["focusedField": "CardNumber"]) } - + func paymentCardTextFieldDidBeginEditingCVC(_ textField: STPPaymentCardTextField) { onFocusChange?(["focusedField": "Cvc"]) } - + func paymentCardTextFieldDidBeginEditingExpiration(_ textField: STPPaymentCardTextField) { onFocusChange?(["focusedField": "ExpiryDate"]) } - + func paymentCardTextFieldDidBeginEditingPostalCode(_ textField: STPPaymentCardTextField) { onFocusChange?(["focusedField": "PostalCode"]) } - + func paymentCardTextFieldDidChange(_ textField: STPPaymentCardTextField) { if onCardChange != nil { let brand = STPCardValidator.brand(forNumber: textField.cardNumber ?? "") @@ -180,29 +186,29 @@ class CardFieldView: UIView, STPPaymentCardTextFieldDelegate { self.cardPostalCode = nil } } - + override func layoutSubviews() { cardField.frame = self.bounds } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) { // } - + func paymentContextDidChange(_ paymentContext: STPPaymentContext) { // } - + func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPPaymentStatusBlock) { // } - + func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) { // } - + } diff --git a/packages/stripe_ios/ios/Classes/Stripe Sdk/StripeSdk+PaymentSheet.swift b/packages/stripe_ios/ios/Classes/Stripe Sdk/StripeSdk+PaymentSheet.swift index 497b330f7..99ba2b784 100644 --- a/packages/stripe_ios/ios/Classes/Stripe Sdk/StripeSdk+PaymentSheet.swift +++ b/packages/stripe_ios/ios/Classes/Stripe Sdk/StripeSdk+PaymentSheet.swift @@ -6,7 +6,7 @@ // import Foundation -@_spi(ExperimentalAllowsRemovalOfLastSavedPaymentMethodAPI) @_spi(STP) import StripePaymentSheet +@_spi(ExperimentalAllowsRemovalOfLastSavedPaymentMethodAPI) @_spi(CustomerSessionBetaAccess) @_spi(STP) import StripePaymentSheet extension StripeSdk { internal func buildPaymentSheetConfiguration( @@ -91,11 +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 { + configuration.customer = .init(id: customerId, customerSessionClientSecret: customerClientSecret) } } diff --git a/packages/stripe_ios/ios/stripe_ios.podspec b/packages/stripe_ios/ios/stripe_ios.podspec index e4a0eb707..167db4270 100644 --- a/packages/stripe_ios/ios/stripe_ios.podspec +++ b/packages/stripe_ios/ios/stripe_ios.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. # Run `pod lib lint stripe_ios.podspec' to validate before publishing. # -stripe_version = '~> 23.28.0' +stripe_version = '~> 23.30.0' Pod::Spec.new do |s| s.name = 'stripe_ios' s.version = '0.0.1'