1

I'm getting an ActiveRecord::RecordNotFound in PaymentsController#create error / Couldn't find Product without an ID

I'm trying to set up Stripe payments, but I get an error after the credit card is submitted. I'm doing this for an Intro to Rails class, and my classmates have the same code but theirs' is working.

It appears that the error is in this line:

@product = Product.find(params[:product_id])

Here is my Payments Controller:

class PaymentsController < ApplicationController

  def new
    @product = @product.payments.new
  end

  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), # 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
        )
        #flash[:success] = "You have successfully paid for your order!"
        UserMailer.order_confirmation_email(@user, @product).deliver_now
    end

    rescue Stripe::CardError => e
      # The card has been 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), notice: "Thank you for your purchase."
  end
end

Here is the console message:

Started POST "/payments/create" for 127.0.0.1 at 2018-12-10 10:49:15 -0500
Processing by PaymentsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"jJhGPS9Su0IuepHt2Eea/hCEhDo3A3fggu6EUjwLDrJphrC8VmNRycUqzyLGiJpcaN3mHOzr224BYsbgbjo38Q==", "stripeToken"=>"tok_1Dfr0PHrTxlTJx3aoOtOh2Z9", "stripeTokenType"=>"card", "stripeEmail"=>"myemail@gmail.com"}
Completed 404 Not Found in 2ms (ActiveRecord: 0.0ms)

routes.rb

Rails.application.routes.draw do
  devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout' }
  resources :users
  resources :products, :invoices, :orders, :users # 5.1 added ", :invoices, :orders, :users"
  resources :users, except: [:index]
  get 'simple_pages/about'
  get 'simple_pages/contact'
  get 'simple_pages/index'
  get 'simple_pages/landing_page'
  post 'simple_pages/thank_you'
  post 'payments/create'
  root 'simple_pages#landing_page'
  mount ActionCable.server => '/cable'


  resources :products do # 5.8 added
    resources :comments
  end

  resources :products do
     resources :payments
  end

  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

payments/_form.html.erb

<%= form_for [@product, @payment] do |f| %>
Hardress
  • 47
  • 6
  • Please add your console output to your question. Specifically, what is the value of `params[:product_id]`? (Please edit your question, don't respond with code in comments.) – jvillian Dec 10 '18 at 15:57
  • Could you tell me how to output that value to the Rails Console? – Hardress Dec 10 '18 at 16:04
  • In your rails console (where your rails server is running), you should see a transaction that begins with `Started POST`. It should include a line that begins `Parameters:`. Just paste the whole thing into your question. – jvillian Dec 10 '18 at 16:10
  • Thank you. I have added the readout – Hardress Dec 10 '18 at 16:15

2 Answers2

2

You are not passing the Product ID :product_id in your parameters. Ideally, this is an issue of routing.

For the request to create a payment for a product, the structure of URL should be POST /products/:product_id/payments. This way, you would not have to explicitly mention the product_id as a hidden field in your form in order to pass it as a parameter in your request.

Following is an example to generate nested routes in your case.

resources :products do
   resources :payments
end

Detailed explanation of changes that you would have to do in order to make it work:

In routes.rb Remove resources :payments and resources :products. These two lines will be replaced by

resources :products do
   resources :payments
end

In app/controllers/payments_controller.rb

def new
  @product = Product.find(params[:product_id])  
  @payment = @product.payments.new
end

In app/views/payments/_form.html.erb

<%= form_for [@product, @payment] do |f| %>

Suppose you have a product with ID=1, in order to add a payment for that product, you can access its payments form at URL /products/1/payments/new.

Zohaib A. Butt
  • 156
  • 1
  • 7
  • Thank you for your response. I'm still not sure how to go about editing the code I have. I'm very new at this, but trying to figure it out. – Hardress Dec 10 '18 at 16:30
  • https://stackoverflow.com/questions/2034700/form-for-with-nested-resources has explained implementation of forms with nested resources quite well, with an example. – Zohaib A. Butt Dec 10 '18 at 16:40
  • I've read through that code and added your example to my routes, but I'm really not sure what else to do to get this working. Sorry I am very inexperienced with this and just want to get it working. – Hardress Dec 10 '18 at 17:28
  • Could you please share what error are you facing after changing the code and share your `routes.rb` file as well? And there is no need to worry about inexperience, we all start from the first step. – Zohaib A. Butt Dec 10 '18 at 17:34
  • Thanks, it's still the same error in the title: ActiveRecord::RecordNotFound in PaymentsController#create, highlighting the @product line. To be clear, I haven't edited anything in the Payments Controller (I'm really not sure what to edit) – Hardress Dec 10 '18 at 17:40
  • I have updated my answer for a much clearer step-wise instructions, that are easier to understand and might take you closer to your solution. – Zohaib A. Butt Dec 10 '18 at 17:52
  • Okay, I have edited and re-pasted the code above. I'm getting a syntax error now, which I'm sure is from adding the code in the wrong place. Also, I didn't have a payments/_form.html.erb file before but I created one and added your recommended code. – Hardress Dec 10 '18 at 18:03
  • Could you please share the error that you are facing? – Zohaib A. Butt Dec 10 '18 at 18:05
  • Yes, the error is: /apps/RailsOne/app/controllers/payments_controller.rb:5: syntax error, unexpected tIVAR, expecting ')' #payment = #product.payments.new ^~~~~~~~ *The ampersands appear to be triggering an error on here so I replaced them with #payment and #product.payments.new – Hardress Dec 10 '18 at 18:10
  • If Product and Payment models are associated, then just write `@product = @product.payments.new` instead of using `#`. Use `@` instead of `#` – Zohaib A. Butt Dec 10 '18 at 18:13
  • I must be doing something wrong, I updated the payments controller file and repasted above, but now I'm back to the original error – Hardress Dec 10 '18 at 18:18
  • Is that where you wanted me to paste in the payments controller? – Hardress Dec 10 '18 at 19:13
1

if you check at your console log, params[:product_id] doesn't exist on the request

Parameters: {"utf8"=>"✓", "authenticity_token"=>"jJhGPS9Su0IuepHt2Eea/hCEhDo3A3fggu6EUjwLDrJphrC8VmNRycUqzyLGiJpcaN3mHOzr224BYsbgbjo38Q==", "stripeToken"=>"tok_1Dfr0PHrTxlTJx3aoOtOh2Z9", "stripeTokenType"=>"card", "stripeEmail"=>"myemail@gmail.com"}

That is your problem. change your request to send the product_id on the params so your lookout will work.

xploshioOn
  • 4,035
  • 2
  • 28
  • 37
  • Thank you. I'm not sure how to change the request to send the product_id on the params. Would I just edit the @product = line? – Hardress Dec 10 '18 at 16:29