1

i'm stucked on this part since the week before. I have this:

export const CreditCardForm = (props) => {
  return (
    <>
      <Elements stripe={stripePromise}>
        <CheckoutForm amount={props.gems} />
      </Elements>
    </>
  );
}

And inside CheckoutForm I have this form:

<form className="card card-body"  style={{}} onSubmit={handleSubmit} action='@Request.Form["continue_url"]' method="POST" id="payment-form">
    <Accordion>
      <AccordionSummary
        expandIcon={<ExpandMoreIcon />}
        aria-controls="panel1a-content"
        id="panel1a-header"
      >
        <Typography><label htmlFor="card-element">{t('gems.card')}</label></Typography>
      </AccordionSummary>
      <AccordionDetails>
        <CardElement />

      </AccordionDetails>
    </Accordion>
    <Button disabled={!stripe} variant="contained" onClick={(e) => handleSubmit(e)}
      sx={{
        marginTop: "10px",
        backgroundImage: "linear-gradient(to right, #169ECB, #7C669E,#DD5373,#E5A572 )",
        borderRadius: "10px",
        padding: "5px 32px"
      }}>
      {t('gems.pay')}
    </Button>
    <DynamicDialog open={openOk} close={handleCloseOk} text={t('dynamicdialog.gems.bought')} />
      <DynamicDialog open={openError} close={handleCloseError} text={t('dynamicdialog.gems.error')} />
  </form>

Where I have a CardElement to save the Card information.

I execute paymentIntent.create() in my backend and it's doing correctly, this I can see in dashboard.stripe:

I'm trying to confirm the payment like this:

const { error, paymentMethod: {status} } = await stripe.confirmCardPayment(response.paymentIntent.client_secret, {
        payment_method: {
          card: elements.getElement(CardElement)
        }
      })

But I'm receiving this error when this function was executed:

POST https://api.stripe.com/v1/payment_intents/pi_3LMoCCBcC6w1iPgO19pOU6tb/confirm 404

If I do this API call from Postman to get the completely response, I recieve this message:

"message": "You cannot confirm this PaymentIntent because it's missing a payment method. You can either update the PaymentIntent with a payment method and then confirm it again, or confirm it again directly with a payment method."

The problem that I'm having if I'm not misreading is when I'm calling the function elements.getElement(CardElement) to put it inside the function stripe.confirmCardPayment() showed before.

I'm not sure if I do a console.log(element.getElement(CardElement), what is printing is this: console log of CardElement

I will be grateful if someone can help me with that, if you need more information or more part of my code I will publish what you need.

Thank you

Edit:

CreditCard.js

