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.
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
New to stripe, and ruby. Trying to add an application fee to my stripe payment flow. The charges are all making it through to my stripe account, but I can't seem to figure out how/where to add the stripe application fee code.
charges/new.html.erb
<%= form_tag charges_path do %>
<div id="error_explanation">
<% if flash[:error].present? %>
<p><%= flash[:error] %></p>
<% end %>
</div>
<article>
<%= label_tag(:amount, 'Payment Amount:') %>
<%= text_field_tag(:amount) %>
</article>
<article>
<%= hidden_field_tag(:stripeToken) %>
</article>
<button id='donateButton'>Pay</button>
<% end %>
<script src="https://checkout.stripe.com/checkout.js"></script>
<script>
var handler = StripeCheckout.configure({
key: '<%= Rails.configuration.stripe[:publishable_key] %>',
locale: 'auto',
name: 'Payments',
description: 'Pay Someone',
token: function(token) {
$('input#stripeToken').val(token.id);
$('form').submit();
}
});
$('#donateButton').on('click', function(e) {
e.preventDefault();
$('#error_explanation').html('');
var amount = $('input#amount').val();
amount = amount.replace(/\$/g, '').replace(/\,/g, '')
amount = parseFloat(amount);
if (isNaN(amount)) {
$('#error_explanation').html('<p>Please enter a valid amount in CAD ($).</p>');
}
else if (amount < 5.00) {
$('#error_explanation').html('<p>Wage amount must be at least $5.</p>');
}
else {
amount = amount * 100; // Needs to be an integer!
handler.open({
amount: Math.round(amount)
})
}
});
// Close Checkout on page navigation
$(window).on('popstate', function() {
handler.close();
});
</script>
charges_controller.rb
class ChargesController < ApplicationController
def new
end
def create
#amount = params[:amount]
#amount = #amount.gsub('$', '').gsub(',', '')
begin
#amount = Float(#amount).round(2)
rescue
flash[:error] = 'Charge not completed. Please enter a valid amount in USD ($).'
redirect_to new_charge_path
return
end
#amount = (#amount * 100).to_i # Must be an integer!
if #amount < 500
flash[:error] = 'Charge not completed. Donation amount must be at least $5.'
redirect_to new_charge_path
return
end
Stripe::Charge.create(
:amount => #amount,
:currency => 'usd',
:source => params[:stripeToken],
:description => 'Custom donation'
)
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
end
end
As I've mentioned, the payment flow works fine, Stripe docs says it must be a number, but I would prefer a percentage if that is possible. this is the code given in the examples:
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "secret key"
# Get the credit card details submitted by the form
token = params[:stripeToken]
# Create the charge with Stripe
charge = Stripe::Charge.create({
:amount => 1000, # amount in cents
:currency => "cad",
:source => token,
:description => "Example charge",
:application_fee => 123 # amount in cents
},
{:stripe_account => CONNECTED_STRIPE_ACCOUNT_ID}
)
I just dont know where or how to add the application fee code! my payment flow works in that the user can enter their own payment amount, and I cant seem to find any answers here or in the documentation which reflects the code in that form. I'm sure its a simple solution, but I'm very new to working with Stripe.
You can only specify an application_fee when creating a charge with Connect, either directly on a connected account (with the Stripe-Account header) or through the platform (with the destination parameter).
From your code, it doesn't look like you're using Connect at all, so application fees wouldn't apply. Can you clarify what exactly you're trying to do? How should the funds from the charge be split?
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.
I have a request object that I want to pass to my charges_controller. I can pass it in to the new action but after that new action is called I need that same request object passed to the create object in the same charges_controller. I can not get it. Here is the initial request_controller action passing the request_id to the charges controller.
class Users::RequestsController < ApplicationController
before_filter :authenticate_user!
def create
#user = current_user
if #request = #user.request.create!(authorization_params)
if #request.user_paying == true
redirect_to new_users_charge_path(id: #request)
else
redirect_to users_dashboard_path
end
else
redirect_to users_dashboard_path, :error => "There is something wrong and your request has not been submitted."
end
end````
It then passes it to the new method in the charges controller. Here is my charges controller.
class Managers::ChargesController < ApplicationController
before_filter :authenticate_user!
def new
#request = Request.find(params[:id])
#user = current_user
#amount = #request.user_amount
end
def create
# Amount in cents
#request = Request.find params[:request_id]
#user = current_user
#amount = #request.user_amount
customer = Stripe::Customer.create(
:email => #user.email,
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'Manager Paid Report',
:currency => 'usd'
)
#request.report_paid = true
#request.save!
redirect_to managers_dashboard_path, :notice => "You have succesfully requested a pre-paid report from #{#reportapproval.tenant_last_name}."
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to managers_charges_path
end
end
I can get the request info into the charges/new.html view. But when I create the charge the request is not found. How can I pass the request to the create action?
Here is the new view...
<h4>Total Payment Due: <%= number_to_currency(#amount.to_i/100.0) %>
<%= form_tag users_charges_path do %>
<%= hidden_field_tag :request_id, #request.id %>
<br />
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
data-description="Payment"
data-amount="<%= #amount %>"
data-locale="auto" >
</script>
<% end %>
Controllers simply respond to HTTP requests - there is no way to forward a request internally in Rails. This is a very conscious design decision.
If you need to stash data in between requests you would either save it in the database, the session or some sort of caching mechanism like Memcached/Redis.
Most commonly in Rails you stash data in the database and pass ID's or other unique identifiers in the request parameters.
If you need to pass the id to the "Request" object you would either pass it in the request url or in the request body in the case of PUT/PATCH/POST.
So in your case you need to ensure that the #new action either posts to:
post "/managers/charges/:request_id"
Or that the form includes
<%= hidden_field_tag 'request_id', #request.id %>
PS
Request although not technically a reserved word is a really bad name for a model since it will eventually cause conflicts and confusion with ActionDispatch::Request especially if you use the controller instance variable #request.
As you can see from the my answer above it gets confusing as h*ll since the word request is allready has a very specific connotation in web development.
The same applies to Response.
Use PaymentRequest or any other synonym.
Assuming you have a form in the charges/newview, you can set the request_id using a hidden form field:
<%= hidden_field_tag :request_id, #request.id %>
You should add the line above between <%= form_for ... do |f| %> and <% end %>.
In that example, you were assuming the create action would somehow have a request_id passed in automatically. Rails doesn't do that, in every request you need to explicitly provide all the information for the controller to process that request.
I hope that helps! ;)
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"}