Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ios] presentPaymentSheet will crash the app once is being called. #290

Closed
paschalidi opened this issue May 31, 2021 · 15 comments
Closed

[ios] presentPaymentSheet will crash the app once is being called. #290

paschalidi opened this issue May 31, 2021 · 15 comments
Labels
docs Docs question Further information is requested

Comments

@paschalidi
Copy link

paschalidi commented May 31, 2021

Describe the bug
Have been following this tutorial and even though most of the things went pretty well, the app would crash when I am calling the presentPaymentSheet with the clientSecret returned from my backend. There isn't any log I could find that would help me debug the issue.

Here is my component, I hope pasting the code helps a little bit more. I am using firebase functions as my backend.

import React, {useState, useEffect} from 'react';
import {StyleSheet, Alert} from 'react-native';
import {Text, useTheme} from 'react-native-paper';
import {
  useStripe,
} from '@stripe/stripe-react-native';
import {useProfile} from './ProfileProvider';
import {SafeAreaView} from 'react-native-safe-area-context';
import {FormButton} from '../../components/FormButton';

export default function CardDetailsScreen({navigation}) {
  const {styles} = useStyles();
  const {
    profile: {stripeId},
  } = useProfile();
  const {initPaymentSheet, presentPaymentSheet} = useStripe();
  const [paymentSheetEnabled, setPaymentSheetEnabled] = useState(false);
  const [clientSecret, setClientSecret] = useState();

  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const initialisePaymentSheet = async () => {
      const fetchPaymentSheetParams = async () => {
        try {
          const response = await fetch(
            'https://MY_BACKEND_ENDPOINT.cloudfunctions.net/completePaymentWithStripe',
            {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                amount: 55 * 100,
                customerStripeId: stripeId,
              }),
            },
          );

          const {paymentIntent, ephemeralKey} = await response.json();
          setClientSecret(paymentIntent);

          return {paymentIntent, ephemeralKey};
        } catch (e) {
          console.log(e);
          debugger;
        }
      };

      const {paymentIntent, ephemeralKey} = await fetchPaymentSheetParams();
      const {error} = await initPaymentSheet({
        customerId: stripeId,
        customerEphemeralKeySecret: ephemeralKey,
        paymentIntentClientSecret: paymentIntent,
      });

      if (!error) {
        setPaymentSheetEnabled(true);
      }
    };

    stripeId && initialisePaymentSheet();
  }, [initPaymentSheet, stripeId]);

  const openPaymentSheet = async () => {
    if (!clientSecret) {
      return;
    }
    setLoading(true);
    try {
      debugger;
      const {error} = await presentPaymentSheet({clientSecret});

      if (error) {
        Alert.alert(`Error code: ${error.code}`, error.message);
      } else {
        Alert.alert('Success', 'Your order is confirmed!');
      }
    } catch (e) {
      debugger;
    }
  };

  return (
    <SafeAreaView style={styles.container}>
      <Text>Hello</Text>
      <FormButton
        title="Show Payment Sheet"
        modeValue="contained"
        onPress={openPaymentSheet}
        loading={loading}
        disabled={!paymentSheetEnabled}
      />
    </SafeAreaView>
  );
}

Expected behavior
Expected would be to open the sheet like the example
image

Desktop (please complete the following information):

  • OS: iOS
  • Version 1.15.7

Smartphone (please complete the following information):

  • Device: iPhone 12
  • OS: 14.4
@paschalidi paschalidi changed the title [ios] [ios] `` May 31, 2021
@paschalidi paschalidi changed the title [ios] `` [ios] presentPaymentSheet will crash the app once is being called. May 31, 2021
@thorsten-stripe
Copy link
Contributor

thorsten-stripe commented May 31, 2021

Can you provide a log of the values of stripeId, ephemeralKey, paymentIntent, and clientSecret just before you call presentPaymentSheet please?

@thorsten-stripe thorsten-stripe added the question Further information is requested label May 31, 2021
@WillKre
Copy link

WillKre commented Jun 2, 2021

Hi @thorsten-stripe I'm also getting this issue with a slightly varied version of the official example

Here's my code:

