From 361df5b50e94fc908c70bce21f98c6fd01b47d23 Mon Sep 17 00:00:00 2001 From: Tyler Clawson Date: Wed, 2 Oct 2024 17:49:48 -0400 Subject: [PATCH 1/8] Parse 'customerSheetClientSecret for Android and iOS --- android/gradle.properties | 2 +- .../java/com/reactnativestripesdk/PaymentSheetFragment.kt | 8 +++++++- ios/StripeSdk+PaymentSheet.swift | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/android/gradle.properties b/android/gradle.properties index c7c7746a7..80bb1a350 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,3 @@ StripeSdk_kotlinVersion=1.8.0 # Keep StripeSdk_stripeVersion in sync with https://github.com/stripe/stripe-identity-react-native/blob/main/android/gradle.properties -StripeSdk_stripeVersion=20.48.+ +StripeSdk_stripeVersion=20.49.+ diff --git a/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt b/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt index a49ff6e45..f04426f52 100644 --- a/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +++ b/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt @@ -55,6 +55,7 @@ class PaymentSheetFragment( } } + @OptIn(ExperimentalCustomerSessionApi::class) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val merchantDisplayName = arguments?.getString("merchantDisplayName").orEmpty() @@ -65,6 +66,7 @@ class PaymentSheetFragment( 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") @@ -191,7 +193,11 @@ class PaymentSheetFragment( .allowsDelayedPaymentMethods(allowsDelayedPaymentMethods ?: false) .defaultBillingDetails(defaultBillingDetails) .customer( - if (customerId.isNotEmpty() && customerEphemeralKeySecret.isNotEmpty()) PaymentSheet.CustomerConfiguration( + 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 497b330f7..b1b8d33a8 100644 --- a/ios/StripeSdk+PaymentSheet.swift +++ b/ios/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( @@ -97,6 +97,9 @@ extension StripeSdk { } configuration.customer = .init(id: customerId, ephemeralKeySecret: customerEphemeralKeySecret) } + else if let customerClientSecret = params["customerSessionClientSecret"] as? String { + configuration.customer = .init(id: customerId, customerSessionClientSecret: customerClientSecret) + } } if let preferredNetworksAsInts = params["preferredNetworks"] as? Array { From 7987b243703ce1112d1010c10e18c27da0badf22 Mon Sep 17 00:00:00 2001 From: Tyler Clawson Date: Thu, 3 Oct 2024 18:31:21 -0400 Subject: [PATCH 2/8] 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; From 8aca8f7a937c11c3bda112c7f235e31492090c95 Mon Sep 17 00:00:00 2001 From: Samer Alabi Date: Fri, 4 Oct 2024 11:49:51 -0400 Subject: [PATCH 3/8] Add Customer Session to server & playground --- example/package.json | 2 +- example/server/index.ts | 170 ++++++++++++---- .../src/screens/PaymentsUICompleteScreen.tsx | 191 ++++++++++++------ example/yarn.lock | 8 +- 4 files changed, 270 insertions(+), 101 deletions(-) diff --git a/example/package.json b/example/package.json index d33734244..300fd3fc2 100644 --- a/example/package.json +++ b/example/package.json @@ -37,7 +37,7 @@ "nodemon": "^2.0.19", "path": "^0.12.7", "react-test-renderer": "18.0.0", - "stripe": "^11.0.0", + "stripe": "^14.18.0", "typescript": "^4.5.5" } } diff --git a/example/server/index.ts b/example/server/index.ts index 33c06e01b..256bdbe72 100644 --- a/example/server/index.ts +++ b/example/server/index.ts @@ -117,7 +117,7 @@ app.post( const { secret_key } = getKeys(payment_method_types[0]); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); @@ -177,7 +177,7 @@ app.post( const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); const customers = await stripe.customers.list({ @@ -255,7 +255,7 @@ app.post( const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); @@ -344,7 +344,7 @@ app.post('/create-setup-intent', async (req, res) => { const { secret_key } = getKeys(payment_method_types[0]); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); const customer = await stripe.customers.create({ email }); @@ -401,7 +401,7 @@ app.post( const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); // console.log('webhook!', req); @@ -464,7 +464,7 @@ app.post('/charge-card-off-session', async (req, res) => { const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); @@ -534,11 +534,27 @@ app.post('/charge-card-off-session', async (req, res) => { // This example sets up an endpoint using the Express framework. // Watch this video to get started: https://youtu.be/rPR2aJ6XnAc. -app.post('/payment-sheet', async (_, res) => { +app.post('/payment-sheet', async (req, res) => { + const { + customer_key_type, + }: { + customer_key_type?: string; + } = req.body; + + if ( + customer_key_type !== 'legacy_ephemeral_key' && + customer_key_type !== 'customer_session' + ) { + return res.send({ + error: + '`customer_key_type` is not valid! Please pass either "customer_session" or "legacy_ephemeral_key"', + }); + } + const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); @@ -553,10 +569,6 @@ app.post('/payment-sheet', async (_, res) => { }); } - const ephemeralKey = await stripe.ephemeralKeys.create( - { customer: customer.id }, - { apiVersion: '2022-11-15' } - ); const paymentIntent = await stripe.paymentIntents.create({ amount: 5099, currency: 'usd', @@ -577,18 +589,72 @@ app.post('/payment-sheet', async (_, res) => { // 'us_bank_account', ], }); - return res.json({ - paymentIntent: paymentIntent.client_secret, - ephemeralKey: ephemeralKey.secret, - customer: customer.id, - }); + + if (customer_key_type === 'legacy_ephemeral_key') { + const ephemeralKey = await stripe.ephemeralKeys.create( + { customer: customer.id }, + { apiVersion: '2023-10-16' } + ); + + return res.json({ + paymentIntent: paymentIntent.client_secret, + ephemeralKey: ephemeralKey.secret, + customer: customer.id, + }); + } else { + const customerSessionClientSecret = await stripe.customerSessions.create( + { + customer: customer.id, + components: { + // This needs to be ignored because `mobile_payment_element` is not specified as a type in `stripe-node` yet. + // @ts-ignore + mobile_payment_element: { + enabled: true, + features: { + payment_method_save: 'disabled', + payment_method_remove: 'enabled', + payment_method_redisplay: 'enabled', + payment_method_allow_redisplay_filters: [ + 'unspecified', + 'limited', + 'always', + ], + }, + }, + }, + }, + { apiVersion: '2023-10-16' } + ); + + return res.json({ + paymentIntent: paymentIntent.client_secret, + customerSessionClientSecret: customerSessionClientSecret.client_secret, + customer: customer.id, + }); + } }); -app.post('/payment-sheet-subscription', async (_, res) => { +app.post('/payment-sheet-subscription', async (req, res) => { + const { + customer_key_type, + }: { + customer_key_type?: string; + } = req.body; + + if ( + customer_key_type !== 'legacy_ephemeral_key' && + customer_key_type !== 'customer_session' + ) { + return res.send({ + error: + '`customer_key_type` is not valid! Please pass either "customer_session" or "legacy_ephemeral_key"', + }); + } + const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); @@ -603,10 +669,6 @@ app.post('/payment-sheet-subscription', async (_, res) => { }); } - const ephemeralKey = await stripe.ephemeralKeys.create( - { customer: customer.id }, - { apiVersion: '2022-11-15' } - ); const subscription = await stripe.subscriptions.create({ customer: customer.id, items: [{ price: 'price_1L3hcFLu5o3P18Zp9GDQEnqe' }], @@ -618,11 +680,49 @@ app.post('/payment-sheet-subscription', async (_, res) => { subscription.pending_setup_intent ); - return res.json({ - setupIntent: setupIntent.client_secret, - ephemeralKey: ephemeralKey.secret, - customer: customer.id, - }); + if (customer_key_type === 'legacy_ephemeral_key') { + const ephemeralKey = await stripe.ephemeralKeys.create( + { customer: customer.id }, + { apiVersion: '2023-10-16' } + ); + + return res.json({ + setupIntent: setupIntent.client_secret, + ephemeralKey: ephemeralKey.secret, + customer: customer.id, + }); + } else { + const customerSessionClientSecret = await stripe.customerSessions.create( + { + customer: customer.id, + components: { + // This needs to be ignored because `mobile_payment_element` is not specified as a type in `stripe-node` yet. + // @ts-ignore + mobile_payment_element: { + enabled: true, + features: { + payment_method_save: true, + payment_method_remove: true, + payment_method_redisplay: true, + payment_method_save_allow_redisplay_override: true, + payment_method_allow_redisplay_filters: [ + 'unspecified', + 'limited', + 'always', + ], + }, + }, + }, + }, + { apiVersion: '2023-10-16' } + ); + + return res.json({ + setupIntent: setupIntent.client_secret, + customerSessionClientSecret: customerSessionClientSecret.client_secret, + customer: customer.id, + }); + } } else { throw new Error( 'Expected response type string, but received: ' + @@ -655,7 +755,7 @@ app.post('/issuing-card-details', async (req, res) => { const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); @@ -678,7 +778,7 @@ app.post('/financial-connections-sheet', async (_, res) => { const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); @@ -704,7 +804,7 @@ app.post('/payment-intent-for-payment-sheet', async (req, res) => { const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); @@ -726,7 +826,7 @@ app.post('/customer-sheet', async (_, res) => { const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); @@ -754,7 +854,7 @@ app.post('/fetch-payment-methods', async (req, res) => { const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); @@ -771,7 +871,7 @@ app.post('/attach-payment-method', async (req, res) => { const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); console.log({ customer: req.body.customerId }); @@ -789,7 +889,7 @@ app.post('/detach-payment-method', async (req, res) => { const { secret_key } = getKeys(); const stripe = new Stripe(secret_key as string, { - apiVersion: '2022-11-15', + apiVersion: '2023-10-16', typescript: true, }); diff --git a/example/src/screens/PaymentsUICompleteScreen.tsx b/example/src/screens/PaymentsUICompleteScreen.tsx index 00956c149..8ac98ac5c 100644 --- a/example/src/screens/PaymentsUICompleteScreen.tsx +++ b/example/src/screens/PaymentsUICompleteScreen.tsx @@ -1,5 +1,13 @@ -import React, { useState } from 'react'; -import { Alert } from 'react-native'; +import React, { useCallback, useEffect, useState } from 'react'; +import { + Alert, + StyleProp, + Switch, + Text, + TextStyle, + View, + ViewStyle, +} from 'react-native'; import { AddressDetails, useStripe, @@ -23,20 +31,39 @@ export default function PaymentsUICompleteScreen() { const [addressSheetVisible, setAddressSheetVisible] = useState(false); const [clientSecret, setClientSecret] = useState(); - const fetchPaymentSheetParams = async () => { + const [customerKeyType, setCustomerKeyType] = useState( + 'legacy_ephemeral_key' + ); + + const fetchPaymentSheetParams = async (customer_key_type: string) => { const response = await fetch(`${API_URL}/payment-sheet`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, + body: JSON.stringify({ + customer_key_type, + }), }); - const { paymentIntent, ephemeralKey, customer } = await response.json(); - setClientSecret(paymentIntent); - return { - paymentIntent, - ephemeralKey, - customer, - }; + + if (customer_key_type === 'customer_session') { + const { paymentIntent, customerSessionClientSecret, customer } = + await response.json(); + setClientSecret(paymentIntent); + return { + paymentIntent, + customerSessionClientSecret, + customer, + }; + } else { + const { paymentIntent, ephemeralKey, customer } = await response.json(); + setClientSecret(paymentIntent); + return { + paymentIntent, + ephemeralKey, + customer, + }; + } }; const openPaymentSheet = async () => { @@ -74,65 +101,89 @@ export default function PaymentsUICompleteScreen() { setLoading(false); }; - const initialisePaymentSheet = async (shippingDetails?: AddressDetails) => { - const { paymentIntent, ephemeralKey, customer } = - await fetchPaymentSheetParams(); + const initialisePaymentSheet = useCallback( + async (shippingDetails?: AddressDetails) => { + const { paymentIntent, customer, ...remainingParams } = + await fetchPaymentSheetParams(customerKeyType); - const address: Address = { - city: 'San Francisco', - country: 'AT', - line1: '510 Townsend St.', - line2: '123 Street', - postalCode: '94102', - state: 'California', - }; - const billingDetails: BillingDetails = { - name: 'Jane Doe', - email: 'foo@bar.com', - phone: '555-555-555', - address: address, - }; + const clientSecretParams = + customerKeyType === 'customer_session' + ? { + customerSessionClientSecret: + remainingParams.customerSessionClientSecret, + } + : { customerEphemeralKeySecret: remainingParams.ephemeralKey }; - const { error } = await initPaymentSheet({ - customerId: customer, - customerEphemeralKeySecret: ephemeralKey, - paymentIntentClientSecret: paymentIntent, - customFlow: false, - merchantDisplayName: 'Example Inc.', - applePay: { merchantCountryCode: 'US' }, - style: 'automatic', - googlePay: { - merchantCountryCode: 'US', - testEnv: true, - }, - returnURL: 'stripe-example://stripe-redirect', - defaultBillingDetails: billingDetails, - defaultShippingDetails: shippingDetails, - allowsDelayedPaymentMethods: true, - appearance, - primaryButtonLabel: 'purchase!', - removeSavedPaymentMethodMessage: 'remove this payment method?', - preferredNetworks: [CardBrand.Amex, CardBrand.Visa], - }); - if (!error) { - setPaymentSheetEnabled(true); - } else if (error.code === PaymentSheetError.Failed) { - Alert.alert( - `PaymentSheet init failed with error code: ${error.code}`, - error.message - ); - } else if (error.code === PaymentSheetError.Canceled) { - Alert.alert( - `PaymentSheet init was canceled with code: ${error.code}`, - error.message - ); + const address: Address = { + city: 'San Francisco', + country: 'AT', + line1: '510 Townsend St.', + line2: '123 Street', + postalCode: '94102', + state: 'California', + }; + const billingDetails: BillingDetails = { + name: 'Jane Doe', + email: 'foo@bar.com', + phone: '555-555-555', + address: address, + }; + + const { error } = await initPaymentSheet({ + customerId: customer, + paymentIntentClientSecret: paymentIntent, + customFlow: false, + merchantDisplayName: 'Example Inc.', + applePay: { merchantCountryCode: 'US' }, + style: 'automatic', + googlePay: { + merchantCountryCode: 'US', + testEnv: true, + }, + returnURL: 'stripe-example://stripe-redirect', + defaultBillingDetails: billingDetails, + defaultShippingDetails: shippingDetails, + allowsDelayedPaymentMethods: true, + appearance, + primaryButtonLabel: 'purchase!', + removeSavedPaymentMethodMessage: 'remove this payment method?', + preferredNetworks: [CardBrand.Amex, CardBrand.Visa], + ...clientSecretParams, + }); + if (!error) { + setPaymentSheetEnabled(true); + } else if (error.code === PaymentSheetError.Failed) { + Alert.alert( + `PaymentSheet init failed with error code: ${error.code}`, + error.message + ); + } else if (error.code === PaymentSheetError.Canceled) { + Alert.alert( + `PaymentSheet init was canceled with code: ${error.code}`, + error.message + ); + } + }, + [customerKeyType, initPaymentSheet] + ); + + const toggleCustomerKeyType = (value: boolean) => { + if (value) { + setCustomerKeyType('customer_session'); + } else { + setCustomerKeyType('legacy_ephemeral_key'); } }; + useEffect(() => { + setPaymentSheetEnabled(false); + initialisePaymentSheet().catch((err) => console.log(err)); + }, [customerKeyType, initialisePaymentSheet]); + return ( // In your app’s checkout, make a network request to the backend and initialize PaymentSheet. // To reduce loading time, make this request before the Checkout button is tapped, e.g. when the screen is loaded. - +