You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在基于React和Stripe的电商网站结算页添加账单及配送地址

React + Stripe 结算页添加账单/配送地址实现方案

核心修改步骤

1. 新增地址相关状态与校验逻辑

在Payment组件原有状态声明后新增以下代码:

// 配送地址状态
const [shippingAddress, setShippingAddress] = useState({
  name: '',
  phone: '',
  street: '',
  city: '',
  province: '',
  zipcode: '',
  country: ''
})
// 账单地址是否与配送地址一致开关
const [billingSameAsShipping, setBillingSameAsShipping] = useState(true)
// 账单地址状态
const [billingAddress, setBillingAddress] = useState({
  name: '',
  phone: '',
  street: '',
  city: '',
  province: '',
  zipcode: '',
  country: ''
})
// 地址必填项校验
const isAddressValid = () => {
  const shippingValid = Object.values(shippingAddress).every(val => val.trim() !== '')
  if (billingSameAsShipping) return shippingValid
  const billingValid = Object.values(billingAddress).every(val => val.trim() !== '')
  return shippingValid && billingValid
}

2. 改造配送地址展示区块为输入表单

找到原有渲染配送地址的<div className="payment__address">区块,替换为以下代码:

<div className="payment__address">
  <p>{user?.email}</p>
  {/* 配送地址输入项 */}
  <input 
    type="text" 
    placeholder="收件人姓名" 
    value={shippingAddress.name}
    onChange={e => setShippingAddress({...shippingAddress, name: e.target.value})}
    required
  />
  <input 
    type="text" 
    placeholder="联系电话" 
    value={shippingAddress.phone}
    onChange={e => setShippingAddress({...shippingAddress, phone: e.target.value})}
    required
  />
  <input 
    type="text" 
    placeholder="街道详细地址" 
    value={shippingAddress.street}
    onChange={e => setShippingAddress({...shippingAddress, street: e.target.value})}
    required
  />
  <div className="address_row">
    <input 
      type="text" 
      placeholder="城市" 
      value={shippingAddress.city}
      onChange={e => setShippingAddress({...shippingAddress, city: e.target.value})}
      required
    />
    <input 
      type="text" 
      placeholder="省份/地区" 
      value={shippingAddress.province}
      onChange={e => setShippingAddress({...shippingAddress, province: e.target.value})}
      required
    />
  </div>
  <div className="address_row">
    <input 
      type="text" 
      placeholder="邮政编码" 
      value={shippingAddress.zipcode}
      onChange={e => setShippingAddress({...shippingAddress, zipcode: e.target.value})}
      required
    />
    <input 
      type="text" 
      placeholder="国家" 
      value={shippingAddress.country}
      onChange={e => setShippingAddress({...shippingAddress, country: e.target.value})}
      required
    />
  </div>

  {/* 账单地址同步开关 */}
  <label className="billing_switch">
    <input 
      type="checkbox" 
      checked={billingSameAsShipping}
      onChange={e => setBillingSameAsShipping(e.target.checked)}
    />
    账单地址与配送地址一致
  </label>

  {/* 不同步时显示账单地址输入项 */}
  {!billingSameAsShipping && (
    <div className="billing_address">
      <h4>账单地址</h4>
      <input 
        type="text" 
        placeholder="账单联系人姓名" 
        value={billingAddress.name}
        onChange={e => setBillingAddress({...billingAddress, name: e.target.value})}
        required
      />
      <input 
        type="text" 
        placeholder="联系电话" 
        value={billingAddress.phone}
        onChange={e => setBillingAddress({...billingAddress, phone: e.target.value})}
        required
      />
      <input 
        type="text" 
        placeholder="街道详细地址" 
        value={billingAddress.street}
        onChange={e => setBillingAddress({...billingAddress, street: e.target.value})}
        required
      />
      <div className="address_row">
        <input 
          type="text" 
          placeholder="城市" 
          value={billingAddress.city}
          onChange={e => setBillingAddress({...billingAddress, city: e.target.value})}
          required
        />
        <input 
          type="text" 
          placeholder="省份/地区" 
          value={billingAddress.province}
          onChange={e => setBillingAddress({...billingAddress, province: e.target.value})}
          required
        />
      </div>
      <div className="address_row">
        <input 
          type="text" 
          placeholder="邮政编码" 
          value={billingAddress.zipcode}
          onChange={e => setBillingAddress({...billingAddress, zipcode: e.target.value})}
          required
        />
        <input 
          type="text" 
          placeholder="国家" 
          value={billingAddress.country}
          onChange={e => setBillingAddress({...billingAddress, country: e.target.value})}
          required
        />
      </div>
    </div>
  )}
</div>

可以自行在Payment.css中添加.address_row、.billing_switch、.billing_address的样式,调整输入框布局与间距。

3. 修改支付提交逻辑

找到handleSubmit方法中调用stripe.confirmCardPayment的部分,修改为以下代码,将地址信息传给Stripe,同时存入Firebase订单:

const handleSubmit = async (event) => {
  event.preventDefault();
  setProcessing(true);

  // 确认最终账单地址
  const finalBillingAddress = billingSameAsShipping ? shippingAddress : billingAddress

  const payload = await stripe
    .confirmCardPayment(clientSecret, {
      payment_method: {
        card: elements.getElement(CardElement),
        // 新增账单地址信息
        billing_details: {
          name: finalBillingAddress.name,
          email: user?.email,
          phone: finalBillingAddress.phone,
          address: {
            line1: finalBillingAddress.street,
            city: finalBillingAddress.city,
            state: finalBillingAddress.province,
            postal_code: finalBillingAddress.zipcode,
            country: finalBillingAddress.country
          }
        }
      },
    })
    .then(({ paymentIntent }) => {
      db.collection("users")
        .doc(user?.uid)
        .collection("orders")
        .doc(paymentIntent.id)
        .set({
          basket: basket,
          amount: paymentIntent.amount,
          created: paymentIntent.created,
          // 新增地址信息持久化
          shippingAddress: shippingAddress,
          billingAddress: finalBillingAddress
        });

      setSucceeded(true);
      setError(null);
      setProcessing(false);

      dispatch({
        type: "EMPTY_BASKET",
      });

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

4. 调整提交按钮禁用逻辑

找到提交按钮的disabled属性,新增地址校验规则:

<button disabled={processing || disabled || succeeded || !isAddressValid()}>
  <span>{processing ? <p>处理中</p> : "立即购买"}</span>
</button>

样式补充参考(可选)

在Payment.css中添加以下代码快速适配布局:

.payment__address input {
  padding: 8px;
  margin: 6px 0;
  width: 100%;
  max-width: 400px;
}
.address_row {
  display: flex;
  gap: 10px;
}
.billing_switch {
  display: block;
  margin: 12px 0;
}
.billing_address {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid #eee;
}

内容的提问来源于stack exchange,提问作者heretolearn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 00:54:03