I'm using stripe checkout in a Rails 4x app. My goal is to provide monthly subscriptions. I'm receiving the following.
Stripe::InvalidRequestError Cannot use stripeToken more than once. This happens when create_stripe_subscription is called in subscriptions_controller#create.
In spite of this error message, I have customers who have made charges that appear in my stripe dashboard.
I'd like to learn where I'm duplicating this one-time-usage token and understand what stripe is requiring for this subscription.
Here are associated files & images to demonstrate what my implementation efforts are looking like so far.
initializers/stripe.rb:
Rails.configuration.stripe = {
publishable_key: ENV["STRIPE_PUBLISHABLE_KEY"],
secret_key: ENV["STRIPE_SECRET_KEY"]
}
Stripe.api_key = Rails.configuration.stripe[:secret_key]
subscriptions/new (the "small" plan's stripe checkout button)
<script src="https://checkout.stripe.com/checkout.js"
class="stripe-button",
data-key="<%= ENV["STRIPE_PUBLISHABLE_KEY"] %>",
data-email="<%= current_employee.email %>",
data-image="app/assets/images/mascot_favicon.ico",
data-name="Small Group Home",
data-description="Monthly subcription plan",
data-amount="<%= #small_plan.amount %>",
data-id="<%= #small_plan.id %>",
data-label="Subscribe!">
</script>
subscriptions_controller.rb
def create
create_stripe_subscription
if create_stripe_subscription.valid?
AdminMailer.welcome_email(#admin).deliver_now
flash[:success] = "#{ #admin.full_name.pluralize } created!"
redirect_to root_path
else
flash.now[:notice] = "There was a problem with the form"
render :new
end
end
.
.
.
def create_stripe_subscription
plan_id = params[:plan_id]
plan = Stripe::Plan.retrieve(plan_id)
token = params[:stripeToken]
email = params[:stripeEmail]
customer = Stripe::Customer.create(
source: token,
email: email,
plan: plan
)
subscription = Subscription.new
subscription.stripe_card_token = customer.id
same_customer = Stripe::Customer.retrieve(subscription.stripe_card_token)
same_customer.subscriptions.create(plan: params[:plan_id])
end
stipe dash
I figured it out by handling the Stripe::InvalidRequestError in the create_stripe_subscription method like so:
subscriptions_controller.rb
def create_stripe_subscription
.
.
.
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error: #{e.message}"
end
Which led me to a noMethodError from my app. Then error came from this line in my subscriptions_controller's create method.
if create_stripe_subscription.valid?
Removing valid? allowed the create method to finish up, sending the email and redirecting as it should.
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've been trying to convert the following from html.erb to html.haml, but things aren't rendering 100% correctly. Specifically I'm wondering how to correctly write the bit of code within the script tags in Haml.
html.erb:
<%= form_tag charges_path do %>
<h4>So what comes with being a Blocipedia premium member? </h4>
<p>The ability to create your very OWN private wikis of course!</p>
<script class='stripe-button' src="https://checkout.stripe.com/checkout.js" data-key="<%= #stripe_btn_data[:key] %>" data-amount=<%= #stripe_btn_data[:amount] %> data-description="<%= #stripe_btn_data[:description] %>" ></script>
<% end %>
html.haml:
= form_tag charges_path do
%h4 So what comes with being a Blocipedia premium member?
%p The ability to create your very OWN private wikis of course!
%script.stripe-button{"data-key" => #stripe_btn_data[:key], :src => "https://checkout.stripe.com/checkout.js", "data_amount" => #stripe_btn_data[:amount], "data_description" => #stripe_btn_data[:description]}
Before I tried to rewrite my code in Haml, my original html.erb file rendered like so below.
Before Haml (The way my page SHOULD render):
Yet after trying to change to Haml, I was not 100% successful in getting the page to render in exactly the same way
After Haml (notice the data-amount ($15.00) and the data-discription (BigMoney Membership) aren't being displayed)
What is the correct Haml syntax to render this page properly?
Update
I'm trying to grab the data-amount and description from my charges_controller.rb
charges_controller.rb:
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[:notice] = "Thanks for all the money, #{current_user.email}! You now have a premium Blocipedia account! Feel free to pay me again."
current_user.update_attribute(:role, "premium")
redirect_to root_path # 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
def destroy
if current_user.update_attributes(role: "standard")
flash[:notice] = "You now have a standard Blocipedia account. Feel free to upgrade back to premium at anytime!"
redirect_to root_path
else
flash[:error] = "There was an error downgrading your account. Please contact technical support."
redirect_to edit_user_registration_path
end
end
end
amount.rb:
class Amount
def self.default
15_00
end
end
To achieve the same HTML output of ERB using HAML
= form_tag charges_path do
%h4 So what comes with being a Blocipedia premium member?
%p The ability to create your very OWN private wikis of course!
%script.stripe-button{data: {key: #stripe_btn_data[:key], amount: #stripe_btn_data[:amount], description: #stripe_btn_data[:description]}, src: "https://checkout.stripe.com/checkout.js"}
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
This is my first time working with Stripe and Rails and now I am trying to allow premium users to cancel their subscriptions.
I can upgrade a user from standard level to premium level with my code, but I am having issues when I attempt to downgrade a premium user to the standard level.
I have followed Stripe Ruby API References of "Cancel a subscription": https://stripe.com/docs/api?lang=ruby#cancel_subscription, but I got this error when I clicked the "cancel subscription" button:
NoMethodError - undefined method encoding' for nil:NilClass:
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/cgi/util.rb:7:inescape'
stripe (1.21.0) lib/stripe/list_object.rb:19:in retrieve'
app/controllers/subscriptions_controller.rb:55:indowngrade'
My rails version is 4.2.1.
My code:
class SubscriptionsController < ApplicationController
def create
subscription = Subscription.new
stripe_sub = nil
if current_user.stripe_customer_id.blank?
# Creates a Stripe Customer object, for associating with the charge
customer = Stripe::Customer.create(
email: current_user.email,
card: params[:stripeToken],
plan: 'premium_plan'
)
current_user.stripe_customer_id = customer.id
current_user.save!
stripe_sub = customer.subscriptions.first
else
customer = Stripe::Customer.retrieve(current_user.stripe_customer_id)
stripe_sub = customer.subscriptions.create(
plan: 'premium_plan'
)
end
current_user.subid = stripe_sub.id
current_user.subscription.save!
update_user_to_premium
flash[:success] = "Thank you for your subscription!"
redirect_to root_path
# Handle exceptions
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_subscriptions_path
end
def downgrade
customer = Stripe::Customer.retrieve(current_user.stripe_customer_id)
customer.subscriptions.retrieve(current_user.subid).delete
downgrade_user_to_standard
flash[:success] = "Sorry to see you go."
redirect_to user_path(current_user)
end
end
ApplitionController:
class ApplicationController < ActionController::Base
def update_user_to_premium
current_user.update_attributes(role: "premium")
end
def downgrade_user_to_standard
current_user.update_attributes(role: "standard")
end
end
config/initializers/stripe.rb:
Rails.configuration.stripe = {
publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'],
secret_key: ENV['STRIPE_SECRET_KEY']
}
# Set our app-stored secret key with Stripe
Stripe.api_key = Rails.configuration.stripe[:secret_key]
Any help will be appreciated!
Update:
Thanks for help from stacksonstacks, all I need is asserting 'subscription.user = current_user' under 'current_user.subid = stripe_sub.id', and then call subscription id with "subscription = current_user.subscription" in downgrade method. Now subscription cancelling works!
It seems current_user.subid returns nil on this line:
customer.subscriptions.retrieve(current_user.subid).delete
You assign subid for current_user but you never save the changes.
You only save the newly created subscription.
current_user.subid = stripe_sub.id
current_user.subscription.save!
If you add current_user.save! I think this will solve the problem.
Hope that helps
I'm using CareerFoundry's tutorial on setting up Stripe Checkout with Rails. I'm getting an error on form submission that the public API is incorrect. I think the problem is in the initializer file below, but it could be something else.
Config/initializers/stripe.rb
if Rails.env.production?
Rails.configuration.stripe = {
publishable_key: ENV[ 'STRIPE_PUBLISHABLE_KEY' ],
secret_key: ENV[ 'STRIPE_SECRET_KEY' ]
}
else
Rails.configuration.stripe = {
publishable_key: 'pk_test_UQ2EqhNNQRrDkDouuZ1xgpS5', #Both test keys are fakes in case you're wondering
secret_key: 'sk_test_hkiYUThcriCTBfHuUSXpUP7n'
}
end
Payments Controller
class PaymentsController < ApplicationController
def create #You want might to make actions more specific.
token = params[:stripeToken] #The token when the form is posted includes the stripe token in the url.
# Create the charge in stripes servers. This is what commits the transaction.
begin
charge = Stripe::Charge.create(
:amount => 200,
:currency => "usd",
:source => token,
:description => params[:stripeEmail]
)
rescue Stripe::CardError => e
#The card was decline because of number/ccv error, expired error, bank decline, or zip error
body = e.json_body
err = body[:error]
flash[:error] = "There was an error in processing your card: #{err[:message]}"
end
respond_to do |format|
format.html { redirect_to "/confirmation" }
#format.html { redirect_to "/purchase", notice: "Purchase was successfully completed. We'll be in contact shortly!" }
end
end
end
Views/Shared/_stripe_checkout_button.html.erb
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
data-image="/assets/square-image.png"
data-name="Achieve More"
data-description="1 Month ($800)"
data-amount="2000">
</script>
Views/payments.html.erb
<%= form_tag "/payments" do %>
<%= render partial: "shared/stripe_checkout_button" %>
<% end %>
So the CareerFoundry tutorial didn't have anything about setting the keys. The stripe demo does. This line:
heroku config:set PUBLISHABLE_KEY=pk_test_UQ2EqhNNQRrDkD5V0Z1xgpS5 SECRET_KEY=sk_test_hkiYUTQzHiCTBfHuUSXpUP7n
sets the keys for the initializer. Those were missing, causing the error.