Skip to main content

Stripe Elements Javascript Discussion

General • Asked by Chris Oliver

Great tutorial! Seems that the new elements library is easy to use. I was wondering how would I go about implementing Stripe Elements in a e-commerce site with a React Front-End and a Rails API backend. Any ideas / suggestions ?


The below error is thrown in the console from the code in your repo. It looks like its being called on other pages as well.
```Uncaught Error: The selector you specified (#card-element) applies to no DOM elements that are currently on the page.
Make sure the element exists on the page before calling mount().```

Hi Andrew, your JS is running before the page has finished rendering. You need to move the <script> to below the HTML on the page, or put it in jQuery and wait for the DOM to stop loading.


I have an error where the iframe is not being displayed. Clicking the Submit Payment button shows the Your card number is incomplete error message. I also see the error reported by Andrew. Looking at Stackoverflow there seem to be a number of current issues with Stripe and Turbolinks.


Hi, I'm watching this as part of the Stripe MasterClass.

In subscriptions.js, my 'public_key' variable works. But when I then declare the 'stripe' variable, it doesn't seem to know what 'Stripe' is, because 'Stripe(public_key)' returns undefined. Any hints? Thanks.


Two errors are happening with Turbolinks and Stripe Elements v3 as this tutorial stands. One was mentioned above. I'll post the 2nd one at some point. I'd look at your example application and navigate around a little - you'll see them.

Hey Chris,

Running into a small issue with Rails 5.2 and Content-Security-Policy issues with the stripe elements.

Any suggestions?

I tried poking around config > initializers > content_security_policy.rb but changes such as the following didn't do much.

# Define an application-wide content security policy
# For further information see the following documentation
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
Rails.application.config.content_security_policy do |policy|
  policy.connect_src :self, :https, 'http://localhost:3035', 'ws://localhost:3035' if Rails.env.development?
  # policy.default_src :self, :https
  # policy.font_src    :self, :https, :data
  # policy.img_src     :self, :https, :data
  # policy.object_src  :none
  policy.script_src  :self, :https, 'stripe.com'
  # policy.style_src   :self, :https

  #   # Specify URI for violation reports
  # policy.report_uri '/csp-violation-report-endpoint'
end

Great upgrade to Stripe episodes. Thank You Chris.


Hi Chris - The Bootstrap 4 Gem, specifically @import "bootstrap/forms"; blocks the Stripe card element from dispalying.

Do you know of a work around?

Thanks,
Brian

Simply change

 <div id="card-element">
      <!-- a Stripe Element will be inserted here. -->
   </div>

to

 <div id="card-element" class="form-control">
      <!-- a Stripe Element will be inserted here. -->
  </div>

Afterwards the card element is shown

Thanks René, I should of figured that out.


Hi Chris,

I'm following your Stripe Elements and Stripe Subscriptions tutorials. Currently Stripe Elements is working perfectly, but while following the subscriptions tutorial after I create the customer in the controller. Stripe is receiving 2 requests for my customer creation (3 requests total for getting the source token) and is creating one customer with the card information and one without the card information. Oddly enough both are receiving the email associated with that customer from Devise and I receive the correct one (with card details) in the customer variable. The only major differences I made from the tutorials is I converted the Stripe Elements js to coffee. I tried removing the submit action on the stripeTokenHandler function so it doesn't submit the token to the server. But it still creates one customer without card details.

Here's my code. Any help would be appreciated.

Coffee File

stripeTokenHandler = (token) ->
  # Insert the token ID into the form so it gets submitted to the server
  form = document.getElementById('payment-form')
  hiddenInput = document.createElement('input')
  hiddenInput.setAttribute 'type', 'hidden'
  hiddenInput.setAttribute 'name', 'stripeToken'
  hiddenInput.setAttribute 'value', token.id
  form.appendChild hiddenInput
  [
    'brand'
    'exp_month'
    'exp_year'
    'last4'
  ].forEach (field) ->
    addFieldToForm form, token, field
    return
  console.log token
  # Submit the form
  form.submit()
  return

addFieldToForm = (form, token, field) ->
  hiddenInput = document.createElement('input')
  hiddenInput.setAttribute 'type', 'hidden'
  hiddenInput.setAttribute 'name', 'card_' + field
  hiddenInput.setAttribute 'value', token.card[field]
  form.appendChild hiddenInput
  return

document.addEventListener 'turbolinks:load', ->
  public_key = document.querySelector('meta[name=\'stripe-public-key\']').content
  stripe = Stripe(public_key)
  elements = stripe.elements()
  # Custom styling can be passed to options when creating an Element.
  style = base:
    fontSize: '16px'
    color: '#32325d'
  # Create an instance of the card Element.
  card = elements.create('card', style: style)
  # Add an instance of the card Element into the `card-element` <div>.
  card.mount '#card-element'
  card.addEventListener 'change', (event) ->
    displayError = document.getElementById('card-errors')
    if event.error
      displayError.textContent = event.error.message
    else
      displayError.textContent = ''
    return
  # Create a token or display an error when the form is submitted.
  form = document.getElementById('payment-form')
  form.addEventListener 'submit', (event) ->
    event.preventDefault()
    stripe.createToken(card).then (result) ->
      if result.error
        # Inform the customer that there was an error.
        errorElement = document.getElementById('card-errors')
        errorElement.textContent = result.error.message
      else
        # Send the token to your server.
        stripeTokenHandler result.token
      return
    return
  return

Controller

class ChargesController < ApplicationController
  before_action :authenticate_user!, only: [:show, :new, :create]
  before_action :set_user, only: [:create]

  def show
  end

  def new
  end

  def create
    customer = Stripe::Customer.create({
      source: params[:stripeToken],
      email: @user.email
      })

    raise customer
  end

  private

    def set_user
      @user = current_user
    end

end

View:

<%= form_with url: charges_path, id: 'payment-form' do |form| %>
  <div class="form-row">
    <label for="card-element">
      Credit or debit card
    </label>
    <div id="card-element">
      <!-- A Stripe Element will be inserted here. -->
    </div>

    <!-- Used to display Element errors. -->
    <div id="card-errors" role="alert"></div>
  </div>

  <button>Submit Payment</button>
<% end %>

Any thoughts?

To anyone having this same problem, and since Chris doesn't seem to respond to this video's comments. If you are using form_with to render the form, you must add local: true to it. As form_with automatically uses remote: true to submit via XMLHttpRequest. This should stop your server from double submitting.


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 22,346+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.