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!

2 Likes

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