mura1
mura1

Reputation: 472

Adding cardholder name in Stripe

So the problem I am facing is this. Here I have created PaymentForm with Stripe. So when I am not entering the input value of CardHolder name, and when I press the Purchase button it should display the <h1>Please enter your cardholder name</h1> but it is not doing it. I just want to create a test of Cardholder name, I know the documentation of Stripe is showing real approaches. Where could the error be located?

Payment form

import React,{useContext, useEffect, useState} from 'react'
import {CardElement, useStripe, useElements } from"@stripe/react-stripe-js"
import { CartContext } from '../../context/cart'

import {  useHistory, Link } from "react-router-dom";



const PaymentForm = () => {
  const { total} = useContext(CartContext)
  const {cart, cartItems}= useContext(CartContext)
  const history = useHistory()
  const {clearCart} = useContext(CartContext)
  const [nameError, setNameError]=useState(null)
  const [name, setName] = React.useState("");
  const [succeeded, setSucceeded] = useState(false);
  const [error, setError] = useState(null);
  const [processing, setProcessing] = useState('');
  const [disabled, setDisabled] = useState(true);
  const [clientSecret, setClientSecret] = useState('');
  const stripe = useStripe();
  const elements = useElements();
  useEffect(() => {
    // Create PaymentIntent as soon as the page loads
    window
      .fetch("http://localhost:5000/create-payment-intent", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({items: [{cart}]})
      })
      .then(res => {
        return res.json();
      })
      .then(data => {
        setClientSecret(data.clientSecret);
      });
  }, [cart]);
  const cardStyle = {
    style: {
      base: {
        color: "#32325d",
        fontFamily: 'Arial, sans-serif',
        fontSmoothing: "antialiased",
        fontSize: "16px",
        "::placeholder": {
          color: "#32325d"
        }
      },
      invalid: {
        color: "#fa755a",
        iconColor: "#fa755a"
      }
    }
  };
  const handleChange = async (event) => {
    setDisabled(event.empty);
    setError(event.error ? event.error.message : "");
  };
  const handleChangeInput = async (event) => {
    setDisabled(event.empty);
    setNameError(event.nameError ? event.nameError.message : "");
    setName(event.target.value)
  };

  const handleSubmit = async ev => {
    ev.preventDefault();
    setProcessing(true);
    const payload = await stripe.confirmCardPayment(clientSecret, {
      payment_method: {
        card: elements.getElement(CardElement)
      }
    });

   
    if (payload.error) {
      setError(`Payment failed ${payload.error.message}`);
      setProcessing(false);
    } else {
      setNameError(null)
      setError(null);     
      setProcessing(false);
      setSucceeded(true)
      clearCart()
    }
  };  
  useEffect(()=>{
    setTimeout(() => {  
    if(succeeded){
      history.push('/')
    }
    }, 3000);
  },[history, succeeded])
 
console.log(name)
 
  return (
    <form id="payment-form" onSubmit={handleSubmit}>
      <h2>Checkout</h2>
      <div className='payment__cont'>
       <label>Cardholder Name </label>
       <input 
        placeholder='Please enter your Cardholder name'
        type="text"
        id="name"
        value={name}
        onChange={handleChangeInput}
         />
      </div>
      <div className="stripe-input">
          <label htmlFor="card-element">
            Credit or Debit Card
            </label>
          <p className="stripe-info">
            Test using this credit card : <span>4242 4242 4242 4242</span>
            <br />
            enter any 5 digits for the zip code
            <br />
            enter any 3 digits for the CVC
          </p>
        </div>
      <CardElement id="card-element" options={cardStyle} onChange={handleChange} />
      <div className='review__order'>
      <h2>Review Order</h2>
      <h4>Total amount to pay ${total}</h4>
      <h4>Total amount of items {cartItems}</h4>
      <button
        className='purchase__button'
        disabled={processing || disabled || succeeded}
        id="submit"
      >     
        <span id="button-text">
          {processing ? (
            <div className="spinner" id="spinner"></div>
          ) : (
            "Complete purchase"
          )}
        </span>
      </button>
      <button className='edit__button'onClick={()=> {history.push('/cart')}}>Edit Items</button>
      </div>
      {error && (
        <div className="card-error" role="alert">
          {error}
        </div>
      )}

       {nameError && (
        <div className="card-error" role="alert">
          <h1>Please enter yor card holder name</h1>
        </div>
      )}
      <p className={succeeded  ? "result-message" : "result-message hidden"}>
        Payment succeeded        
          {''}
          <h1>Redirecting you yo the home</h1>
      </p>
  
    </form>
  );
}



export default PaymentForm

Upvotes: 4

Views: 3654

Answers (2)

I'm not sure what you want to do is possible with Stripe elements as cardholder name isn't actually part of it. You'll need to handle it yourself.

An alternative way of ensuring the name field is entered prior to the card element being pressed would be to force input into cardholder name field before displaying/enabling the Stripe Card Element. This way you can be more certain the the name has been entered (and then you can do what you like with it) before the card element is pressed. You could do this a lot of ways, but for example in your render:

{name.length >= 10 ? ( //Check length of name
<CardElement id="card-element" options={cardStyle} onChange={handleChange} />
) : (
   <span>Please enter your cardholder name.</span> // do nothing if check not passed  
)}

This is a really simple example but it checks the length of name and then if greater than or equals ten characters makes the card element visible. You could instead use your handleChangeInput to set a boolean state (true or false) on button press or something; it would be better to make this more robust.

edit: some clarifications.

Upvotes: 0

cisco
cisco

Reputation: 888

You still need to validate your name input yourself. Stripe doesn't do that for you

Your handleChangeInput handler only fires when you write to your name input, and you're treating the event as if it's fired from a Stripe component, but it's not, so try this:

  // Validates name input only
  const handleChangeInput = async (event) => {
    const { value } = event.target;

    // Disable when value is empty
    setDisabled(!value); 

    // Set the error if value is empty
    setNameError(value ? "" : "Please enter a name");

    // Update form value
    setName(value)
  };

Upvotes: 1

Related Questions