Updating column from being NULL after action is called - ruby-on-rails

Stripe is setup on my app and users can cancel or upgrade their subscription. It communicates perfectly to Stripe. I need help figuring out how I can make the changes communicate with my database.
If a user cancels their subscription it should be marked in the Subscriptions table under a cancelled column. With this in place the user will be restricted access to website if it shows in the database that their subscription was cancelled.
I am not sure how to add this to the cancel subscription action I have setup.
Help with this would be greatly appreciated!
Subscriptions controller:
def new
plan = Plan.find(params[:plan_id])
#subscription = plan.subscriptions.build
if params[:PayerID]
#subscription.paypal_customer_token = params[:PayerID]
#subscription.paypal_payment_token = params[:token]
#subscription.email = #subscription.paypal.checkout_details.email
end
end
def create
#subscription = Subscription.new(params[:subscription])
if #subscription.save_with_payment
redirect_to #subscription, :notice => "Thank you for subscribing!"
else
render :new
end
end
def show
#subscription = Subscription.find(params[:id])
end
def updatesubscription
#user = current_user
#customer = Stripe::Customer.retrieve(#user.subscription.stripe_customer_token)
#customer.update_subscription(:plan => "1", :prorate => true)
current_user.save!
flash.alert = 'Your subscription has been updated!'
redirect_to root_url
end
def cancelsubscription
#user = current_user
#customer = Stripe::Customer.retrieve(#user.subscription.stripe_customer_token)
#customer.cancel_subscription()
current_user.save!
flash.alert = 'Your subscription has been cancelled successfully!'
redirect_to root_url
end
def showcard
#user = current_user
Stripe::Customer.retrieve(#user.subscription.stripe_customer_token).cards.all()
end
def changecard
#user = current_user
#customer = Stripe::Customer.retrieve(#user.subscription.stripe_customer_token)
card = #customer.cards.create({
:card => #user.subscription.stripe_customer_token
})
#customer.default_card = card
#customer.save
end
end
Subscription model:
belongs_to :plan
belongs_to :subscription
belongs_to :user
validates_presence_of :plan_id
validates_presence_of :email
attr_accessor :stripe_card_token, :paypal_payment_token
def save_with_payment
if valid?
if paypal_payment_token.present?
save_with_paypal_payment
else
save_with_stripe_payment
end
end
end
def paypal
PaypalPayment.new(self)
end
def save_with_paypal_payment
response = paypal.make_recurring
self.paypal_recurring_profile_token = response.profile_id
save!
end
def save_with_stripe_payment
customer = Stripe::Customer.create(description: email, plan: plan_id, card: stripe_card_token)
self.stripe_customer_token = customer.id
save!
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
false
end
def payment_provided?
stripe_card_token.present? || paypal_payment_token.present?
end
def suspend_paypal
paypal.suspend
save
end
def reactivate_paypal
paypal.reactivate
save
end
end

The simplest way to do it would be to add extra lines to update the required column.
def cancelsubscription
#user = current_user
#customer = Stripe::Customer.retrieve(#user.subscription.stripe_customer_token)
#customer.cancel_subscription()
current_user.subscription.update_attributes(:cancelled => 1)
current_user.save!
flash.alert = 'Your subscription has been cancelled successfully!'
redirect_to root_url
end
However, ideally all this should happen within a model. From the controller you should just be telling the model to cancel the subscription.
So, this method could become something like:
#subscriptions_controller.rb
def cancelsubscription
current_user.subscription.cancel_stripe_subscription
flash.alert = 'Your subscription has been cancelled successfully!'
redirect_to root_url
end
#subscription.rb model
def cancel_stripe_subscription
customer = Stripe::Customer.retrieve(self.stripe_customer_token)
customer.cancel_subscription()
self.cancelled = 1
self.save!
end

