0

So I tried to make that passed dynamic total is passed to amount in create payment intent in ruby script and I get 500 error with create payment intent and it says that there is error with fetch create payment intent in payment.js(total is passed correctly)

document.addEventListener('DOMContentLoaded', async () => {
  // Fetch publishable key from the server
  const { publishableKey } = await fetch('/config').then(r => r.json());
  const stripe = Stripe(publishableKey);

  // Fetch the total amount from the DOM
  const totalSpan = document.getElementById("total");
  const total = parseFloat(totalSpan.textContent.replace("$", ""));
  console.log(total)
  
  // Create payment intent on the server
  const { clientSecret } = await fetch("/create-payment-intent", {
    method: "POST",
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ total: total })
  }).then(r => r.json());  

  // Render payment element
  const elements = stripe.elements({ clientSecret });
  const paymentElement = elements.create('payment', {
    fields: {
      billingDetails: {
        name: 'auto',
        email: 'auto',
      }
    }
  });
  paymentElement.mount('#payment-element');

  const nameInput = document.getElementById('name');
  const emailInput = document.getElementById('email');

  const form = document.getElementById('payment-form');
  form.addEventListener('submit', async (e) => {
    e.preventDefault();

    // Get the values from the input fields
    const name = nameInput.value;
    const email = emailInput.value;

    // Confirm the payment and redirect to the return URL
    const { error } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: window.location.origin + '/complete',
        payment_method_data: {
          billing_details: {
            name: name,
            email: email,
          }
        },
      },
    });

    if (error) {
      const messages = document.getElementById('error-messages');
      messages.innerText = error.message;
    }
  });
});
this is my html :

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Stripe Sample</title>
    <meta name="description" content="A demo of Stripe" />

    <link rel="icon" href="favicon.ico" type="image/x-icon" />
    <link rel="stylesheet" href="css/normalize.css" />
    <link rel="stylesheet" href="css/global.css" />
    <script src="https://js.stripe.com/v3/"></script>
    <script src="./payment.js"></script>
  </head>

  <body>   
    <div class="sr-root">
      <div class="sr-main">
        <h1>Pay with a bunch of payment option</h1>

        <form id="payment-form" method="POST">
          <input type="text" name="email" id="email" placeholder="E-Mail" required>
          <input type="text" name="name" id="name" placeholder="Full Name" required>
    
          <div id="payment-element"></div>

          <span name="total" id="total" class="total"></span>   
          <button>Pay</button><!--Make functional-->

          <!--Make total price hidden and that total of this is same as in checkout and that its connected to ruby-->
          <div id="error-messages"></div>
        </form>        
      </div>
    </div>
    <script>
      // Get the value of the "total" parameter from the URL
      const urlParams = new URLSearchParams(window.location.search);
      const total = urlParams.get("total");
    
      // Set the total value in the DOM
      const totalSpan = document.getElementById("total");
      totalSpan.textContent = total;
    
      // Update the form action URL to include the total as a query parameter
      const form = document.getElementById('payment-form');
      form.action = `/create-payment-intent?total=${total}`;
    </script>    
</body>
</html>

, and this is my ruby :

# frozen_string_literal: true
require 'stripe'
require 'sinatra'
require 'dotenv'
require 'mail'
require 'nokogiri'
require 'open-uri'
require 'logger'
require 'json'
enable :sessions  

# Replace if using a different env file or config
Dotenv.load

# For sample support and debugging, not required for production:
Stripe.set_app_info(
  'stripe-samples/<name-of-sample>/[<name-of-integration-type>]',
  version: '0.0.1',
  url: 'https://github.com/stripe-samples'
)
Stripe.api_version = '2020-08-27'
Stripe.api_key = ENV['STRIPE_SECRET_KEY']

set :static, true
set :public_folder, File.join(File.dirname(__FILE__), ENV['STATIC_DIR'])
set :port, 4242

# Define a global array to store the products added to cart
$cart_items = []

# Define a route to add products to the cart
post '/cart' do
  request.body.rewind
  product = JSON.parse(request.body.read)
  $cart_items.push(product)
  content_type :json
  { cart_size: $cart_items.size }.to_json
