From 3a3dbfd479e054b17958868075adb918290f7b8d Mon Sep 17 00:00:00 2001 From: Tyler Clawson Date: Fri, 4 Oct 2024 19:42:26 -0400 Subject: [PATCH] Add Customer Session to example --- .../PaymentSheetFragment.kt | 3 + example/ios/Podfile.lock | 104 ++++----- example/server/index.ts | 17 +- .../src/components/CustomerSessionSwitch.tsx | 39 ++++ example/src/helpers.ts | 12 + ...mentSheetDeferredIntentMultiStepScreen.tsx | 59 ++++- .../PaymentSheetDeferredIntentScreen.tsx | 220 +++++++++++------- .../screens/PaymentSheetWithSetupIntent.tsx | 70 ++++-- .../src/screens/PaymentsUICompleteScreen.tsx | 45 +--- .../src/screens/PaymentsUICustomScreen.tsx | 63 ++++- stripe-react-native.podspec | 2 +- 11 files changed, 415 insertions(+), 219 deletions(-) create mode 100644 example/src/components/CustomerSessionSwitch.tsx diff --git a/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt b/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt index 15b4ea591..e7c887013 100644 --- a/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt +++ b/android/src/main/java/com/reactnativestripesdk/PaymentSheetFragment.kt @@ -438,6 +438,9 @@ class PaymentSheetFragment( return if (customerSessionClientSecret.isNotEmpty() && customerEphemeralKeySecret.isNotEmpty()) { throw PaymentSheetException("`customerEphemeralKeySecret` and `customerSessionClientSecret` cannot both be set") } else if (customerId.isNotEmpty() && customerSessionClientSecret.isNotEmpty()) { + println("YEET creating customer configuration") + println("YEET customerID: $customerId") + println("YEET customerSessionClientSecret: $customerSessionClientSecret") PaymentSheet.CustomerConfiguration.createWithCustomerSession( id = customerId, clientSecret = customerSessionClientSecret diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index cce12ca84..6e68ab586 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -299,50 +299,50 @@ PODS: - RNScreens (3.29.0): - React-Core - React-RCTImage - - Stripe (23.28.1): - - StripeApplePay (= 23.28.1) - - StripeCore (= 23.28.1) - - StripePayments (= 23.28.1) - - StripePaymentsUI (= 23.28.1) - - StripeUICore (= 23.28.1) - - stripe-react-native (0.38.4): + - Stripe (23.30.0): + - StripeApplePay (= 23.30.0) + - StripeCore (= 23.30.0) + - StripePayments (= 23.30.0) + - StripePaymentsUI (= 23.30.0) + - StripeUICore (= 23.30.0) + - stripe-react-native (0.38.6): - React-Core - - Stripe (~> 23.28.0) - - StripeApplePay (~> 23.28.0) - - StripeFinancialConnections (~> 23.28.0) - - StripePayments (~> 23.28.0) - - StripePaymentSheet (~> 23.28.0) - - StripePaymentsUI (~> 23.28.0) - - stripe-react-native/Tests (0.38.4): + - Stripe (~> 23.30.0) + - StripeApplePay (~> 23.30.0) + - StripeFinancialConnections (~> 23.30.0) + - StripePayments (~> 23.30.0) + - StripePaymentSheet (~> 23.30.0) + - StripePaymentsUI (~> 23.30.0) + - stripe-react-native/Tests (0.38.6): - React-Core - - Stripe (~> 23.28.0) - - StripeApplePay (~> 23.28.0) - - StripeFinancialConnections (~> 23.28.0) - - StripePayments (~> 23.28.0) - - StripePaymentSheet (~> 23.28.0) - - StripePaymentsUI (~> 23.28.0) - - StripeApplePay (23.28.1): - - StripeCore (= 23.28.1) - - StripeCore (23.28.1) - - StripeFinancialConnections (23.28.1): - - StripeCore (= 23.28.1) - - StripeUICore (= 23.28.1) - - StripePayments (23.28.1): - - StripeCore (= 23.28.1) - - StripePayments/Stripe3DS2 (= 23.28.1) - - StripePayments/Stripe3DS2 (23.28.1): - - StripeCore (= 23.28.1) - - StripePaymentSheet (23.28.1): - - StripeApplePay (= 23.28.1) - - StripeCore (= 23.28.1) - - StripePayments (= 23.28.1) - - StripePaymentsUI (= 23.28.1) - - StripePaymentsUI (23.28.1): - - StripeCore (= 23.28.1) - - StripePayments (= 23.28.1) - - StripeUICore (= 23.28.1) - - StripeUICore (23.28.1): - - StripeCore (= 23.28.1) + - Stripe (~> 23.30.0) + - StripeApplePay (~> 23.30.0) + - StripeFinancialConnections (~> 23.30.0) + - StripePayments (~> 23.30.0) + - StripePaymentSheet (~> 23.30.0) + - StripePaymentsUI (~> 23.30.0) + - StripeApplePay (23.30.0): + - StripeCore (= 23.30.0) + - StripeCore (23.30.0) + - StripeFinancialConnections (23.30.0): + - StripeCore (= 23.30.0) + - StripeUICore (= 23.30.0) + - StripePayments (23.30.0): + - StripeCore (= 23.30.0) + - StripePayments/Stripe3DS2 (= 23.30.0) + - StripePayments/Stripe3DS2 (23.30.0): + - StripeCore (= 23.30.0) + - StripePaymentSheet (23.30.0): + - StripeApplePay (= 23.30.0) + - StripeCore (= 23.30.0) + - StripePayments (= 23.30.0) + - StripePaymentsUI (= 23.30.0) + - StripePaymentsUI (23.30.0): + - StripeCore (= 23.30.0) + - StripePayments (= 23.30.0) + - StripeUICore (= 23.30.0) + - StripeUICore (23.30.0): + - StripeCore (= 23.30.0) - Yoga (1.14.0) DEPENDENCIES: @@ -511,17 +511,17 @@ SPEC CHECKSUMS: RNCMaskedView: bc0170f389056201c82a55e242e5d90070e18e5a RNCPicker: 0bf8ef8f7800524f32d2bb2a8bcadd53eda0ecd1 RNScreens: fa9b582d85ae5d62c91c66003b5278458fed7aaa - Stripe: 20e24971647daa5750e7764faa1e8aefe4917243 - stripe-react-native: 3479d62b758ac162b4465cb0aed313b525a575f3 - StripeApplePay: 5b098a0ba6136f4b587e03f5a3776461b4f20dd4 - StripeCore: 880a68482cf78d4745c5213c2fd3446efc73574b - StripeFinancialConnections: 49a19ca17dbb3055a8b559a1e3adfe769784a8f8 - StripePayments: 3af5b03fa1831c301a4d1621eb0e790280dccb46 - StripePaymentSheet: 8321efebb6d104add8fce929286d414545913114 - StripePaymentsUI: 4c35e12ebcd4bd9bd21e379f03f730bee097250d - StripeUICore: 22b314dc9f7ea8814d0f9eeba0ddb5e4b77f34f3 + Stripe: 9757efc154de1d9615cbea4836d590bc4034d3a4 + stripe-react-native: 99b3cc2fae5a53f0ab1d9949ff19ae2aaba73641 + StripeApplePay: ca33933601302742623762157d587b79b942d073 + StripeCore: 2af250a2366ff2bbf64d4243c5f9bbf2a98b2aaf + StripeFinancialConnections: 3ab1ef6182ec44e71c29e9a2100b663f9713ac20 + StripePayments: 658a16bd34d20c8185aa281866227b9e1743300e + StripePaymentSheet: eac031f76d7fbb4f52df9b9c39be5be671ca4c07 + StripePaymentsUI: 7d7cffb2ecfc0d6b5ac3a4488c02893a5ff6cc77 + StripeUICore: bb102d453b1e1a10a37f810bc0a9aa0675fb17fd Yoga: 8a90b50af67eaa9fe94fd03e550bfeab06096873 PODFILE CHECKSUM: 7b4a5e954edfeed0967520f79be9e773f07d8266 -COCOAPODS: 1.11.3 +COCOAPODS: 1.15.2 diff --git a/example/server/index.ts b/example/server/index.ts index 256bdbe72..c82b93934 100644 --- a/example/server/index.ts +++ b/example/server/index.ts @@ -541,16 +541,6 @@ app.post('/payment-sheet', async (req, res) => { 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, { @@ -701,10 +691,9 @@ app.post('/payment-sheet-subscription', async (req, res) => { 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_save: 'enabled', + payment_method_remove: 'enabled', + payment_method_redisplay: 'enabled', payment_method_allow_redisplay_filters: [ 'unspecified', 'limited', diff --git a/example/src/components/CustomerSessionSwitch.tsx b/example/src/components/CustomerSessionSwitch.tsx new file mode 100644 index 000000000..5c13dcfb0 --- /dev/null +++ b/example/src/components/CustomerSessionSwitch.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { + View, + Text, + Switch, + ViewStyle, + TextStyle, + StyleProp, +} from 'react-native'; + +interface CustomerSessionSwitchProps { + value: boolean; // Switch value + onValueChange: (value: boolean) => void; +} + +const SWITCH_CONTAINER_STYLE: StyleProp = { + flex: 1, + flexDirection: 'row', + justifyContent: 'space-between', +}; + +const SWITCH_TITLE_STYLE: StyleProp = { + marginEnd: 10, + textAlignVertical: 'center', +}; + +const CustomerSessionSwitch: React.FC = ({ + value, + onValueChange, +}) => { + return ( + + Enable Customer Session + + + ); +}; + +export default CustomerSessionSwitch; diff --git a/example/src/helpers.ts b/example/src/helpers.ts index 5235ff22b..3bf7e3b32 100644 --- a/example/src/helpers.ts +++ b/example/src/helpers.ts @@ -21,3 +21,15 @@ export async function fetchPublishableKey( return null; } } + +export function getClientSecretParams( + customerKeyType: string, + remainingParams: any +): any { + return customerKeyType === 'customer_session' + ? { + customerSessionClientSecret: + remainingParams.customerSessionClientSecret, + } + : { customerEphemeralKeySecret: remainingParams.ephemeralKey }; +} diff --git a/example/src/screens/PaymentSheetDeferredIntentMultiStepScreen.tsx b/example/src/screens/PaymentSheetDeferredIntentMultiStepScreen.tsx index bc6b62ad1..7011faedd 100644 --- a/example/src/screens/PaymentSheetDeferredIntentMultiStepScreen.tsx +++ b/example/src/screens/PaymentSheetDeferredIntentMultiStepScreen.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { Alert, StyleSheet, View } from 'react-native'; import { useStripe, @@ -11,6 +11,8 @@ import { colors } from '../colors'; import Button from '../components/Button'; import PaymentScreen from '../components/PaymentScreen'; import { API_URL } from '../Config'; +import CustomerSessionSwitch from '../components/CustomerSessionSwitch'; +import { getClientSecretParams } from '../helpers'; export default function PaymentSheetDeferredIntentMultiStepScreen() { const { initPaymentSheet, presentPaymentSheet, confirmPaymentSheetPayment } = @@ -22,25 +24,48 @@ export default function PaymentSheetDeferredIntentMultiStepScreen() { label: string; } | null>(null); - 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 { customer } = await response.json(); - return { - customer, - }; + if (customer_key_type === 'customer_session') { + const { customerSessionClientSecret, customer } = await response.json(); + return { + customerSessionClientSecret, + customer, + }; + } else { + const { ephemeralKey, customer } = await response.json(); + return { + ephemeralKey, + customer, + }; + } }; - const initialisePaymentSheet = async () => { + const initialisePaymentSheet = useCallback(async () => { setLoading(true); try { - const { customer } = await fetchPaymentSheetParams(); + const { customer, ...remainingParams } = await fetchPaymentSheetParams( + customerKeyType + ); + + const clientSecretParams = getClientSecretParams( + customerKeyType, + remainingParams + ); const address: Address = { city: 'San Francisco', @@ -63,6 +88,7 @@ export default function PaymentSheetDeferredIntentMultiStepScreen() { style: 'automatic', returnURL: 'stripe-example://stripe-redirect', defaultBillingDetails: billingDetails, + ...clientSecretParams, intentConfiguration: { confirmHandler: async ( paymentMethod: PaymentMethod.Result, @@ -120,8 +146,21 @@ export default function PaymentSheetDeferredIntentMultiStepScreen() { } finally { setLoading(false); } + }, [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]); + const choosePaymentOption = async () => { const { error, paymentOption } = await presentPaymentSheet(); @@ -154,6 +193,10 @@ export default function PaymentSheetDeferredIntentMultiStepScreen() { // 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. +