import {  Elements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
import { Avatar, styled } from '@mui/material';
import stripeApi from '../utils/api/pay'
import { useLocalStorage } from '../utils/useLocalStorage';
import CheckoutForm from './CheckoutForm'

const stripePromise = loadStripe('pk_test_51KYq9ELsTxDutkYQKQ1rkl6AtHGVEwm0QsawsgcVYaob4ATxUx8I0wVAt5J0R2KQGgsckC6DhIPHdPaILZHfaluV003GIkkZ3O');
// https://www.youtube.com/watch?v=szRNm8mDrNY
export const getServerSideProps = async (e, amount) => {
  const [tokenStorage, setTokenStorage] = useLocalStorage('token', '');
  const paymentIntent = await stripeApi.buyGems(`/payment/checkout`, {paymentMethod: {}, amount: 'amount.amount'}, tokenStorage)
  return {
      paymentIntent: paymentIntent.paymentIntent    
  }
}

export const JewelAvatar = styled(Avatar)(({ theme }) => ({
  width: "32px",
  height: "auto"
}));

export const CreditCardForm = (props) => {
  
  return (
    <>
      <Elements stripe={stripePromise}>
        <CheckoutForm amount={props.gems}/>
        
      </Elements>
    </>
  );
}

export default CreditCardForm;

CheckoutForm.js

import { CardElement, useElements, useStripe, Elements,PaymentElement } from '@stripe/react-stripe-js'
import { loadStripe } from '@stripe/stripe-js';
import axios from "axios";

import { Avatar, styled } from '@mui/material';
import stripeApi from '../utils/api/pay'
import Accordion from '@mui/material/Accordion';
import AccordionSummary from '@mui/material/AccordionSummary';
import AccordionDetails from '@mui/material/AccordionDetails';
import Typography from '@mui/material/Typography';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import Button from '@mui/material/Button';
import { useLocalStorage } from '../utils/useLocalStorage';
import { useStateValue } from '../context/StateProvider';
import { actionTypes } from '../context/reducer';
import { useNavigate } from 'react-router-dom';
import DynamicDialog from './DynamicDialog/DynamicDialog';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import Stripe from 'stripe';


 const CheckoutForm = (amount) => {
    const stripe = useStripe();
    const elements = useElements();
    const [tokenStorage, setTokenStorage] = useLocalStorage('token', '');
    const [userStorage, setUserStorage] = useLocalStorage('user', {});
    const [paymentIntentIdStorage, setPaymentIntentIdStorage] = useLocalStorage('paymentIntentId', '')
    const [{user, token}, dispatch] = useStateValue()
    const navigate = useNavigate();
    const {t} = useTranslation()
    const [openOk, setOpenOk] = useState(false);
    const [openError, setOpenError] = useState(false);

    const handleCloseOk = () => {
      setOpenOk(false)
    }
  
    const handleCloseError = () => {
      setOpenError(false)
    }
  
    const handleSubmit = async (e) => {
      e.preventDefault();
      console.log(amount)
      const response = await stripeApi.buyGems(`/payment/checkout`, {amount: amount.amount}, tokenStorage)
        
        
      // });
  
        try {
        const card =  elements.getElement(CardElement)
        
        console.log(card)

          const { error, paymentMethod: {status} } = await stripe.confirmCardPayment(response.paymentIntent.client_secret, {
            payment_method: {
              card: elements.getElement(CardElement)
            }
          })
          
          
          if(error) throw new Error(error.message);
          if(status === 'succeeded'){
            alert('ok')
          } 
          let userEdited = {}
          userEdited = userStorage;
          // userEdited.bits = userEdited.bits + paymentIntent.gems;
          setUserStorage(userEdited)
          dispatch({
            type: actionTypes.SET_USER,
            user: userEdited
          })
          elements.getElement(CardElement).clear();
        } catch (error) {
            console.log(error)
          setOpenError(true)
        }
        
      
    };
  
    
    return (
      <form className="card card-body"  style={{}} onSubmit={handleSubmit} action='@Request.Form["continue_url"]' method="POST" id="payment-form">
        <Accordion>
          <AccordionSummary
            expandIcon={<ExpandMoreIcon />}
            aria-controls="panel1a-content"
            id="panel1a-header"
          >
            <Typography><label htmlFor="card-element">{t('gems.card')}</label></Typography>
          </AccordionSummary>
          <AccordionDetails>
            <CardElement  className="form-control" />
  
          </AccordionDetails>
        </Accordion>
        <Button disabled={!stripe} variant="contained" onClick={(e) => handleSubmit(e)}
          sx={{
            marginTop: "10px",
            backgroundImage: "linear-gradient(to right, #169ECB, #7C669E,#DD5373,#E5A572 )",
            borderRadius: "10px",
            padding: "5px 32px"
          }}>
          {t('gems.pay')}
        </Button>
        <DynamicDialog open={openOk} close={handleCloseOk} text={t('dynamicdialog.gems.bought')} />
          <DynamicDialog open={openError} close={handleCloseError} text={t('dynamicdialog.gems.error')} />
      </form>
    );
  };

  export default CheckoutForm

API Endpoint:

import HttpException from '@/mow/shared/domain/exceptions/http';
import { pool } from '@/mow/shared/infrastructure/database';
import Stripe from 'stripe'

class PaymentRepository {
    public async pay(price: number): Promise<{} | HttpException> {
      try{
        const stripe = new Stripe('sk_test_51KqhuBBcC6w1iPgOgQcQGgIjxd4wlcpjpD7TGMqGfIsJS6SObfbagDlvXRNpbA330kbfJtpdeIKHMu3ol2qZpX9A00o8gk7zoc', {
            apiVersion: '2020-08-27',
          });
        const payment = await stripe.paymentIntents.create({
            amount: price * 100,
            currency: 'EUR',
            description: 'Bits',
  
          });
          let paymentIntent
        
        return payment;
    } catch (error: any){
      console.log(error);
      return new HttpException(500, error.message);
    }
  } 
   
}

export default PaymentRepository;
  • Is `elements` defined in your `CheckoutForm` component (via `useElements`)? Is there a reproduction of this I can try out somewhere? – Jonathan Steele Jul 18 '22 at 09:29
  • Yes I'm using useElements and useStripe in CheckoutForm. And no sorry, I don't have any reproduction but I'm going to update right now the post and put it all my 2 files, and the API endpoint so you will have more information. – Marc Sagués Jul 18 '22 at 10:00

0 Answers0