条纹卡片元素未显示在我的 Rails 应用中

Stripe card element is not showing in my Rails app

我正在创建一个简单的 rails 应用程序,它使用 Stripe 进行卡支付,但是当我 运行 该应用程序时,卡输入字段没有显示。我觉得一切都很好,但由于某种原因没有显示出来。我正在使用 Rails 5.2.4.4,Ruby 2.7。这是我的代码:

/app/views/layout/application.html.erb :

    <!DOCTYPE html>
<html>
  <head>
    <title><%= Rails.configuration.application_name %></title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <%= stylesheet_link_tag 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' %>
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'https://js.stripe.com/v3/', 'data-turbolinks-track': 'reload' %>
    <%= tag :meta, name: "stripe-key:", content: Rails.application.credentials.stripe_publishable_key %>
  </head>

  <body class="<%= yield (:body_class) %>">
    <% if flash[:notice] %>
      <div class="notification is-success global-notification">
        <p class="notice"><%= notice %></p>
      </div>
    <% end %>

    <% if flash[:alert] %>
    <div class="notification is-danger global-notification">
      <p class="alert"><%= alert %></p>
    </div>
    <% end %>

     <nav class="navbar is-light" role="navigation" aria-label="main navigation">
      <div class="navbar-brand">
        <%= link_to root_path, class:"navbar-item" do %>
          <h1 class="title is-5"><%= Rails.configuration.application_name %></h1>
        <% end  %>
        <div class="navbar-burger burger" data-target="navbar">
          <span></span>
          <span></span>
          <span></span>
        </div>
      </div>

      <div id="navbar" class="navbar-menu">
        <div class="navbar-start">
          <% if subscribed? %>
            <div class="navbar-item">
              <%= link_to library_index_path, class: 'navbar-item button is-dark' do %>
                <i class="fa fa-book"></i> &nbsp;&nbsp; My Bookcase
              <% end %>
            </div>
          <% end %>
        </div>
        <div class="navbar-end">
          <div class="navbar-item">
            <% if admin? %>
              <%= link_to 'New Book', new_book_path, class:'button is-dark' %>
            <% end%>
            <div class="field is-grouped">
            <% if user_signed_in? %>

              <div class="navbar-item has-dropdown is-hoverable">
                <%= link_to 'Account', edit_user_registration_path, class: "navbar-link" %>
                <div class="navbar-dropdown is-right">
                  <%= link_to edit_user_registration_path, class:"navbar-item" do %>
                    <%= current_user.name %> <% if admin? %> &nbsp; <span class="tag is-warrning">ADMIN</span> <% end %>
                  <% end %>
                  <%= link_to "Log Out", destroy_user_session_path, method: :delete, class:"navbar-item" %>
                </div>
              </div>
            <% else %>
            <p class="control">
              <%= link_to 'Pricing', pricing_index_path, class: 'navbar-item button is-light' %>
            </p>
            <p class="control">
              <%= link_to "Sign In", new_user_session_path, class:"navbar-item button is-light" %>
            </p>
            <p class="control">
              <%= link_to "Sign up", new_user_registration_path, class:"navbar-item button is-light"%>
            </p>
            <% end %>

          </div>
        </div>
      </div>
    </div>
  </nav>

    <div class="container">
      <%= yield %>
    </div>

  </body>
</html>

/app/views/layout/subscribe.html.erb :

<!DOCTYPE html>
<html>
  <head>
    <title><%= Rails.configuration.application_name %></title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <meta name="viewport" content="width=device-width, initial-scale=1">
    <%= stylesheet_link_tag 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' %>
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'https://js.stripe.com/v3/', 'data-turbolinks-track': 'reload' %>
    <%= tag :meta, name: "stripe-key:", content: Rails.application.credentials.stripe_publishable_key %>
  </head>

  <body class="<%= yield (:body_class) %>">
    <% if flash[:notice] %>
      <div class="notification is-success global-notification">
        <p class="notice"><%= notice %></p>
      </div>
    <% end %>

    <% if flash[:alert] %>
    <div class="notification is-danger global-notification">
      <p class="alert"><%= alert %></p>
    </div>
    <% end %>

     <nav class="navbar is-light" role="navigation" aria-label="main navigation">
      <div class="navbar-brand">
        <div class="navbar-item">
        <h1 class="title is-5"><%= Rails.configuration.application_name %></h1>
          </div>
        </div>
      </nav>

    <div class="container">
      <%= yield %>
    </div>
  </body>

</html>

