Search code examples
ruby-on-railsdevisestripe-payments

How to pass user.id from one controller to another "undefined method `id' for nil:NilClass"


So I'm building an e-commerce platform that processes payments via Stripe. Once this has been successfully processed I want to create an Order entry for page admins to see on an Orders page.

Stripe successfully processes payment, Then Order is created by adding an entry that captures:

  • @product.id
  • @user.id
  • @product.price

Product and Price are being pulled successfully, but unfortunately @user.id isn't being pulled into the record. I get the following error : "undefined method `id' for nil:NilClass".

** Payments Controller **

class PaymentsController < ApplicationController
  def create
    token = params[:stripeToken]
    @product = Product.find(params[:product_id])
    @user = current_user
    # Create the charge on Stripe's servers - this will charge the user's card
    begin
      charge = Stripe::Charge.create(
        :amount => (@product.price*100).to_i, # amount in cents, again
        :currency => "usd",
        :source => token,
        :description => params[:stripeEmail]
      )

      if charge.paid
        Order.create!(          
          product_id: @product.id, 
          user_id: @user_id, 
          total: @product.price)
      end

      flash[:success] = "Payment processed successfully"
    rescue Stripe::CardError => e 
      #Card was declined
      body = e.json_body
      err = body[:error]
      flash[:error] = "Unfortunately, there was an error processing your payment: #{err[:message]}"
    end
    redirect_to product_path(@product)
  end
end

** orders Controller**

class OrdersController < ApplicationController
  protect_from_forgery with: :null_session

  def index
  end

  def show 
  end

  def create
  end

  def destroy
  end
end

** Show page where I am rendering the payments partial (via Stripe)**

<div class="col-md-6 center text-center">
  <div class="container">
    <div class="header-spacing">
      <%= image_tag(@product.image_url, class:" img-responsive center") %>
    </div>

    <h2>
      <%= @product.name %>
    </h2>

    <h4>
      <%= @product.desc %>
      <%= @product.style %>
      <strong>$<%= '%.2f' % @product.price %></strong>
    </h4>

    <%= form_tag("/payments/create", remote: true) do %>
      <%= render partial: "shared/stripe_checkout_button" %>
      <%= hidden_field_tag(:product_id, @product.id) %>
    <% end %>
  </div>
</div>

** My OrdersController **

class OrdersController < ApplicationController
  # before_filter :authenticate_user!
  protect_from_forgery with: :null_session

  def index
  end

  def show 
  end

  def create
    @order = Order.create(order_params)
    respond_with @order
  end

  def destroy
  end

  private

  def order_params
    params.require(:order).permit(:product_id, :user_id, :total)
  end
end

Now I've already considered adding:

<%= hidden_field_tag(:user_id, @user.id) %>

But I get the exact same error.

Any help would be greatly appreciated.

UPDATE

It turned out the AJAX request I was making didn't carry the CSRF token. For that reason, Rails was killing my session. I added skip_before_filter :verify_authenticity_token in my paymentsController and all is well now.

But I would still like to know how to fix/why this is happening, if anyone has any info

UPDATE 2

Now that I understand the problem better, I've found an older post dealing with the issue, might be helpful to others.

Rails simple form gives InvalidAuthenticityToken error


Solution

  • Essentially form_tag's do not automatically generate CSRF tokens, unless explicitly told to do so, however neither do form_for tag's, as they chopped that in Rails 4.

    I simply resolved the issue by adding a hidden input in my form:

    <%= form_tag("/payments/create", remote: true) do %>
       <%= render partial: "shared/stripe_checkout_button" %>
       <%= hidden_field_tag(:product_id, @product.id) %>
       <input type="hidden" value="<%= form_authenticity_token() %>"name="authenticity_token"/>
    <% end %>
    

    The main reason they chopped automatic generation of CSRF tokens has to do with people fragment-caching because the authenticity token would be wrong on subsequent requests when the form was pulled from the cache.