简体   繁体   中英

Node Stripe Payment Gateway Create Payment Intent Error

When testing my live Stripe integration, I am getting a 404 error when trying to create the payment intent. The following shows up in the browser dev console - 'POST {domain}/create-payment-intent 404'.

When testing locally, everything works perfectly fine. However, when switching out the API keys to the live ones and running on our web server, I am getting this error and it's not able to load the payment element, I am not sure where I'm going wrong, any help/ideas on what I may be forgetting would be greatly appreciated.

*Note: The web server is being run via Windows IIS

Below is my checkout.js code, it is failing on line 'const response = await fetch("/create-payment-intent"'

const stripe = Stripe("key here");

var amount = document.getElementById('amount-input');
var firstName = document.getElementById('firstname-input');
var lastName = document.getElementById('lastname-input');
var email = document.getElementById('email-input');
var phone = document.getElementById('phone-input');
var accountNumber = document.getElementById('accountnumber-input');


var btnProceed = document.getElementById('proceedToPayment');
btnProceed.addEventListener('click', function(e) {
    e.preventDefault();
    createPayment();
});

let elements;

document
  .querySelector("#payment-form")
  .addEventListener("submit", handleSubmit);
  
 var btnProceed = document.getElementById('proceedToPayment');

// Fetches a payment intent and captures the client secret
async function initialize() {
  const response = await fetch("/create-payment-intent", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ 
        amount: parseFloat(amount.value),
        firstName: String(firstName.value),
        lastName: String(lastName.value),
        email: String(email.value),
        phone: String(phone.value),
        accountNumber: String(accountNumber.value),
    }),
  });
  const { clientSecret } = await response.json();

  const appearance = {
    theme: 'stripe',
  };
  elements = stripe.elements({ appearance, clientSecret });

  const paymentElement = elements.create("payment");
  paymentElement.mount("#payment-element");
}

function createPayment(){
    //first validate fields
    if(String(firstName.value) === ""){
        alert("Please enter your first name.");
        return;
    }
    if(String(lastName.value) === ""){
        alert("Please enter your last name.");
        return;
    }
    if(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(String(email.value)) === false){
        alert("Please enter a valid email address.");
        return;
    }
    if(String(phone.value).match(/\d/g).length !== 10 && (String(phone.value).match(/\d/g).length === 11 && String(phone.value).charAt(0) !== "1")){
        alert("Please enter a valid phone number.");
        return;
    }
    if(String(accountNumber.value).length !== 8){
        alert("Please enter a valid account number.");
        return;
    }
    if(String(amount.value) === ""){
        alert("Please enter an amount to pay.");
        return;
    }else if(parseFloat(amount.value) === 0){
        alert("You may not enter 0 for an amount.");
        return;
    }else if(parseFloat(amount.value) > 0 && parseFloat(amount.value) < 5){
        alert("Minimum payment amount accepted is $5.00");
        return;
    }
    //hide basic info form
    hideBasicInfo();
    //unhide payment form
    document.getElementById('payment-form').hidden = '';
    //show amount they will be paying on payment form
    displayAmount(amount.value);
    //create payment form
    initialize();
    checkStatus();

}

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: "https://www.humanserviceagency.org/paymentcomplete.html",
    },
  });

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

function hideBasicInfo(){
    var form = document.getElementById('basicinfo-form');
    form.hidden = 'hidden';
}
function displayAmount(amount){
    var amountFields = document.getElementById('amount-fields');
    var amountDisplay = document.getElementById('paymentAmount');
    amountDisplay.innerHTML = amountDisplay.innerHTML + String(amount);
    amountFields.hidden = '';
}

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

My guess is the page this is on is inside a directory on the live web server. If that's the case the issue is the leading / in this code:

fetch("/create-payment-intent"

The / at the beginning will make this always resolve to the root of your domain, meaning if your domain is example.com this will always point to https://example.com/create-payment-intent , even if your payment page is in a directory.

In other words, if your page is located at https://example.com/checkout and your Payment Intent creation URL is at https://example.com/checkout/create-payment-intent you need to remove to forward slash to make the URL relative to the current page instead of absolute.

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