简体   繁体   中英

Attach customer details to Stripe Payment Intent

I am migrating from the 'Card Element' to the 'Payment Element' and have got the payments working successfully. I am able to pass in the amount and charge a customer. However, I cannot seem to add any data from my form to the Payment Intent on submission of form.

Once logging the req.body on form submission I realised that the body is empty, although submitting the form with multiple inputs. I have never come across this issue before outside of Stripe, I believe it is the 'checkout.js' file causing this possibly? I am wondering if I will need to split my address inputs into a separate form that needs to be submitted first to be able to extract these values?

Server code:

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

Frontend 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/HBS:

   <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>

Any help would be greatly appreciated, thank you in advance.

Based on your code, your frontend is calling your backend to create the PaymentIntent here:

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

However the user has not filled in the inputs yet (since the page just loaded), and you are not passing any body in your POST request. So it's expected that the req.body is empty.

To fix that, should collect the customer information in a separate form with his shipping details And after that you create the payment_intent with these details https://stripe.com/docs/api/payment_intents/create#create_payment_intent-shipping

and pass the client_secret to the Payment Element in order to complete the payment

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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