Cant you update the Subscription table via an active_record query? Like this:
updateQuery = ActiveRecord::Base.connection.execute("UPDATE Subscription SET cancelled = 'Cancelled' WHERE user_id = #{#user.id}")
That'd update your subscription table and change the targetted field value to "Cancelled". Its not clear if this is a boolean or not. If it is, then the value should be "TRUE" instead of the "Cancelled" string i put in there.
If subscription succesfully saves, then you execute the query. But you'd have to make sure you put the query between a condition so that it only executes it when the user has cancelled the subscription, not every time its updated.
respond_to do |format|
if #subscription.save
if (something to tell the controller you are cancelling the subscription)
EXECUTE QUERY HERE
end
end
end

Related

Can't figure out how to pass coupon in a URL params on Stripe subscription creation in Rails 5 app

really appreciate the help on this one, already a 3 hours into debugging this. I'm trying to support coupons during Stripe Subscription creation. To do that, all you have to do is pass the couponId you create in Stripe as an option when you create the subscription for a user, eg:
subscription = customer.subscriptions.create(
source: params[:stripeToken],
plan: #plan,
coupon: 'testcoupon' #this works when you hardcode this
)
But I want it to reference a url parameter c_id so I can make it dynamic, like localhost:3000/charges/new?c_id='testcoupon' eg:
subscription = customer.subscriptions.create(
source: params[:stripeToken],
plan: #plan,
coupon: #coupon
)
I've declared strong parameters to allow for it
private
def coupon_params
#strong parameters to allow c_id
params.permit(:c_id)
#coupon = params[:c_id]
end
And have before_action :coupon_params declared in the charges controller.
However, the #coupon is empty when I create the charge for some reason. Does anyone know what I'm doing wrong?
///UPDATE, Here's the entire charges_controller:
class ChargesController < ApplicationController
before_action :authenticate_user!
before_action :amount_to_be_charged
before_action :set_plan
before_action :coupon_params
def new
if current_user.subscribed?
redirect_to root_path, notice: "Not authorized."
end
#coupon = params[:c_id]
puts "def new coupon: #{#coupon}"
end
def thanks
if !current_user.subscribed?
redirect_to new_charge_path
end
end
def create
params.permit(:stripeToken)
#coupon = params[:c_id]
puts "here is the coupon on param check #{#coupon}"
customer = if current_user.stripe_id?
Stripe::Customer.retrieve(current_user.stripe_id)
else
Stripe::Customer.create(email: current_user.email)
end
subscription = customer.subscriptions.create(
source: params[:stripeToken],
plan: #plan,
#coupon: 'test' #this works when hardcoded sdafasdf#dfs.com
coupon: #coupon
)
Rails.logger.debug("Coupon == #{#coupon}")
options = {
stripe_id: customer.id,
stripe_subscription_id: subscription.id,
}
puts current_user.update(options)
current_user.update(options)
redirect_to thanks_path
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
end
private
def set_plan
#plan = ENV['STRIPE_PLAN_ID']
end
def coupon_params
#strong parameters to allow c_id
params.permit(:c_id)
#coupon = params[:c_id]
end
end

Booking Process Issues - Stripe and Rails

