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"}
Related
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'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.
We have a form to submit ratings for a certain restaurant in a in our views/restaurants/show.html.erb. If there is a validation error we get redirected back to views/restaurants/show.html.erb but the validation messages do not appear. We figured out that this happens because we lose the messages using redirect_to(#restaurant) in our RatingController create action. But how can we get back without redirection?
Thanks!
Here is how I solved this. (Note that in what follows I'm obviously just including just the most relevant lines.)
In the model there may be multiple validations and even methods that potentially report on multiple errors.
class Order < ActiveRecord::Base
validates :name, :phone, :email, :presence => true
def some_method(arg)
errors.add(:base, "An error message.")
errors.add(:base, "Another error message.")
end
end
As well, the controller action may set flash messages. Finally, the user may have entered data into the input fields, and we want it to persist through the redirect_to too.
class OrdersController < ApplicationController
def create
#order = Order.new(params[:order])
respond_to do |format|
if #order.save
session.delete(:order) # Since it has just been saved.
else
session[:order] = params[:order] # Persisting the order data.
flash[:notice] = "Woohoo notice!" # You may have a few flash messages
flash[:alert] = "Woohoo alert!" # as long as they are unique,
flash[:foobar] = "Woohoo foobar!" # since flash works like a hash.
flash[:error] = #order.errors.to_a # <-- note this line
format.html { redirect_to some_path }
end
end
end
end
Depending on your setup, you may or may not need to save the model data, such as order, to the session. I did this for the purpose of passing the data back to the original controller, and thereby being able to setup the order there again.
In any case, to display the actual error and flash messages, I did the following (in views/shared/_flash_messages.html.erb, but you could do it in application.html.erb or wherever else makes sense for your app). And this is thanks to that line flash[:error] = #order.errors.to_a
<div id="flash_messages">
<% flash.each do |key, value|
# examples of value:
# Woohoo notice!
# ["The server is on fire."]
# ["An error message.", "Another error message."]
# ["Name can't be blank", "Phone can't be blank", "Email can't be blank"]
if value.class == String # regular flash notices, alerts, etc. will be strings
value = [value]
end
value.each do |value| %>
<%= content_tag(:p, value, :class => "flash #{key}") unless value.empty? %>
<% end %>
<% end %>
</div><!-- flash_messages -->
To be clear, regular flash messages such as notices, alerts, etc. will be strings, however errors will be arrays since the above call was errors.to_a
You can pass your error on flash message
flash[:error] = #restaurant.errors
After you need display it in your redirect
Here is how I did it still doing the redirect:
Just before you redirect on validation errors in your controller store errors to flash like suggested by #shingara:
if #restaurant_rating.save
redirect_to #restaurant, :notice => "Successfully added rating to restaurant."
else
flash[:error] = #restaurant_rating.errors
redirect_to #restaurant, :alert => "There were errors to add rating to restaurant. "
end
Then in your form for rating you assign errors back for the rating object just before rendering the form:
- flash[:error].messages.each {|error| #restaurant_rating.errors.add(error[0], error[1][0]) } if flash[:error]
= simple_form_for #restaurant_rating do |f|
....
You can use render instead of redirect_to
render :action => "show"
or set flash[:error], flash[:notice] again, because they automatically reseted
After your clarification in the comment, you need to setup your
/app/views/layouts/application.html.erb
with this line
<%- flash.each do |name, msg| -%><%= content_tag :div, msg, :id => "flash_#{name}" %><%- end -%>