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
Related
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 am using Rails 4.2, and trying to integrate Stripe Checkout (https://stripe.com/docs/checkout/guides/rails) in my rails app and have a scenario I haven't seen outlined anywhere. Note: I tried custom form integration from a number of online resources but couldn't get it to work so opted to give checkout a go.
In my rails app, I have an orders table, and the main thing I'm trying to accomplish is having a form where the user can submit their personal information (non-payment) to place an order. Then, the stripe checkout integration will allow them to pay; however, a record of the order will not be created in the database without a stripe charge being logged. I've been unable to accomplish this with using the separate "charges" controller that stripe suggests, and also tried incorporating the stripe code into my orders controller (see below).
I should note that I HAVE been able to get checkout button to submit to stripe and the charges are processed, but HAVE NOT been able to get a order record to be created in my database.
I have searched far and wide for an answer to this question (currently waiting on response from stripe support). Any suggestions would be much appreciated!
orders_controller.rb
(this is the example where I tried combining the stripe code from the charges controller they suggest into my own orders controller. i'm not sure now what to do after the charge is processed to get it to submit the form)
def create
#order = Order.new(order_params)
customer = Stripe::Customer.create(
:email => 'example#stripe.com',
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => 5000,
:description => 'Rails Stripe customer',
:currency => 'usd'
)
rescue Stripe::CardError => e
flash[:error] = e.message
render 'new'
end
orders/new.html.erb
(I am leaving out code for all the other fields in my form, this just shows my form submit button and the stripe checkout button. Ideally I could combine the actions into one button, or only have the submit go through when the payment successfully processes through stripe)
<%= form_for #order do |f| %>
// lots of form fields
<%= f.submit %>
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
data-description="A month's subscription"
data-amount="500"></script>
Typically you would do...
def create
#order = Order.new(order_params)
charge_error = nil
if #order.valid?
begin
customer = Stripe::Customer.create(
:email => 'example#stripe.com',
:card => params[:stripeToken])
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => 5000,
:description => 'Rails Stripe customer',
:currency => 'usd')
rescue Stripe::CardError => e
charge_error = e.message
end
if charge_error
flash[:error] = charge_error
render :new
else
#order.save
redirect_to (successful page)
end
else
flash[:error] = 'one or more errors in your order'
render :new
end
end
This way the charge isn't made unless the #order is validated, and the #order isn't saved unless the charge is successful.
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
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