OK, I have been trying to find the issue for the past 2 hours.. For some reason my bookings don't come through, not even notifications appear on the page.
When I click BOOK NOW, the page reloads and doesn't give me any errors, so not sure what am I doing wrong.
I'm adding the stripe to the booking path step by step. The BOOK NOW button was working fine on its own before, but now that I aded the card charges to it - it doesn't.
Any help is very welcome! Happy to provide additional information, if needed.
Thank you
Reservations Controller
def create
room = Room.find(params[:room_id])
if current_user.stripe_id.blank?
flash[:alert] = "Please update your payment method."
return redirect_to payment_method_path
#reservation = current_user.reservations.build
#reservation.room = room
#reservation.price = room.price
#reservation.total = room.price * days
if #reservation.Waiting!
if room.Request?
flash[:notice] = "Request sent succesfully"
else
charge(room, #reservation)
end
else
flash[:alert] = "Cannot make a reservation"
end
end
redirect_to room
end
def your_trips
#rooms = current_user.rooms
end
def aprove
charge(#reservation, room, #reservation)
redirect_to your_trips_path
end
def decline
#reservation.Declined!
redirect_to your_trips_path
end
Reservations Controller - private
private
def set_reservation
#reservation = Reservation.find(params[:id])
end
def charge(room, reservation)
if !reservation.user.stripe_id.blank?
customer = Stripe::Customer.retrieve(reservation.user.stripe_id)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => reservation.price,
:description => room.overview,
:currency => "usd"
)
if charge
reservation.Approved!
flash[:notice] = "Reservation created successfully!"
else
reservation.Declined!
flash[:alert] = "Cannot charge with this payment method!"
end
end
rescue Stripe::CardError => e
reservation.declined!
flash[:alert] = e.message
end
After checking current_user.stripe_id.blank? it looks like you are unintentionally returning all requests.
I would try this:
if current_user.stripe_id.blank?
flash[:alert] = "Please update your payment method."
redirect_to payment_method_path and return
end
If your model code depends on the record being saved before calling Waiting!, keep in mind that #reservation = current_user.reservations.build does not save the record.
The rescue block in your charge method also appears to be out of scope. That could be refactored into a begin-rescue-end block within the first if.
And if you aren't seeing notifications on the page, make sure your layout displays :notice and :alert somewhere.

Nested form validation rails 3.2

I have a job and user(devise) form in the same view. When I am trying to submit with errors in the user fields it gives me an exception page with the validation messages. Submitting errors in the job fields works fine!
job_controller.rb
def new
#job = Job.new
if !current_user
#job.user = User.new
end
respond_to do |format|
format.html # new.html.erb
end
end
def create
#types = Type.all
#categories = Category.all
#job = Job.new(params[:job])
#if not logged in creates a user and sign in
if !current_user
#user = User.new(params[:job][:user_attributes])
else
#user = current_user
end
#job.user_id = #user.id
respond_to do |format|
if #job.save
if !current_user
sign_in(:user, #user)
end
format.html { redirect_to #job }
else
format.html { render action: "new" }
end
end
end
job.rb
attr_accessible :user_attributes, :description, :name ....
belongs_to :user
accepts_nested_attributes_for :user
Thanks!
That becuase you are calling, #user.save! which will generate an exception. Also doing it this way won't put the job in the same transaction as User. What you want are nested_attributes:
class Job < ActiveRecord::Base
accepts_nested_attributes_for :user
end
If the user is logged in, don't show that part of the form and filter those params.
See more in the Rails documentation here http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
EDIT:
Simplify your controller code, since you're using nested attributes you no longer need to manually create a user.
#if not logged in creates a user and sign in
if !current_user
#user = User.new(params[:job][:user_attributes]) # this is no longer needed
else
#user = current_user
end
#job.user_id = #user.id # this is redundant
To something more like:
# if logged in, manually assign the user (also you may want to reject any user attributes)
#job.user = current_user if current_user

Rails error messages always displayed, even before submit

My Account model:
def save_with_payment
if valid?
customer = Stripe::Customer.create(description: email, plan: plan_id, card: stripe_card_token)
self.stripe_customer_token = customer.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
false
end
My accounts controller:
# GET /accounts/new
# GET /accounts/new.json
def new
#account = Account.new
#company = #account.build_company
#user = #company.users.build
if #account.save_with_payment
redirect_to success_path, :notice => "Thank you for subscribing!"
else
render :new
end
respond_to do |format|
format.html # new.html.erb
format.json { render json: #account }
end
end
For some reason (only just started happening), the form always shows the validation errors (with or without submitting first)
why is this?
You execute #account.save_with_payment before submitting (and you don't pass parameters to this method). The code looks strange, usually there are two methods new and create, in first one you just find #account and pass it to view for form, in second one you save #account.
def new
#account = Account.new
end
def create
#account = Account.new(params[:account])
if #account.save
redirect_to #account
else
render :action => 'new'
end
end

Rails 3: Instance variable not working as charge amount for Stripe?

I'm adding Stripe to my Rails 3 app to accept credit card payments, and I'm trying to use an instance variable, "#total", for the amount to be charged to the customers.
Here's what I currently have in my model:
class Video < ActiveRecord::Base
attr_accessible :age, :category, :description, :ethnicity, :gender, :genre, :interest, :length, :panda_video_id, :size, :tagline, :title, :video_id, :stripe_card_token
attr_writer :current_step
attr_accessor :stripe_card_token
serialize :size
serialize :age
serialize :gender
serialize :ethnicity
has_many :audiences, :dependent => :destroy
accepts_nested_attributes_for :audiences, :allow_destroy => true
#validates_presence_of :panda_video_id, :if => lambda { |o| o.current_step == "upload" }
def panda_video
#original_video ||= Panda::Video.find(panda_video_id)
end
def save_with_payment
if valid?
charge = Stripe::Charge.create(
amount: #total,
currency: "usd",
card: stripe_card_token
)
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating charge: #{e.message}"
errors.add :base, "There was a problem with your credit card."
false
end
def current_step
#current_step || steps.first
end
def steps
%w[upload info audience review]
end
def next_step
self.current_step = steps[steps.index(current_step)+1]
end
def previous_step
self.current_step = steps[steps.index(current_step)-1]
end
def first_step?
current_step == steps.first
end
def last_step?
current_step == steps.last
end
def all_valid?
steps.all? do |step|
self.current_step = step
valid?
end
end
end
I contacted Stripe support, and they said that the instance variable should be working here. So, I suspect the problem lies with my controller:
class VideosController < ApplicationController
def index
#videos = Video.all
end
def show
#video = Video.find(params[:id])
#original_video = #video.panda_video
#h264_encoding = #original_video.encodings["h264"]
end
def new
session[:video_params] ||= {}
#video = Video.new(session[:video_params])
#video.current_step = session[:video_step]
end
def create
session[:video_params].deep_merge!(params[:video]) if params[:video]
#Save total value and audience form to session.
#total = session[:video_params]["size"].collect(&:to_i).sum - 10 if session[:video_params]["size"]
#audiences = session[:video_params].slice("size", "gender", "age", "ethnicity").to_json
#video = Video.new(session[:video_params])
#video.current_step = session[:video_step]
if #video.valid?
if params[:back_button]
#video.previous_step
elsif #video.last_step?
#video.save if #video.all_valid? && #video.save_with_payment
else
#video.next_step
end
session[:video_step] = #video.current_step
end
if #video.new_record?
render "new"
else
session[:video_step] = session[:video_params] = nil
flash[:notice] = "Video saved"
redirect_to #video
end
end
def edit
#video = Video.find(params[:id])
end
def update
#video = Video.find(params[:id])
if #video.update_attributes(params[:video])
redirect_to #video, :notice => "Successfully updated video."
else
render :action => 'edit'
end
end
def destroy
#video = Video.find(params[:id])
#video.destroy
redirect_to videos_url, :notice => "Successfully destroyed video."
end
end
My rails app is a multi-step form, where all the data is stored in a session. #total sums up the input values in the audience step of the form, and then that #total number shows up in the final step, where the customer enters their payment info. The #total number currently shows up on this page, but for some reason Stripe can't process it, since I keep getting the following error:
Stripe error while creating charge: Missing required param: amount
What am I doing wrong here?
I guess, controller instance variables are not available in models. Check the solution posted to this question Ruby on Rails - Access controller variable from model.
UPDATE
You don't need to follow the example mentioned in the URL. You can directly pass the #total variable as an argument like this
#model
def save_with_payment(total)
...
charge = Stripe::Charge.create(
amount: total,
currency: "usd",
card: stripe_card_token
)
...
end
# controller
def create
...
if params[:back_button]
#video.previous_step
elsif #video.last_step?
#video.save if #video.all_valid? && #video.save_with_payment(#total)
else
#video.next_step
end
...
end
Im not sure because I cannot see your model code but I suspect this is because you have no attr_accessor set for :total, so attr_accessor :total in the modal and see what happens. unless i have a better idea (see your code) about how your payment process is structured I cant really give a solid answer on the matter. By you can check out http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/ to see if you find something that works for you.
EDIT
I though you were talking about model instance variable, you have to pass the instance variable from the controller to the model. Below is an example.
Controller snippet
...
#video.save if #video.all_valid? && #video.save_with_payment(#total)
...
Model snippet
...
def save_with_payment(total)
if valid?
charge = Stripe::Charge.create(
amount: total,
currency: "usd",
card: stripe_card_token
)
end

Resources