From 93dcdac6a08c9e6af703089528923b2f3f117cae Mon Sep 17 00:00:00 2001 From: charliecruzan-stripe <97612659+charliecruzan-stripe@users.noreply.github.com> Date: Tue, 10 May 2022 10:48:05 -0400 Subject: [PATCH] feat: add token & paymentMethodId handling to confirmPayment for Cards (#931) --- .../PaymentMethodCreateParamsFactory.kt | 27 ++++- e2e/payments.test.ts | 2 +- .../src/screens/SetupFuturePaymentScreen.tsx | 98 ++++++++++++++++++- 3 files changed, 117 insertions(+), 10 deletions(-) diff --git a/android/src/main/java/com/reactnativestripesdk/PaymentMethodCreateParamsFactory.kt b/android/src/main/java/com/reactnativestripesdk/PaymentMethodCreateParamsFactory.kt index 4dd04e789..58c1dfc08 100644 --- a/android/src/main/java/com/reactnativestripesdk/PaymentMethodCreateParamsFactory.kt +++ b/android/src/main/java/com/reactnativestripesdk/PaymentMethodCreateParamsFactory.kt @@ -172,14 +172,31 @@ class PaymentMethodCreateParamsFactory( @Throws(PaymentMethodCreateParamsException::class) private fun createCardPaymentSetupParams(): ConfirmSetupIntentParams { + val paymentMethodId = getValOr(paymentMethodData, "paymentMethodId", null) + val token = getValOr(paymentMethodData, "token", null) val cardParams = cardFieldView?.cardParams ?: cardFormView?.cardParams - ?: throw PaymentMethodCreateParamsException("Card details not complete") - val paymentMethodCreateParams = - PaymentMethodCreateParams.create(cardParams, billingDetailsParams) + if (paymentMethodId != null) { + return ConfirmSetupIntentParams.create( + paymentMethodId, + clientSecret + ) + } - return ConfirmSetupIntentParams - .create(paymentMethodCreateParams, clientSecret) + val paymentMethodCreateParams = + if (token != null) + PaymentMethodCreateParams.create(PaymentMethodCreateParams.Card.create(token), billingDetailsParams) + else if (cardParams != null) + PaymentMethodCreateParams.create(cardParams, billingDetailsParams) + else + null + + if (paymentMethodCreateParams != null) { + return ConfirmSetupIntentParams + .create(paymentMethodCreateParams, clientSecret) + } else { + throw PaymentMethodCreateParamsException("Card details not complete") + } } @Throws(PaymentMethodCreateParamsException::class) diff --git a/e2e/payments.test.ts b/e2e/payments.test.ts index f584c1f13..e17e23766 100644 --- a/e2e/payments.test.ts +++ b/e2e/payments.test.ts @@ -149,7 +149,7 @@ describe('Common payment scenarios', () => { cardField.setExpiryDate('12/22'); cardField.setCvcNumber('123'); - getElementByText('Save').click(); + getElementByText('Save via card input form').click(); const alert = getElementByText('Success'); alert.waitForDisplayed({ timeout: 20000, diff --git a/example/src/screens/SetupFuturePaymentScreen.tsx b/example/src/screens/SetupFuturePaymentScreen.tsx index e6fd68f2c..7147b8be1 100644 --- a/example/src/screens/SetupFuturePaymentScreen.tsx +++ b/example/src/screens/SetupFuturePaymentScreen.tsx @@ -5,6 +5,8 @@ import { useConfirmPayment, useConfirmSetupIntent, useStripe, + createToken, + createPaymentMethod, } from '@stripe/stripe-react-native'; import { API_URL } from '../Config'; import Button from '../components/Button'; @@ -61,8 +63,7 @@ export default function SetupFuturePaymentScreen() { return { clientSecret, error }; }; - const handlePayPress = async () => { - console.log('email', email); + const handlePayPressUsingForm = async () => { // 1. Create setup intent on backend const clientSecret = await createSetupIntentOnBackend(email); @@ -101,6 +102,83 @@ export default function SetupFuturePaymentScreen() { } }; + const handlePayPressUsingToken = async () => { + const clientSecret = await createSetupIntentOnBackend(email); + + const { error: tokenError, token } = await createToken({ + type: 'Card', + name: 'David Wallace', + currency: 'usd', + }); + + if (tokenError) { + Alert.alert(`Error code: ${tokenError.code}`, tokenError.message); + console.log('Setup intent confirmation error', tokenError.message); + return; + } + + const { error, setupIntent: setupIntentResult } = await confirmSetupIntent( + clientSecret, + { + paymentMethodType: 'Card', + paymentMethodData: { + token: token?.id, + }, + } + ); + + if (error) { + Alert.alert(`Error code: ${error.code}`, error.message); + console.log('Setup intent confirmation error', error.message); + } else if (setupIntentResult) { + Alert.alert( + 'Success', + `Setup intent created. Intent status: ${setupIntentResult.status}` + ); + + setSetupIntent(setupIntentResult); + } + }; + + const handlePayPressUsingID = async () => { + const clientSecret = await createSetupIntentOnBackend(email); + + const { error: e, paymentMethod } = await createPaymentMethod({ + paymentMethodType: 'Card', + }); + + if (e) { + Alert.alert(`Error code: ${e.code}`, e.message); + console.log('Setup intent confirmation error', e.message); + return; + } else if (!paymentMethod) { + Alert.alert(`Something went wrong creating the payment method.`); + return; + } + + const { error, setupIntent: setupIntentResult } = await confirmSetupIntent( + clientSecret, + { + paymentMethodType: 'Card', + paymentMethodData: { + paymentMethodId: paymentMethod.id, + }, + } + ); + + if (error) { + Alert.alert(`Error code: ${error.code}`, error.message); + console.log('Setup intent confirmation error', error.message); + } else if (setupIntentResult) { + Alert.alert( + 'Success', + `Setup intent created. Intent status: ${setupIntentResult.status}` + ); + + setSetupIntent(setupIntentResult); + } + }; + // It's only for example purposes // This action is responsible for charging your previously added card and should be called independently of the payment flow. const handleOffSessionPayment = async () => { @@ -204,8 +282,20 @@ export default function SetupFuturePaymentScreen() {