export default function PaymentsUICompleteScreen() {
  const { initPaymentSheet, presentPaymentSheet } = useStripe();
  const [email, setEmail] = useState('');
  const [amount, setAmount] = useState('');

  const fetchPaymentSheetParams = async () => {
    const apiClient = new APIClient(PAYMENT_URL);

    try {
      const response = await apiClient.request(PROCESS_CHARGE, { // Just an API call to my backend endpoint to fetch data returned below
        email,
        amount: Number(amount),
      });

      return {
        paymentIntent: response.intent.id,
        ephemeralKey: response.ephemeralKey.id,
        customer: response.customer.id,
      };
    } catch (err) {
      console.log('fetchPaymentSheetParams err is: ', err);
    }
  };

  const openPaymentSheet = async (paymentIntent: string) => {
    console.log('Inside openPaymentSheet: paymentIntent is: ', paymentIntent);
    if (!paymentIntent) {
      return;
    }
    console.log('Come this far 1');
    try {
      console.log('Come this far 2');
      await presentPaymentSheet({ clientSecret: paymentIntent }); // Fails here...
      console.log('Come this far 3');
    } catch (err) {
      console.log('openPaymentSheet err is: ', err);
    }

    console.log('Come this far 4');
  };

  const initialisePaymentSheet = async () => {
    const { paymentIntent, ephemeralKey, customer } = await fetchPaymentSheetParams();

    console.log('paymentIntent is: ', paymentIntent);
    console.log('ephemeralKey is: ', ephemeralKey);
    console.log('customer is: ', customer);

    try {
      await initPaymentSheet({
        customerId: customer,
        customerEphemeralKeySecret: ephemeralKey,
        paymentIntentClientSecret: paymentIntent,
        customFlow: false,
        merchantDisplayName: 'Example Inc.',
        style: 'alwaysDark',
      });
      openPaymentSheet(paymentIntent);
    } catch (err) {
      console.log('initialisePaymentSheet err is: ', err);
    }
  };

  return (
    <PaymentScreen>
      <Input value={email} onChangeText={setEmail} label="Email" />
      <Input value={amount} onChangeText={setAmount} label="Amount" />
      <Button text="Charge" onPress={initialisePaymentSheet} />
    </PaymentScreen>
  );
}

Here's the log output I get (in order):

LOG      paymentIntent is:  pi_1IxhTODchL2cGl0JcKZO5U4c
LOG      ephemeralKey is:  ephkey_1IxhTODchL2cGl0JmXxpYtGC
LOG      customer is:  cus_JatFNcTg1T6Dk3
LOG      Inside openPaymentSheet: paymentIntent is:  pi_1IxhTODchL2cGl0JcKZO5U4c
LOG      Come this far 1
LOG      Come this far 2

After the "Come this far 2" log it exits and the app closes (crashes?) with no error or further log/info, so similarly to @paschalidi you can tell it's crashing because of presentPaymentSheet.

Please let me know if you can see anything wrong whether it be setup or the values being passed in

@thorsten-stripe
Copy link
Contributor

@apswak you need to use the secrets instead of the ids

res.json({
    paymentIntent: paymentIntent.client_secret,
    ephemeralKey: ephemeralKey.secret,
    customer: customer.id,
  });

@thorsten-stripe thorsten-stripe added the docs Docs label Jun 2, 2021
@paschalidi
Copy link
Author

@apswak yep seems to be the same. Thanks @thorsten-stripe will try it out tonight! 👍

@amonraRivers
Copy link

This happens to me as well when running without xcode the app just crashes. When running with xcode it shows the following error:

Stripe/PaymentSheet.swift:98: Fatal error: presentingViewController is already presenting a view controller.

Line 98 of said file is :
assertionFailure("presentingViewController is already presenting a view controller")

Don't know how to fix this but will continue to investigate, can anyone help me make it work?, Thank you

@amonraRivers
Copy link

So I solved it! In my case I was using wix react-native-navigation. And the app crashed when calling the function presentPaymentSheet on a modal. I had to setRoot from the modal to go to the checkout process. That closes all modals which solved the issue.

@thorsten-stripe
Copy link
Contributor

Thanks for providing your resolution @ramosquito5 ! I'm closing this out as it seems resolved. Do reopen if that's not the case. Thanks.

@WillKre
Copy link

WillKre commented Jun 4, 2021

@thorsten-stripe just for the record your answer was indeed the solution to my issue (using id instead of secret / client_secret), works now thanks

@trex-quo
Copy link

trex-quo commented Jun 4, 2021

So I solved it! In my case I was using wix react-native-navigation. And the app crashed when calling the function presentPaymentSheet on a modal. I had to setRoot from the modal to go to the checkout process. That closes all modals which solved the issue.

This tripped me up for awhile too, I think that it would be a good caveat to inform people of that you cannot call presentPaymentSheet from a modal

@paschalidi
Copy link
Author

paschalidi commented Jun 7, 2021

Hey sorry for the super late response @thorsten-stripe but havent had much time lately. I am still facing the same situation. Can you please see if I am missing something or if we can reopen the issue?

PS: you can see my code on the react-native side in the issue description, in case you need it

Jun-07-2021.22-24-29.mp4

@paschalidi
Copy link
Author

@thorsten-stripe wondering if we can reopen this after my last comment?

@paschalidi
Copy link
Author

More info:
image

I think, it looks similar to this one (?)

@talha-m-dev
Copy link

talha-m-dev commented Jul 29, 2021

I'm still getting that error. i tried everything but it doesn't work.
it's working fine on Android but app freezes on IOS during sheet opening.
Screen Shot 2021-07-29 at 3 08 41 PM

@eliasbrazjr
Copy link

@amonraRivers I have the same problem, wix navigation. But I couldn't solve it. Can you explain better how to fix it?

@amonraRivers
Copy link

Hello,
Get rid of any modal. I would recommend, when the user clicks on checkout or similar, to then setRoot in wix navigation to close all modals and take the user to the payment sheet page. Hope this helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Docs question Further information is requested
Projects
None yet
Development

No branches or pull requests

7 participants