Stimulus Discourse

Stripe and stimulus giving 'Cannot read property 'stripe' of undefined'

I’m trying to get Stripe to work using a Stimulus controller in a Rails environment. When I follow the Stripe instructions for using Checkout it works perfectly. When I’m using stimulus with (what I think!) is the same code, I’m getting the following error in the console:

Error: TypeError: Cannot read property 'stripe' of undefined
at payment_controller.js:28

Which seems to be the following line of code:

return this.stripe.redirectToCheckout({ sessionId: session.id });

Here is the full listing of the stimulus code:

import { Controller } from "stimulus"

export default class extends Controller {

    connect() {
        console.log("I am loaded")
        let stripeMeta = document.querySelector('meta[name="stripe-key"]')
        if (stripeMeta === null) { return }

        let stripeKey = stripeMeta.getAttribute("content")
        this.stripe = Stripe(stripeKey)
        this.csrf = document.querySelector("meta[name='csrf-token']").getAttribute("content");
    }

    loadstripe() {
        fetch('/create-checkout-session', {
            method: 'POST',
            headers: {
                'X-CSRF-Token': this.csrf,
            },
        })
        .then(function(response) {
            return response.json();
        })
        .then(function(session) {
            console.log(session.id)
            console.log(this.stripe)
            return this.stripe.redirectToCheckout({ sessionId: session.id });
        })
        .then(function(result) {
            // If `redirectToCheckout` fails due to a browser or network
            // error, you should display the localized error message to your
            // customer using `error.message`.
            if (result.error) {
                alert(result.error.message);
            }
        })
        .catch(function(error) {
            console.error('Error:', error);
        });
    }
}

Which seems to be the same as:

    <script type="text/javascript">
    // Create an instance of the Stripe object with your publishable API key
    var stripeKey = document.querySelector('meta[name="stripe-key"]').getAttribute("content")
    var stripe = Stripe(stripeKey);
    var checkoutButton = document.getElementById('checkout-button');

    checkoutButton.addEventListener('click', function() {
        // Create a new Checkout Session using the server-side endpoint you
        // created in step 3.
        fetch('/create-checkout-session', {
            method: 'POST',
            headers: {
                'X-CSRF-Token': document.querySelector("meta[name='csrf-token']").getAttribute("content"),
            },
        })
            .then(function(response) {
                return response.json();
            })
            .then(function(session) {
                return stripe.redirectToCheckout({ sessionId: session.id });
            })
            .then(function(result) {
                // If `redirectToCheckout` fails due to a browser or network
                // error, you should display the localized error message to your
                // customer using `error.message`.
                if (result.error) {
                    alert(result.error.message);
                }
            })
            .catch(function(error) {
                console.error('Error:', error);
            });
    });
</script>

Here is the code from Rails (which seems to be working):

  def create_checkout_session
    session = Stripe::Checkout::Session.create(
      payment_method_types: ['card'],
      line_items: [{
        price_data: {
          currency: 'aud',
          product_data: {
            name: 'Hamper',
          },
          unit_amount: 10000,
        },
        quantity: 1,
      }],
      mode: 'payment',
      success_url: "https://localhost:5000/payment_success.html",
      cancel_url: "https://localhost:5000/payment_failed.html",
    )
    render json: { id: session.id }
  end

Thanks in advance for any help you can give to shed light on why this isn’t working

Hey @maffsguru, welcome!

This looks to be a scoping issue with JS, not specific to Stimulus. The error is telling you that this is undefined, and that’s because that code is executing in a callback function which changes the scope - it’s no longer executing within the scope of the Stimulus controller.

A very simple fix would be to update loadStripe as such:

loadStripe() {
  const stripe = this.stripe
  // ... etc.
}

And update your function to access that local const rather than this. Your function will be able to access the local const and you should be golden.

Again, this is a JS nuance and not specific to Stimulus. You can read up more on scope and context in JS in this article (and there are plenty more out there).

Hope this helps!

Hey @welearnednothing.

Thanks so much for the welcome and the solution. Works like a charm [even though I don’t understand why at the moment]!!!

I am trying to learn so much at the moment and will definitely take a look at the article you’ve sent. The idea of scopes really confuses me. One day I am going to find a mentor who can teach me all the things I seem to be missing. I can get by, but really feel the foundations need a lot more work! Like contexts in Javascript. No idea! Much better when working with someone.

It really did help. I can’t thank you enough!

Now … onto the next challenge :slight_smile:

1 Like