Ad

Stipe, React Native, [Unhandled Promise Rejection: SyntaxError: JSON Parse Error: Unexpected Identifier "Error"]

I'm developing an app with Expo, Firebase Cloud Function and Stripe, for some reason the first time I navigate to the screen CardPayment the code returns the error: [Unhandled promise rejection: SyntaxError: JSON Parse error: Unexpected identifier "Error"], but if I refresh the screen the code runs perfectly. Anyone have any idea why and how I can fix it? I've tried a bunch of stuff, even using axios, but nothing seems to be working.

Backend

exports.addCardForExistingCustomer = functions.https.onRequest(async (req, res) => {
  
    const ephemeralKey = await stripe.ephemeralKeys.create(
      {customer: `${req.query.customer}`},
      {apiVersion: '2020-08-27'}
    );
  
    const setupIntent = await stripe.setupIntents.create({
      customer: `${req.query.customer}`
    });

    const paymentMethods = await stripe.paymentMethods.list({
      customer: `${req.query.customer}`,
      type: 'card',
    });
    
    res.json({
      setupIntent: setupIntent.client_secret,
      ephemeralKey: ephemeralKey.secret,
      customer: `${req.query.customer}`,
      paymentMethods: paymentMethods,
    })
});

Frontend

    export default function CardPayment() {
    
      const { initPaymentSheet, presentPaymentSheet } = useStripe();
      const [loading, setLoading] = useState(false);
      const [customerId, setCustomerId] = useState('');
    
      const fetchPaymentSheetParams = async () => {
        const response = await fetch(`https://<<path>>.cloudfunctions.net/addCardForExistingCustomer?customer=${customerId}`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
        });
        const { setupIntent, ephemeralKey, customer } = await response.json();
    
        return {
          setupIntent,
          ephemeralKey,
          customer,
        };
      };
    
      const initializePaymentSheet = async () => {
        const {
          setupIntent,
          ephemeralKey,
          customer,
        } = await fetchPaymentSheetParams();
    
        console.log(setupIntent)
    
        const { error } = await initPaymentSheet({
          customerId: customer,
          customerEphemeralKeySecret: ephemeralKey,
          setupIntentClientSecret: setupIntent,
        });
        if (!error) {
          setLoading(true);
        } else {
          console.log(error)
        }
      };
    
    
      const openPaymentSheet = async () => {
    
        const { error } = await presentPaymentSheet();
    
        if (!error) {
          Alert.alert('Success', 'Your payment method is successfully set up for future payments!');
        }
      };
    
      const fetchCustomer = async () => {
       const fetchInformation = doc(db, 'clients', 'user', 'Information', auth.currentUser.uid)
       await getDoc(fetchInformation)
       .then((snapshot) => {
        const stripeId = snapshot.data().stripeCustomerId
        setCustomerId(stripeId)
        initializePaymentSheet();
      })
  }
    
      useEffect(() => {
        fetchCustomer()
      }, []);
      
      if (loading == false) {
        return (
          <View>
            <ActivityIndicator/>
          </View>
        )
      } else {
        return (
          <View>
            <CustomButton
              buttonTxt={'Add Credit/Debit Card'}
              txtColor={'white'}
              backgroundColor={'black'}
              onPress={openPaymentSheet}
            />
          </View>
        )
      }
    }
Ad

Answer

fetchCustomer on the frontend isn't waiting for the getDoc promise chain to complete. Try putting return or await before the call to getDoc. If return is used, fetchCustomer as posted doesn't need to be an async function.

If this resolves the error, my guess would be that some kind of response caching is allowing the then handler of getDoc(fetchInformation) to win a race condition set up in promise job handling (in the microtask queue) when the page is reloaded.

It is not self-evident why this handler (async (snapshot) => {...}) has been declared as an async function - it appears to be straight forward synchronous code.

Ad
source: stackoverflow.com
Ad