/app/assets/javascripts/subscriptions.jss

    document.addEventListener("turbolinks:load", function() {
  const publishableKey = document.querySelector("meta[name='stripe-key']").content;
  const stripe = Stripe(publishableKey);

  const elements = stripe.elements({
    fonts: [{
      cssSrc: "https://rsms.me/inter/inter-ui.css"
    }],
    locale: 'auto'
  });

  // Custom styling can be passed to options when creating an Element.
  const style = {
    base: {
      color: "#32325D",
      fontWeight: 500,
      fontFamily: "Inter UI, Open Sans, Segoe UI, sans-serif",
      fontSize: "16px",
      fontSmoothing: "antialiased",

      "::placeholder": {
        color: "#CFD7DF"
      }
    },
    invalid: {
      color: "#E25950"
    }
  };

  // Create an instance of the card Element.
  const card = elements.create('card', { style });

  // Add an instance of the card Element into the `card-element` <div>.


    card.mount('#card-element');

    card.addEventListener('change', ({ error }) => {

      const displayError = document.getElementById('card-errors');
      if (error) {
        displayError.textContent = error.message;
      } else {
        displayError.textContent = '';
      }
    });

    // Create a token or display an error when the form is submitted.
    const form = document.getElementById('payment-form');

    form.addEventListener('submit', async(event) => {
      event.preventDefault();

      const { token, error } = await stripe.createToken(card);

      if (error) {
        // Inform the customer that there was an error.
        const errorElement = document.getElementById('card-errors');
        errorElement.textContent = error.message;
      } else {
        // Send the token to your server.
        stripeTokenHandler(token);
      }
    });

    const stripeTokenHandler = (token) => {
      // Insert the token ID into the form so it gets submitted to the server
      const form = document.getElementById('payment-form');
      const hiddenInput = document.createElement('input');
      hiddenInput.setAttribute('type', 'hidden');
      hiddenInput.setAttribute('name', 'stripeToken');
      hiddenInput.setAttribute('value', token.id);
      form.appendChild(hiddenInput);

      ["type", "last4", "exp_month", "exp_year"].forEach(function(field) {
        addCardField(form, token, field);
      });

      // Submit the form
      form.submit();
    }

    function addCardField(form, token, field) {
      let hiddenInput = document.createElement('input');
      hiddenInput.setAttribute('type', 'hidden');
      hiddenInput.setAttribute('name', "user[card_" + field + "]");
      hiddenInput.setAttribute('value', token.card[field]);
      form.appendChild(hiddenInput);
    }

});

/app/controllers/subscriptions_controller.rb

class SubscriptionsController < ApplicationController
  layout "subscribe"
  before_action :authenticate_user!, except: [:new, :create]

  def new
    if user_signed_in? && current_user.subscribed?
      redirect_to root_path, notice: "You are already a subscriber!"
    end
  end

  def create
    Stripe.api_key = Rails.application.credentials.stripe_api_key

    plan_id = params[:plan_id]
    plan = Stripe::Plan.retrieve(plan_id)
    token = params[:stripeToken]

    product = Stripe::Product.retrieve(Rails.application.credentials.book_library)

    customer = if current_user.stripe_id?
                 Stripe::Customer.retrieve(current_user.stripe_id)
               else
                 Stripe::Customer.create(email: current_user.email, source: token)
               end

    subscription = customer.subscriptions.create(plan: plan.id)

    options = {
      stripe_id: customer.id,
      stripe_subscription_id: subscription.id,
      subscribed: true,
    }

    options.merge!(
      card_last4: params[:user][:card_last4],
      card_exp_month: params[:user][:card_exp_month],
      card_exp_year: params[:user][:card_exp_year],
      card_type: params[:user][:card_type]
    ) if params[:user][:card_last4]

    current_user.update(options)

    redirect_to root_path, notice: "&#x1f389; Your subscription was set up successfully!"
  end

  def destroy
    customer = Stripe::Customer.retrieve(current_user.stripe_id)
    customer.subscriptions.retrieve(current_user.stripe_subscription_id).delete
    current_user.update(stripe_subscription_id: nil)

    redirect_to root_path, notice: "Your subscription has been cancelled."
  end

end

/app/views/subscriptions/new.html.erb

<div class="section">
  <div class="columns is-centered">
    <div class="column is-6 border pa5">
      <h1 class="title is-3">Subscribe</h1>
      <hr />
      <p>Chosen plan: <strong><%= params[:plan] %>
      <hr/>
      <%= form_tag subscriptions_path, id: "payment-form" do |form| %>

        <div class="field">
          <label for="card-element" class="label">Enter credit or debit card</label>

          <div id="card-element">
            <!-- A Stripe element will be intserted here !-->
          </div>

          <div id="card-errors" role="alert"></div>

          <%= hidden_field_tag :plan_id, params[:plan_id] %>

          <button class="button is-fullwidth is-link mt4">Submit</button>
        </div>

      <% end %>
    </div>
  </div>
</div>

控制台:(从 subscriptions.jss 中的第 2 行指向 'content')

Uncaught TypeError: Cannot read property 'content' of null
    at HTMLDocument.<anonymous> (subscriptions.self-16b0862fbc44d0ce1a2c1d499b9e65be153010612892033690c2ee948affcab0.js?body=1:2)
    at Object.e.dispatch (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6)
    at r.notifyApplicationAfterPageLoad (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7)
    at r.pageLoaded (turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:7)
    at turbolinks.self-569ee74eaa15c1e2019317ff770b8769b1ec033a0f572a485f64c82ddc8f989e.js?body=1:6

这是查询选择器错误的问题吗?我注意到您将元标记名称声明为 "stripe-key:"(带有尾随冒号),但选择带有 document.querySelector("meta[name='stripe-key']") 的元素(没有尾随冒号元素名称)