繁体   English   中英

将客户详细信息附加到 Stripe Payment Intent

[英]Attach customer details to Stripe Payment Intent

我正在从“卡元素”迁移到“支付元素”,并且付款成功。 我能够传递金额并向客户收费。 但是,我似乎无法在提交表单时将表单中的任何数据添加到付款意图中。

在表单提交中记录 req.body 后,我意识到主体是空的,尽管提交了具有多个输入的表单。 在 Stripe 之外我从来没有遇到过这个问题,我相信这可能是 'checkout.js' 文件造成的? 我想知道我是否需要将我的地址输入拆分成一个单独的表单,需要先提交才能提取这些值?

服务器代码:

router.post("/create-payment-intent", async (req, res) => {
    if (req.session.cart.products.length > 0) {
        // Create a PaymentIntent with the order amount and currency
        const paymentIntent = await stripe.paymentIntents.create({
        amount: req.session.cart.totalPrice * 100,
        currency: "aud",
        payment_method_types: ['card', 'afterpay_clearpay'],
        receipt_email: req.body.email,
        shipping: {
            address: {
                city: req.body.city,
                line1: req.body.line1,
                line2: req.body.line2,
                postal_code: req.body.postal_code,
                state: req.body.state
            },
            name: req.body.name
        }
        })
        req.session.clientId = paymentIntent.id

        res.send({
        clientSecret: paymentIntent.client_secret
        })
    } else {
        res.redirect('/shop')
    }
})

前端JS:

// This is your test publishable API key.
const stripe = Stripe("pk_test_123");
// The items the customer wants to buy


let elements;
initialize();
checkStatus();
document
  .querySelector("#payment-form")
  .addEventListener("submit", handleSubmit);
// Fetches a payment intent and captures the client secret
async function initialize() {
  const response = await fetch("/cart/create-payment-intent", {
    method: "POST",
    headers: { "Content-Type": "application/json" }
  });
  console.log(response)
  const { clientSecret } = await response.json();
  const appearance = {
    theme: 'night',
    rules: {
      '.Tab': {
        // border: '1px solid #E0E6EB',
        // boxShadow: '0px 1px 1px rgba(0, 0, 0, 0.03), 0px 3px 6px rgba(18, 42, 66, 0.02)',
        backgroundColor: '#ccc',
        fontFamily: "'Montserrat', sans-serif"
      },
      '.TabLabel': {
        color: 'black',
        fontFamily: "'Montserrat', sans-serif"
      },
      '.Label': {
        color: '#edf0f1',
        fontFamily: "'Montserrat', sans-serif",
        fontSize: '15px',
      },
      '.Input': {
        backgroundColor: '#edf0f1',
        color: 'black'
      },
      '.Input::placeholder': {
        backgroundColor: '#grey'
      },
      '.Dropdown': {
        color: 'grey',
        backgroundColor: 'red'
      },
      '.DropdownItem  ': {
        color: 'grey',
        backgroundColor: 'red'
      },
      '.DropdownItem:active': {
        color: 'grey',
        backgroundColor: 'red'
      }
    }
  };
  elements = stripe.elements({ appearance, clientSecret });
  const paymentElement = elements.create("payment");
  paymentElement.mount("#payment-element");
}
async function handleSubmit(e) {
  e.preventDefault();
  setLoading(true);
  const { error } = await stripe.confirmPayment({
    elements,
    confirmParams: {
      // Make sure to change this to your payment completion page
      return_url: "http://localhost:3000/cart/success",
      shipping: {

      }
    },
  });
  // This point will only be reached if there is an immediate error when
  // confirming the payment. Otherwise, your customer will be redirected to
  // your `return_url`. For some payment methods like iDEAL, your customer will
  // be redirected to an intermediate site first to authorize the payment, then
  // redirected to the `return_url`.
  if (error.type === "card_error" || error.type === "validation_error") {
    showMessage(error.message);
  } else {
    showMessage("An unexpected error occurred.");
  }
  setLoading(false);
}
// Fetches the payment intent status after payment submission
async function checkStatus() {
  const clientSecret = new URLSearchParams(window.location.search).get(
    "payment_intent_client_secret"
  );
  if (!clientSecret) {
    return;
  }
  const { paymentIntent } = await stripe.retrievePaymentIntent(clientSecret);
  switch (paymentIntent.status) {
    case "succeeded":
      showMessage("Payment succeeded!");
      break;
    case "processing":
      showMessage("Your payment is processing.");
      break;
    case "requires_payment_method":
      showMessage("Your payment was not successful, please try again.");
      break;
    default:
      showMessage("Something went wrong.");
      break;
  }
}
// ------- UI helpers -------
function showMessage(messageText) {
  const messageContainer = document.querySelector("#payment-message");
  messageContainer.classList.remove("hidden");
  messageContainer.textContent = messageText;
  setTimeout(function () {
    messageContainer.classList.add("hidden");
    messageText.textContent = "";
  }, 4000);
}
// Show a spinner on payment submission
function setLoading(isLoading) {
  if (isLoading) {
    // Disable the button and show a spinner
    document.querySelector("#submit").disabled = true;
    document.querySelector("#spinner").classList.remove("hidden");
    document.querySelector("#button-text").classList.add("hidden");
  } else {
    document.querySelector("#submit").disabled = false;
    document.querySelector("#spinner").classList.add("hidden");
    document.querySelector("#button-text").classList.remove("hidden");
  }
}

HTML/哈佛商学院:

   <form action="/cart/create-payment-intent" method="POST" id="payment-form">
        <div class="flex_center">
        <h2 class="padded_p bold price_header">Order Total AUD </h2><h2 class="price_header yellow padded_p">${{session.cart.totalPrice}}.00</h2>
        </div>
        <div class="row">
          <div class="col-50">
            <h3>Shipping Address</h3>
            <br>
            <label for="fname"><i class="fa fa-user"></i> Full Name</label>
            <input type="text" id="fname" name="name" placeholder="John M. Doe">

            <label for="email"><i class="fa fa-envelope"></i> Email</label>
            <input type="text" id="email" name="email" placeholder="john@example.com">

            <label for="adr"><i class="fa fa-address-card-o"></i> Address</label>
            <input type="text" id="adr" name="line1" placeholder="542 W. 15th Street">

            <label for="city"><i class="fa fa-institution"></i> City</label>
            <input type="text" id="city" name="city" placeholder="New York">

            <div class="row">
              <div class="col-50">
                <label for="state">State</label>
                <input type="text" id="state" name="state" placeholder="NY">
              </div>
              <div class="col-50">
                <label for="zip">Zip</label>
                <input type="text" id="zip" name="postal_code" placeholder="10001">
              </div>
            </div>
          </div>
          <div class="col-50">
            <h3>Payment</h3>
            <div id="payment-element">
          </div>
          </div>     
        </div>
        <button id="submit">
        <div class="spinner hidden" id="spinner"></div>
        <span id="button-text" class="bold">Pay now</span>
      </button>
      </form>

任何帮助将不胜感激,在此先感谢您。

根据您的代码,您的前端正在调用您的后端在此处创建 PaymentIntent:

const response = await fetch("/cart/create-payment-intent", { method: "POST", headers: { "Content-Type": "application/json" } });

但是,用户尚未填写输入(因为页面刚刚加载),并且您没有在 POST 请求中传递任何正文。 所以预计req.body是空的。

要解决此问题,应使用单独的表格收集客户信息及其运输详细信息,然后使用这些详细信息创建 payment_intent https://stripe.com/docs/api/payment_intents/create#create_payment_intent-shipping

并将 client_secret 传递给支付元素以完成支付

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM