I've just started using Stripe and have followed their guide on setting up a checkout with Rails
In my case, i'm giving users to ability to upgrade an event listing.
In my event model I have a make_premium method:
def make_premium
self.update_attribute(:premium_event, true)
end
My event_controller's upgrade action is on Based on Stripe's create example, except i've added a repond_to block to take users back to the event they upgraded and also call the make_premium method.
def upgrade
#event = Event.find_by_slug(params[:id])
# Amount in pence
#amount = 299
customer = Stripe::Customer.create(
:email => current_user.email,
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'Premium Event Upgrade',
:currency => 'gbp',
:metadata => {'event_id' => #event.id, 'event_headline' => #event.headline}
)
respond_to do |format|
format.html { redirect_to #event, :notice => 'Event was successfully upgraded.' }
#event.make_premium
end
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to event_path(#event)
end
What I want to know, is a) is the respond to block ok to use here given the rescue that comes after and b) is it ok to call my make_premium method from within this block?
I've played around with using Stripe's webhooks and while the charge.suceeded hook works well, it seems like an extra step I could avoid if the above is sufficient.
Any insight from people more experienced with Stripe would be appreciated.
I'm almost positive that you can have a rescue block anywhere in a method and it will get called if the exception occurs.
The only potential change I would suggest is to add a check to make sure the attribute got updated successfully.
def upgrade
#event = Event.find_by_slug(params[:id])
# Amount in pence
#amount = 299
customer = Stripe::Customer.create(
:email => current_user.email,
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'Premium Event Upgrade',
:currency => 'gbp',
:metadata => {'event_id' => #event.id, 'event_headline' => #event.headline}
)
respond_to do |format|
if #event.make_premium
format.html { redirect_to #event, :notice => 'Event was successfully upgraded.' }
else
// handle error and redirect
end
end
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to event_path(#event)
end
Related
I have a simple checkout with Rails using Stripe.
Based on option selected/button clicked the user will be charged a different amount, change description and listing id e.g.
<%= link_to "Pay To Activate",
new_charge_path(:id => listing.id, :amount => 123, :desc => "Option A"),
class: "btn btn-primary btn-sm", :method=> :get %>
When I send this to the ChargesController I'm getting amount, description and id from the parameters:
http://localhost:3000/charges/new?amount=123&desc=Option+A&id=45
Obviously this is not secure because the user can change the amount in URL.
create action looks as below:
def create
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:source => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => #description,
:currency => 'eur'
)
redirect_to thankyou_path
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
end
How should I changed my code to make it more secure?
Based on experts comment payment process function have to model.
On the payment model I don't know what model actually using for payment, by the way, anything like that
#=> model.rb
def process_payment
customer = Stripe::Customer.create email: email, card: token
Stripe::Charge.create customer: customer.id, amount: 1000, description: 'Premium', currency: 'usd' #=> 1000 means 10 USD
end
Controller
#payment = Payment.new({ email: params[:stripeEmail],
token: params[:stripeToken]
})
begin
#payment.process_payment
#payment.save
redirect_to thankyou_path
rescue Exception => e
flash[:error] = e.message
#payment.destroy
redirect_to new_charge_path
end
I think to create a problem when you try to implement this formula that's why to make best common sense and create a secure payment. I need to say this is not accurate for your use because I don't know your project structure this is just a formula for a secure payment.
Thanks
Hope to help
I have multiple clients relying on my server that processes Stripe charge requests. When a charge is processed, I want to send my client back JSON of whether the charge was successfully created, and if it wasn't, the reasons why.
My server can be viewed here.
The code for my controller is the following:
class ChargesController < ApplicationController
protect_from_forgery
skip_before_action :verify_authenticity_token
def new
end
def create
# Amount in cents
#amount = 500
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:source => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'Rails Stripe customer',
:currency => 'usd'
)
#*WHAT I TRIED DOING THAT DIDN'T WORK*
# respond_to do |format|
# msg = { :status => "ok", :message => "Success!"}
# format.json { render :json => msg }
# end
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
end
end
I am trying to call my RESTful API with the following URL:
curl -XPOST https://murmuring-wave-13313.herokuapp.com/charges.json?stripeToken=tok_*****************&stripeEmail=rsheeler#gmail.com
I'm assuming I need to access some of the metadata, but I'm unsure how to.
Which results in a 500 Response
How can I properly structure my Charges controller in order to return JSON of Stripe's response?
Why doesnt this work?
#*WHAT I TRIED DOING THAT DIDN'T WORK*
respond_to do |format|
msg = { :status => "ok", :message => "Success!"}
format.json { render :json => msg } # don't do msg.to_json
format.html { render :template => "charges/create"}
end
What are the errors in your log?
So I'm smacking myself. What I realized was after you make the Stripe::Charge object, a JSON-serialized Charge object is assigned to it.
Because of this, you can access all the metadata in the Charge instance by simply calling charge.attribute_name. For instance, if it was a valid charge, charge.status would return "succeeded". Because what assigned back to charge is JSON you can simply return render charge if the requested format is JSON.
The working Charge controller looks like the following:
class ChargesController < ApplicationController
protect_from_forgery
skip_before_action :verify_authenticity_token
def new
end
def create
# Amount in cents
#amount = 500
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:source => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'Rails Stripe customer',
:currency => 'usd'
)
# If in test mode, you can stick this here to inspect `charge`
# as long as you've imported byebug in your Gemfile
byebug
respond_to do |format|
format.json { render :json => charge }
format.html { render :template => "charges/create"}
end
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
end
end
For example, if I purposely make the card decline, I get shown the error page. Like so:
Instead of an error page I want to be notified with a flash instead. I have the below code, but why am I not alerted using a flash?
class ChargesController < ApplicationController
def new
end
def create
# Amount in cents
#amount = 100
# Get the credit card details submitted by the form
customer = Stripe::Customer.create(
:email => params[:email],
:source => params[:stripeToken]
)
# Create the charge on Stripe's servers - this will charge the user's card
begin
Stripe::Charge.create(
:amount => #amount,
:currency => 'usd',
:customer => customer.id,
:description => 'Example charge custom form'
)
current_user.subscribed = true
current_user.stripe_id = customer.id
current_user.expiry_date = Date.today + 30.days
current_user.save
flash[:success] = "Thank you for subscribing. Your account has been unlocked."
redirect_to root_path
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to root_path
end
end
end
Can you please try this one, just to see the result.
Don't put the flash or redirection. Use raise or something else just to see where the code get caught.
begin
# Use Stripe's library to make requests...
rescue Stripe::CardError => e
# Since it's a decline, Stripe::CardError will be caught
rescue Stripe::RateLimitError => e
# Too many requests made to the API too quickly
rescue Stripe::InvalidRequestError => e
# Invalid parameters were supplied to Stripe's API
rescue Stripe::AuthenticationError => e
# Authentication with Stripe's API failed
# (maybe you changed API keys recently)
rescue Stripe::APIConnectionError => e
# Network communication with Stripe failed
rescue Stripe::StripeError => e
# Display a very generic error to the user, and maybe send
# yourself an email
rescue => e
# Something else happened, completely unrelated to Stripe
end
I am using stripe as payment gateway (Embedded form).It works fine.
However, I can not display card errors on my website.
Errors displayed in Action controller's error page!
My controller
def process
begin
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:source => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => totalprice, #Amount should be in cents
:description => orderid,
:currency => 'aud'
)
rescue Stripe::CardError => e
flash[:error]= e.message <-------------not working?!
redirect_to root_url
end
showconfirmation
end
I want to display stripe error as flash message in my website. How to fix it?
Thanks.
In your code, you are rescuing from Stripe::CardError, but originally you got an Stripe::InvalidRequestError. So, that's why your code is not being able to rescue from the error.
Invalid request errors arise when your request has invalid parameters. See Stripe API Error reference.
You have to make sure you are sending correct parameters. Or, you can rescue from Stripe::InvalidRequestError based on your need:
begin
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:source => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => totalprice, #Amount should be in cents
:description => orderid,
:currency => 'aud'
)
rescue Stripe::CardError, Stripe::InvalidRequestError => e
flash[:error]= e.message
redirect_to root_url
end
I'm building a small proof of concept with Stripe and Ruby on Rails 3.2. So far I've watched the Railscast on how to implement Stripe in a RoR app and it's working really well.
I've built my app by following RailsCast #288 Billing with Stripe. Now my users can add and edit their credit cards and even register to classes and have their credit card billed upon completion.
Now I've been testing with Stripe's numerous test credit cards and I want to catch as many exceptions when raised. I'm using Stripe's example errors in my Registration model as show here:
class Registration < ActiveRecord::Base
belongs_to :user
belongs_to :session
attr_accessible :session_id, :user_id, :session, :user, :stripe_payment_id
validates :user_id, :uniqueness => {:scope => :session_id}
def save_with_payment(user, stripe_card_token)
if valid?
if user.stripe_customer_id.present?
charge = Stripe::Charge.create(
:customer => user.stripe_customer_id,
:amount => self.session.price.to_i * 100,
:description => "Registration for #{self.session.name} (Id:#{self.session.id})",
:currency => 'cad'
)
else
customer = Stripe::Customer.create(
:email => user.email,
:card => stripe_card_token,
:description => user.name
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => self.session.price.to_i * 100,
:description => "Registration for #{self.session.name} (Id:#{self.session.id})",
:currency => 'cad'
)
user.update_attribute(:stripe_customer_id, customer.id)
end
self.stripe_payment_id = charge.id
save!
end
rescue Stripe::CardError => e
body = e.json_body
err = body[:error]
logger.debug "Status is: #{e.http_status}"
logger.debug "Type is: #{err[:type]}"
logger.debug "Code is: #{err[:code]}"
logger.debug "Param is: #{err[:param]}"
logger.debug "Message is: #{err[:message]}"
rescue Stripe::InvalidRequestError => e
# Invalid parameters were supplied to Stripe's API
rescue Stripe::AuthenticationError => e
# Authentication with Stripe's API failed
# (maybe you changed API keys recently)
rescue Stripe::APIConnectionError => e
# Network communication with Stripe failed
rescue Stripe::StripeError => e
# Display a very generic error to the user, and maybe send
# yourself an email
rescue => e
# Something else happened, completely unrelated to Stripe
end
end
I'm merely rescuing from errors right now and not really taking action after one being raised and ultimately I would like to stop the current class registration from happening and redirect a user with a flash error.
I've read about rescure_from but I'm not sure what is the best way to handle of all the possible Stripe errors. I know can't redirect from the model, how would you experts handle this?
Here's my Registration controller:
class Classroom::RegistrationsController < ApplicationController
before_filter :authenticate_user!
def new
if params[:session_id]
#session = Session.find(params[:session_id])
#registration = Registration.new(user: current_user, session: #session)
else
flash[:error] = "Course session is required"
end
rescue ActiveRecord::RecordNotFound
render file: 'public/404', status: :not_found
end
def create
if params[:session_id]
#session = Session.find(params[:session_id])
#registration = Registration.new(user: current_user, session: #session)
if #registration.save_with_payment(current_user, params[:stripe_card_token])
flash[:notice] = "Course registration saved with success."
logger.debug "Course registration saved with success."
mixpanel.track 'Registered to a session', { :distinct_id => current_user.id,
:id => #session.id,
'Name' => #session.name,
'Description' => #session.description,
'Course' => #session.course.name
}
mixpanel.increment current_user.id, { :'Sessions Registered' => 1}
mixpanel.track_charge(current_user.id, #session.price.to_i)
else
flash[:error] = "There was a problem saving the registration."
logger.debug "There was a problem saving the registration."
end
redirect_to root_path
else
flash[:error] = "Session required."
redirect_to root_path
end
end
end
Thanks for taking the time to respond, much appreciated!
Francis
Have you thought of putting the actually Stripe call in a custom validator?
http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validate
That way you could add errors to the object with something like the following
The logic behind this is you only want to save successful transactions as 'transaction' anyway so why not just put the Stripe charge in the validator.
validate :card_validation
def card_validation
begin
charge = Stripe::Charge.create(
:customer => user.stripe_customer_id,
:amount => self.session.price.to_i * 100,
:description => "Registration for #{self.session.name} (Id:#{self.session.id})",
:currency => 'cad'
)
etc etc
rescue => e
errors.add(:credit_card, e.message)
#Then you might have a model to log the transaction error.
Error.create(charge, customer)
end
end
This way you can handle the errors like any other errors you would get from a entry not saving, instead of giving a blank error message, or having to handle every last error from Stripe.
class Classroom::RegistrationsController < ApplicationController
before_filter :authenticate_user!
def create
if params[:session_id]
#session = Session.find(params[:session_id])
params[:registration][:user] = current_user
params[:registration][:session] = #session
params[:registration][:stripe_card_token] = params[:stripe_card_token]
#registration = Registration.new(params[:registration])
respond_with(#registration) do |format|
if #registration.save
format.html {redirect_to root_path, :notice => "SOMETHING HERE TO TELL THEM SUC"}
else
format.html {render}
end
end
else
respond_with do |format|
format.html {redirect_to root_path, :error => "SOMETHING HERE TO TELL THEM GET SESSION"}
end
end
end
end