Gourav Sanyal
Gourav Sanyal

Reputation: 166

Unhandled Rejection (TypeError): Cannot read property 'amount' of undefined

Making an eCommerce app with React. This is a "handleSubmit" function that is returning an error every time I'm trying to checkout.

I have imported "db" and declared "paymentIntent". It is still coming up with this error in the browser.

    const handleSubmit = async (event) => {
        //do fancy stripe stuff
        event.preventDefault()
        setProcessing(true)

        // const payload = await stripe

        const payload = await stripe.confirmCardPayment(clientSecret, {
            payment_method: {
                card: elements.getElement(CardElement)
            }
        }).then(({ paymentIntent }) => {
            //paymetnIntent = payment Confirmation

           db
            .collection('users') 
            .doc(user?.uid)
            .collection('orders')
            .doc(paymentIntent?.id)
            .set({
                basket: basket,
                amount: paymentIntent.amount,
                created: paymentIntent.created,
            })

            setSucceeded(true);
            setError(null)
            setProcessing(false)
            dispatch({
                type: 'EMPTY_BASKET'
            })

            history.replace('/orders')
        })

    }

Upvotes: 1

Views: 1237

Answers (4)

Ritesh Kumar
Ritesh Kumar

Reputation: 11

import { db } from './firebase';
import { collection, addDoc } from "firebase/firestore"; 
    
const handleSubmit = async (event) => {
            event.preventDefault();
            setProcessing(true);
    
            const payload = await stripe.confirmCardPayment(clientSecret, {
                payment_method: {
                    card: elements.getElement(CardElement)
                }
            }).then( async ({ paymentIntent }) => {
                try{
                    const docRef = await addDoc(collection(db, "users", user.uid, "orders"), {
                        basket: basket,
                        amount: paymentIntent.amount,
                        created: paymentIntent.created
                    });
                    console.log("Document written with orderID: ", docRef.id);
                }catch(e){
                    console.log("Error adding document: ", e);
                }
    
                setSucceeded(true);
                setError(null);
                setProcessing(false);
    
                dispatch({
                    type: 'EMPTY_BASKET'
                })
    
                navigate('/orders', { replace : true }); // don't come back to the payment page
            })
        }

Upvotes: 1

Vishmi Money
Vishmi Money

Reputation: 144

I faced the same problem some time ago and we have to notice that in the stipe documentation for confirmCardPayment, it says the returning result object is either the payment intent or the error.

So if the request is successful you'll receive a payment intent object for the response as,

{
   "id": "xxx", 
   "amount": 30, 
   "created": 123213,
    ... 
}

or if the request fails the response would be an error as,

{
   "error": {
      "code": "parameter_unknown",
      "message": "Error message",
      "payment_intent": {
          "id": "xxx",
          "amount": 30,
          ...
       }
   }
}

Note that in the success response you'll get the payment intent object itself so there will be no property as paymentIntent in the response object and that's why you get it as undefined. So in your code you'll have to do as,

.then(({ id, amount, created, error }) => {
     if(error) {
        // error handling
        // dispatch error action
      } else {
        db.collection('users') 
         .doc(user?.uid)
         .collection('orders')
         .doc(id) // destructured id from paymentIntent object
         .set({
            basket: basket,
            amount, // destructured amount from paymentIntent object
            created, // destructured created from paymentIntent object
           })

         setSucceeded(true);
         setError(null)
         setProcessing(false)
         dispatch({
              type: 'EMPTY_BASKET'
         })

         history.replace('/orders')
      }
})

Upvotes: 2

Zsolt Meszaros
Zsolt Meszaros

Reputation: 23189

You will need to check if there are any errors. .confirmPayment() returns a promise and when it's resolved a result object. It has two keys, paymentIntent (the one you already use), and error (the one you need to check):

.then(({ error, paymentIntent }) => {
  // check if there is any error
  if (error || !paymentIntent) {
    // handle error, tell the user
    // ...
    // return to exit code
    return;
  }

  db.collection('users')
    .doc(user?.uid)
    .collection('orders')
    .doc(paymentIntent.id)
    .set({
      basket: basket,
      amount: paymentIntent.amount,
      created: paymentIntent.created,
    });

  setSucceeded(true);
  setError(null);
  setProcessing(false);
  dispatch({
    type: 'EMPTY_BASKET',
  });

  history.replace('/orders');
});

Upvotes: 2

Suleman Ahmad
Suleman Ahmad

Reputation: 484

Try to add console.log(paymentIntent) after line in the beginning of the then body and check the format it has. If it contains the amount property in paymentIntent then Add the checks before extracting the amount property as it might be taking sometime to load that. So,

       .set({
            basket: basket,
            amount: paymentIntent?.amount, //Add check here
            created: paymentIntent?.created, //Also check here
        })

Upvotes: 1

Related Questions