so I just noticed I had a small problem. I'm using Stripe as my payment processor, and what happened was quite simple. I had a bug in my code which didn't allow me to save the customer_id_token and change a boolean called subscription active.
However the payment still went through (confirmed in stripe logs). Thus it's obvious I don't have proper validations in place as this should never happen. Here's what my code looks like now:
def create
customer = Stripe::Customer.create(
:email => current_user.email,
:plan => params[:plan_id],
:card => params[:stripeToken]
)
current_user.update_attributes!(
:stripe_customer_token => customer.id,
:subscription_active => true,
:plan_id => params[:plan_id]
)
redirect_to root_url, notice: 'Successfully subscribed'
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to charges_path
end
I can see that they are obviously not really connected with one another, and my question is how can I turn this into something that if the customer_token is not saved it won't go through with the payment either?
Okay,
I'm going to take a shot at this, problem is I do not know how Stripe works, so I cannot guarantee this to work.
I just want to demonstrate the usage of transactions:
def create
ActiveRecord::Base.transaction do
customer = Stripe::Customer.create(
email: current_user.email,
plan: params[:plan_id],
card: params[:stripeToken]
)
current_user.update_attributes!(
stripe_customer_token: customer.id,
subscription_active: true,
plan_id: params[:plan_id]
)
end
redirect_to root_url, notice: 'Successfully subscribed'
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to charges_path
end
What I would definitely add to your current_user model, is a validation to check that the token cannot be null:
class User < ActiveRecord::Base
validates :stripe_customer_token, presence: true
end
That way the update fails when the token is not provided, rolling back the transaction. Although with this it should also work if the first call fails.
However this will only work if this Stripe::Customer supports transactions. I do not know or gurantee this to be the solution.
If the Customer call still goes through and does not validate whether the payment is valid, then there's not much you can do.
Related
So my Plan says Stripe::InvalidRequestError at /orders/26/payments
No such plan: The title of my plan.
This code should check if the the plan already exists and if not create it and subscribe the user to it. I thought this worked because it was working for the case where I already had a plan with the same ID and it said "Plan already exists". How can I prevent this Error from happening?
This is my code:
class PaymentsController < ApplicationController
before_action :set_order
def new
end
def create
#user = current_user
customer = Stripe::Customer.create(
source: params[:stripeToken],
email: params[:stripeEmail],
)
# Storing the customer.id in the customer_id field of user
#user.customer_id = customer.id
#plan = Stripe::Plan.retrieve(#order.service.title)
unless #plan
plan = Stripe::Plan.create(
:name => #order.service.title,
:id => #order.service.title,
:interval => "month",
:currency => #order.amount.currency,
:amount => #order.amount_pennies,
)
else
subscription = Stripe::Subscription.create(
:customer => #user.customer_id,
:plan => #order.service.title
)
end
#order.update(payment: plan.to_json, state: 'paid')
redirect_to order_path(#order)
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_order_payment_path(#order)
end
private
def set_order
#order = Order.where(state: 'pending').find(params[:order_id])
end
end
The documentation says that if you try to retrieve a plan that does not exist, it will raise an error. So you just need to catch the error:
begin
#plan = Stripe::Plan.retrieve(#order.service.title)
rescue
#plan = Stripe::Plan.create(...)
end
Little bit improved version. It's sad that there is no way to check if plan exists and you have to rely on exception swallowing it. Here is my version, it tries to retrieve the plan, if error is 404, it creates the plan. Otherwise, lets exception to pop up. So it won't swallow all of the exceptions, which is important IMO when you work with finance API.
def retrieve_or_create_plan(id)
begin
Stripe::Plan.retrieve(id)
rescue Stripe::InvalidRequestError => e
if e.response.http_status == 404
Stripe::Plan.create(
name: 'Your plan name',
id: id,
interval: :month,
currency: :usd,
amount: 100
)
else
raise e
end
end
end
I have a rails app where Stripe is set up similar to this documentation. When I hit the "Pay With Card" button and enter the test card information it says it's successful, then gives me an error message saying:
No route matches [POST] "/charges/new"
Even though my routes are like this:
resources :charges
get 'charges/shipping'
get 'charges/address'
post 'charges/update_order'
My charges_controller reads like this:
class ChargesController < ApplicationController
...
def new
#order = current_order
end
def create
# Amount in cents
#amount = current_order.total * 100
#user = current_user
#order = current_order
if #user.stripe_customer_id
customer = #user.stripe_customer_id
else
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:source => params[:stripeToken]
)
#user.update_attributes(stripe_customer_id: customer.id)
end
#order.update_attributes(order_status_id: 2)
#order.update_attributes(date_placed: DateTime.now)
# OrderMailer.order_placed(#order.user, #order).deliver_now
session[:order_id] = nil
charge = Stripe::Charge.create(
:customer => #user.stripe_customer_id,
:amount => #amount.to_i,
:description => 'Rails Stripe customer',
:currency => 'usd'
)
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
end
private
def order_params
params.permit(:subtotal, :tax, :shipping, :total, :order_status_id, :user_id, :date_placed, :shipping_choice)
end
end
If I manually insert a post "charges/new" into the routes.rb it says it's successful, but it redirects to the same page (indicating a Stripe::CardError) and no charge registers in the stripe dashboard.
Can anyone see where I'm going wrong?
It looks like you're not permitting the 'stripeToken' parameter in your order_params. Because of camel-case that might be a little fiddly, but make sure you're allowing that param with something like:
params.permit( :"stripeToken" …
Without this, that parameter is null, and would give you a Stripe failure.
Without seeing your client side code, I can't be sure - but it sounds like your client side code is trying to POST to /charges/new - which doesn't exist - so it's failing. I'd suggest you update your client side code to POST to the route you want to have handle that, and then continue debugging from there.
The problem is this part here in your controller:
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
You should remove redirect_to new_charge_path, and simply just let Stripe render the error message. No need to redirect upon error. Stripe takes care of the error-handling for you (which is one of the things making Stripe great).
Currently, your redirect_to is being treated as the path after each successful purchase. Not just on errors.
Update
Upon further looking at your code. You don´t seem to actually have a redirect_to after each successful purchase. So regardless, you should remove the redirect_to new_charge_path because it´s currently arbitrary to => e. It might look like it has something to do with the Error handiling, cause it's below that block, but in fact it doesn't. You should instead add redirect_to successful_path or render :success (for instance) right above the rescue Stripe::CardError =>. And then let Stripe handle (or rescue) failed attempts to make a purchase, as explained before.
Try to restart your server to reload the routes. Run rake routes | grep charges to verify that you see POST /charges/new.
Wait, I know why. You are supposed to GET /charges/new and POST to /charges. Show us your view code, specifically the form submission URL address.
See http://guides.rubyonrails.org/routing.html#crud-verbs-and-actions
In the end I had to change the code on the form to charge_path instead of new_charge_path so it would hit the create action. Even though this was different from the Stripe documentation it ended up working.
Trying to implement Stripe into my app, but my understanding of Ruby/Rails' syntax is, while functional, still very basic. How should I express a default amount? I'm being prompted to "create a Amount class and its default class method, which should return the number you wish to charge, in pennies. One such value might be 15_00, for fifteen dollars." Thanks in advance for the help!
class ChargesController < ApplicationController
def create
# Creates a Stripe Customer object, for associating
# with the charge
customer = Stripe::Customer.create(
email: current_user.email,
card: params[:stripeToken]
)
# Where the real magic happens
charge = Stripe::Charge.create(
customer: customer.id, # Note -- this is NOT the user_id in your app
amount: Amount.default,
description: "BigMoney Membership - #{current_user.email}",
currency: 'usd'
)
flash[:success] = "Thanks for all the money, #{current_user.email}! Feel free to pay me again."
redirect_to user_path(current_user) # or wherever
# Stripe will send back CardErrors, with friendly messages
# when something goes wrong.
# This `rescue block` catches and displays those errors.
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
end
def new
#stripe_btn_data = {
key: "#{ Rails.configuration.stripe[:publishable_key] }",
description: "BigMoney Membership - #{current_user.name}",
amount: Amount.default
}
end
end
I am integrating Stripe with Devise. I want to sign a user up with stripe and store that Stripe ID before the user is saved. The method is completing before the API call return the Customer hash. How can I fix this?
class User < ActiveRecord::Base
validates_presence_of :stripe_id
before_validation :create_stripe_customer
def create_stripe_customer
customer = Stripe::Customer.create(
:email => email,
:card => stripe_card_token
)
self.stripe_id = customer.id
end
end
When I inspect the User, stripe_id is nil, and validation fails.
The best way to achieve this is to wrap it with a protective block using Active Record Transaction.
Transactions are protective blocks where SQL statements are only permanent if they can all succeed as one atomic action.
begin
#user = User.new(strong_params)
User.transaction do
#user.save!
customer = Stripe::Customer.create(
:email => email,
:card => stripe_card_token
)
#user.update_attributes!(stripe_id: customer.id)
end
rescue Exception => ex
flash[:danger] = "#{ex}"
render 'new'
end
This will enforce that the statement will be executed together or not at all.
Context:
I am using Stripe checkout to accept one-time payment in rails.
I have a charges controller as shown below.
I was initially using stripe webhook to listen to charge.succeeded, but running into some issues due to the async nature of webhooks.
My I have moved the business logic to the controller.
If the customer charge is a success, then I save the customer and some other details to the db.
My question:
Is this check enough to ensure that a charge is successful ?
if charge["paid"] == true
The Stripe documentation for Stripe::Charge.create states, "
Returns a charge object if the charge succeeded. Raises an error if something goes wrong. A common source of error is an invalid or expired card, or a valid card with insufficient available balance."
My ChargesController:
class ChargesController < ApplicationController
def new
end
def create
# Amount in cents
#amount = 100
temp_job_id = cookies[:temp_job_id]
customer_email = TempJobPost.find_by(id: temp_job_id).company[:email]
customer = Stripe::Customer.create(
:email => customer_email,
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'Rails Stripe customer',
:currency => 'usd',
:metadata => {"job_id"=> temp_job_id}
)
# TODO: charge.paid or charge["paid"]
if charge["paid"] == true
#Save customer to the db
end
# need to test this and refactor this using begin-->rescue--->end
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to charges_path
end
end
Yes, that's all you need to do. If the charge succeeded, Stripe will return a Charge object, and you can check its paid parameter. If the charge failed, we'd throw an exception.
Cheers,
Larry
PS I work on Support at Stripe.