在 Stripe 中添加持卡人姓名

Adding cardholder name in Stripe

所以我面临的问题是这样的。在这里,我使用 Stripe 创建了 PaymentForm。因此,当我没有输入 CardHolder 名称的输入值时,当我按下购买按钮时,它应该显示 <h1>Please enter your cardholder name</h1> 但它没有这样做。我只想创建一个持卡人姓名测试,我知道 Stripe 的文档显示了真正的方法。错误可能在哪里?

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

您仍然需要自己验证 name 输入。 Stripe 不会为你做这些

您的 handleChangeInput 处理程序仅在您写入 name 输入时触发,并且您将事件视为从 Stripe 组件触发,但事实并非如此,所以试试这个:

  // 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)
  };

我不确定你想用 Stripe 元素做什么是可能的,因为持卡人姓名实际上不是其中的一部分。你需要自己处理。

确保在按下卡片元素之前输入姓名字段的另一种方法是在 displaying/enabling 条纹卡片元素之前强制输入持卡人姓名字段。通过这种方式,您可以更加确定在按下卡片元素之前已经输入了名称(然后您可以用它做任何您想做的事)。您可以通过多种方式执行此操作,但例如在您的渲染中:

{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  
)}

这是一个非常简单的示例,但它会检查名称的长度,然后如果大于或等于十个字符,则使卡片元素可见。您可以改为使用 handleChangeInput 在按下按钮或其他东西时设置布尔状态(true 或 false);最好让它更健壮。

编辑:一些说明。