end

# Define a route to get the cart items
get '/cart' do
  content_type :json
  $cart_items.to_json
end

# Create a new logger instance that logs to a file named 'development.log'
logger = Logger.new('C:\s\starter-ruby\server\logs/development.log')

# Set the logger to be used by Sinatra
set :logger, logger

get '/' do
  logger.info 'Hello World!'
  content_type 'text/html'
  send_file File.join(settings.public_folder, 'index.html')
end

get '/config' do
  content_type 'application/json'
  {
    publishableKey: ENV['STRIPE_PUBLISHABLE_KEY'],
  }.to_json
end    

post '/create-payment-intent' do
  total = data['total'].to_i

  payment_intent = Stripe::PaymentIntent.create({
    amount: total,
    currency: 'usd',
    automatic_payment_methods: { enabled: true }
  })

  { clientSecret: payment_intent.client_secret }.to_json
end      

post '/webhook' do
  # You can use webhooks to receive information about asynchronous payment events.
  # For more about our webhook events check out https://stripe.com/docs/webhooks.
  webhook_secret = ENV['STRIPE_WEBHOOK_SECRET']
  payload = request.body.read
  if !webhook_secret.empty?
    # Retrieve the event by verifying the signature using the raw body and secret if webhook signing is configured.
    sig_header = request.env['HTTP_STRIPE_SIGNATURE']
    event = nil

    begin
      event = Stripe::Webhook.construct_event(
        payload, sig_header, webhook_secret
      )
    rescue JSON::ParserError => e
      # Invalid payload
      status 400
      return
    rescue Stripe::SignatureVerificationError => e
      # Invalid signature
      puts '⚠️  Webhook signature verification failed.'
      status 400
      return
    end
  else
    data = JSON.parse(payload, symbolize_names: true)
    event = Stripe::Event.construct_from(data)
  end

  case event.type
  when 'payment_intent.succeeded'
    puts '  Payment received!'
  end

  content_type 'application/json'
  {
    status: 'success'
  }.to_json
end

I built it with this tutorial : https://youtu.be/OGmEZbLMjOs, I found this but this is with checkout session instead of payment intent https://youtu.be/1-olKBnmC84 and someone suggest me to check this https://stripe.com/docs/products-prices/pricing-models#variable-pricing link but i cant figure it out with my code!

Holger Just
  • 52,918
  • 14
  • 115
  • 123
  • A 500 error is a Ruby issue, so you'll need a Ruby stack trace here to help isolate it. – tadman May 20 '23 at 20:01
  • The context is unclear. A stack trace would help. For example, if the "500 error" is in fact the _status code_ of an _HTTP response_ received from `stripe.com`, that is an Internal Server Error at Stripe, ie. not your fault. Without such context, "500" is just a number. – Jared Beck May 21 '23 at 03:08

1 Answers1

0

Looking at your Ruby code and HTML, I can identify a couple of issues that could be causing the 500 error with the create payment intent.

  1. Missing data variable in /create-payment-intent route: In the /create-payment-intent route, you're referencing data['total'] to retrieve the total amount, but the data variable is not defined. To fix this, you need to parse the request body as JSON to access the total value. Modify the code as follows:

    post '/create-payment-intent' do request_payload = JSON.parse(request.body.read) total = request_payload['total'].to_i

payment_intent = Stripe::PaymentIntent.create({ amount: total, currency: 'usd', automatic_payment_methods: { enabled: true } })

{ clientSecret: payment_intent.client_secret }.to_json end

  1. Incorrect endpoint path in JavaScript: In your JavaScript code, you're making a POST request to /create-payment-intent:

    const { clientSecret } = await fetch("/create-payment-intent", { // ... });

However, in your Ruby code, the actual route is defined as post '/create-payment-intent' do. Make sure the endpoint path matches in both the JavaScript and Ruby code.

After applying these changes, try running your code again to see if the 500 error is resolved. If you encounter any further issues or error messages, please provide more details, and I'll be happy to assist you further.

user1051849
  • 2,307
  • 5
  • 26
  • 43