Stripe javascript not retrieving the token I need to create a charge - ruby-on-rails

I am trying to process a payment using a simple stripe charge, the user must pay a quantity in the website through a form and, basically, I don´t know why i am not getting the token back, I think my javascript is not being executed actually. Here is the code:
This is the form placed in order#new (the form view)
:javascript
Stripe.setPublishableKey("<%= ENV['STRIPE_TEST_PUBLISHABLE_KEY] %>");
.container.body-content
%h2
Complete order:
= #cart.name
%table.table.table-items
%tr
%th Design name
%th Quantity
%th Price
- #cart.cart_items.each do |c|
%tr
%td= c.design.name
%td= c.quantity
%td= c.design.price
%tr
%th Total price
%th
%th= #total_price
= simple_form_for(#order, html: { role: "form", class: "cc_form" }) do |f|
= f.simple_fields_for( :payment ) do |p|
.row.col-md-12
.form-group.col-md-4.no-left-padding
= p.input :card_number,
label: "Card Number",
label_html: { data: { stripe: "label" } },
input_html: { required: "true", data: { stripe: "number" }, class: "form-control" }
.form-group.col-md-2
= p.input :card_cvv,
label: "Card CVV",
label_html: { data: { stripe: 'label' } },
input_html: { required: "true", data: { stripe: "cvv" }, class: "form-control" }
.form-group.col-md-6
.col-md-12
= p.label :card_expires, "Card expires", data: { stripe: 'label' }
.col-md-3
= p.select :card_expires_month, options_for_select(Payment.month_options), { include_blank: 'Month' }, data: { stripe: "exp-month" }, class: "form-control", required: true
.col-md-3
= p.select :card_expires_year, options_for_select(Payment.year_options.push), { include_blank: 'Year' }, data: { stripe: "exp-year" }, class: "form-control", required: true
= f.hidden_field :cart_id, :value => #cart.id
= f.hidden_field_tag 'cart', #cart.id
= f.hidden_field_tag 'total_price', #total_price
= f.button :submit, "Pay and finish order", class: "btn btn-login"
This is orders_controller.rb:
class OrdersController < ApplicationController
include ApplicationHelper
def index
end
def new
#order = current_user.orders.build
#cart = Cart.find(params[:format])
#payment = #order.build_payment
#total_price = get_total_price_for_navigation_bar
end
def create
#order = current_user.orders.build(order_params)
#cart = Cart.find(params[:cart])
#payment = #order.build_payment
#payment.user_id = current_user.id
process_payment(params[:total_price])
if payment.save
if #order.save
#cart.purchased = true
#cart.total_price = params[:total_price]
#cart.save
flash[:success] = "Order created"
redirect_to orders_path
else
flash[:danger] = "Order couldn´t be saved, please try again"
render :new
end
else
flash[:danger] = "There was a problem processing your payment, please try again"
render :new
end
end
private
def order_params
params.require(:order).permit(:cart_id)
end
def process_payment(amount)
#byebug
token = params[:stripeToken]
byebug
# Create a charge: this will charge the user's card
begin
charge = Stripe::Charge.create( :amount => amount * 100, :currency => "eur", :source => token, :description => "Example charge")
rescue Stripe::CardError => e
# The card has been declined
end
end
end
and to process the payment I have created credit_card_form.js (of course under assets -> javascripts (I have also removed the turbolinks from application.js )
$(document).ready(function(){
var show_error, stripeResponseHandler, submitHandler;
submitHandler = function(event){
var $form = $(event.target);
$form.find("input[type=submit]").prop("disabled", true);
//If stripe was correctly initialized this will create a token using the credit card info
if(Stripe){
Stripe.card.createToken($form, stripeResponseHandler);
} else {
show_error("Failed to load credit card processing functionality. Please reload this page in your browser.")
}
return false;
};
$(".cc_form").on('submit', submitHandler);
stripeResponseHandler = function(status, response){
var token, $form;
$form = $('.cc_form');
if (response.error) {
console.log(response.error.message);
show_error(response.error.message);
$form.find("input[type=submit]").prop("disabled", false);
} else {
token = response.id;
$form.append($('<input type="hidden" name="stripeToken">').val(token));
$form.get(0).submit();
}
return false;
};
show_error = function(message){
if($("#flash-messages").size() < 1){
$('div.container.main div:first').prepend("<div id='flash-messages'></div>")
}
$("#flash-messages").html('<div class="alert alert-warning"><a class="close" data-dismiss="alert">x</a><div id="flash_alert">' + message + '</div></div>');
$('.alert').delay(5000).fadeOut(3000);
return false;
};
});
When I try to process a payment I never receive the token in the order#create method, so I think that the javascript is not executing
When I inspect variables in terminal I see that the token is not added to the form as a param, as I am suppose to append in stripeResponseHandler in credit_card_form.js this is the output I see when I inspect variables:
(byebug) token
nil
(byebug) params
{"utf8"=>"✓", "authenticity_token"=>"ps963bnlJwbayybIgXVAu8xQI3L6BneUK6qg5k9U9BV7XL+3M+Bl+OVJqJGNrcMHLAjhjvxOkOlhyig9Sh5Cdw==", "order"=>{"payment_attributes"=>{"card_number"=>"4012888888881881", "card_cvv"=>"123", "card_expires_month"=>"3", "card_expires_year"=>"2017"}, "cart_id"=>"9"}, "cart"=>"9", "total_price"=>"390.0", "controller"=>"orders", "action"=>"create"}
(byebug)
Well, I don´t know if I am not seing something stupid or I am completely wrong in my approach, any suggestion will be appreciated.
Thanks in advance
Antonio

[Update: Problem solved]
After several hours, I found what was the problem, wasn´t the code itself, the problem was HAML actually, precisely the way haml interpretes the form id or the class, I don´t know exactly why but I have changed from haml to .erb file and both the customer creation and the stripe charge are processed correctly. For what i´ve seen, using that haml version of the form stripeResponseHandler wasn´t correctly executed (don´t ask me why, I am not so sure, I just know the form wasn´t updated correctly and the token was not included as a param as I wanted in order to process the charge) that is why the charge wasn´t created successfully.
Hope it helps to someone else.
Regards and thanks to u all.

Related

Ruby on Rails 6 Turbolinks Stripe form values not persisting on back

I am trying to build a form that allows a user to make a booking for a martial arts class they wish to attend. I have created a form that dynamically changes based on the selections the user makes, when I change any of the select options the form updates and when I submit the form it redirects to a Stripe checkout. The problem I have is after submitting the the form and I click the browsers back button or the back button provided on the Stripe checkout page the select options that have been updated have reverted back to the default options rather than the updated options. Can anyone help me correct this behaviour and get the correct form elements to persist?
Here is the code I am using to do this:
The view I am rendering the form in:
<% content_for :banner_title, #page_data['bannerTitle'] %>
<% content_for :head do %>
<meta name="turbolinks-cache-control" content="no-cache">
<% end %>
<div class="content container py-5">
<div class="row">
<div class="col-12 col-md-7 mx-auto">
<%= render "forms/booking", options: #options %>
</div>
</div>
</div>
The form I am using:
<%= form_for #booking, class: 'booking clearfix' do |f| %>
<div class="form-group">
<%= f.label(:class_name, "Select a class you wish to attend: ") %>
<%= f.select(:class_name, options_for_select(options[:class_names], #booking.class_name), {}, class: 'form-control' ) %>
</div>
<div class="form-group">
<%= f.label(:date, "Select a date:") %>
<%= f.select(:date, options_for_select( options[:dates], #booking.date ), {}, class: 'form-control' ) %>
</div>
<div class="form-group">
<%= f.label(:time, "Select a time: ")%>
<%= f.select(:time, options_for_select(options[:times], #booking.time), {}, class: 'form-control') %>
</div>
<div class="form-group">
<%= f.label(:attendees, "How many attending: ") %>
<%= f.select(:attendees, options_for_select(options[:attendees], #booking.attendees), {}, class: 'form-control' )%>
</div>
<%= f.submit 'Place Booking', class: 'btn btn-primary btn-lg text-light float-right', id: 'create-booking' %>
<% end %>
<%= javascript_pack_tag 'booking_form' %>
<script src="https://js.stripe.com/v3/"></script>
The model for the form (I'm not using ActiveRecord, i dont know if this makes any difference?):
class Booking
include ActiveModel::Model
MAX_ATTENDEES = 10
attr_accessor :time, :class_data, :attendees, :date, :class_name
def initialize(args={})
#time = args['time']
#class_name = args['class_name']
#class_data = args['class_data']
#date = args['date']
#attendees = args['attendees']
end
def day
#date.split(',').first
end
def available_dates
days_index_array = class_data['times'].keys.map {|k| day_index(k) }
days_within( days_index_array )
end
def available_times
if !date
class_data['times'][class_data['times'].keys.first.downcase]
else
class_data['times'][day.downcase]
end
end
def total_cost
#class_data['cost'].to_i * #attendees.to_i
end
def attending_string
ActionController::Base.helpers.pluralize(attendees, 'person')
end
private
def days_within(days, timeframe=1.month)
start_date = Date.tomorrow
end_date = start_date + timeframe
(start_date..end_date).to_a.select {|k| days.include?(k.wday) }
end
def day_index(day)
DateTime::DAYNAMES.index(day.to_s.capitalize)
end
end
And the controller I am calling the new action in:
class BookingsController < ApplicationController
include BookingsHelper
before_action :set_class_data
skip_before_action :set_page_data, except: :new
def new
set_booking
# store values to be passed to the form helper method options_for_select. Each value must be an array populated with arrays with the format [value, text]
#options = {
class_names: #class_data.map {|c| [ c['name'], c['name'] ]},
dates: #booking.available_dates.map {|d| [d.strftime('%A, %d %B'), d.strftime('%A, %d %B')] },
times: #booking.available_times.map {|t| [t,t]},
attendees: Booking::MAX_ATTENDEES.times.map {|i| [i+1, i+1]}
}
end
def create
end
def booking_form_data
booking_form_data = set_booking_form_data(params)
update_session_booking(booking_form_data)
render json: booking_form_data
end
private
def set_booking
if session[:current_booking]
pp "session exists"
#booking = Booking.new(session[:current_booking])
else
pp "session does not exist"
#booking = Booking.new
session[:current_booking] = #booking.instance_values
end
set_booking_class_data
end
def set_booking_class_data
!#booking.class_name ? #booking.class_data = #class_data.first.except('information') : #booking.class_data = #class_data.find {|cd| cd['name'] == #booking.class_name}.except('information')
end
def booking_params
params.permit(:class_name, :date, :time, :attendees, :update_type)
end
def update_session_booking(booking_form_data)
if params[:update_type] == 'class_name'
session[:current_booking]['class_name'] = params[:class_name]
session[:current_booking]['date'] = booking_form_data[:date_options].first
session[:current_booking]['time'] = booking_form_data[:time_options].first
elsif params[:update_type] == 'date'
session[:current_booking]['date'] = params[:date]
session[:current_booking]['time'] = booking_form_data[:time_options].first
elsif params[:update_type] == 'time'
session[:current_booking]['time'] = params['time']
elsif params[:update_type] == 'attendees'
session[:current_booking]['attendees'] = params[:attendees]
elsif params[:update_type] == 'load'
session[:current_booking] = booking_params.except(:update_type)
end
pp "Session Booking: #{session[:current_booking]}"
end
def set_booking_form_data(params)
booking_form_data = {}
selected_class = #class_data.find {|cd| cd['name'] == params[:class_name] }
# when the class_name select is changed
if params[:update_type] == 'class_name'
booking_form_data[:date_options] = days_within( selected_class['times'].keys.map {|k| day_index(k) } ).map {|d| d.strftime('%A, %d %B') }
booking_form_data[:time_options] = selected_class['times'][booking_form_data[:date_options].first.split(',')[0].downcase]
# when date select is changed
elsif params[:update_type] == 'date'
booking_form_data[:time_options] = selected_class['times'][params[:date].split(',')[0].downcase]
end
booking_form_data
end
end
And the javascript I am using to update the form:
getBookingFormData = (bodyData={}, successCallback=()=>{}) => {
$.ajax({
url: '/booking_form_data',
method: 'POST',
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
data: bodyData,
success: successCallback
})
}
createOptions = (values) => {
let newOptions = [];
$.each(values, (index, value) => {
let newOption = $('<option></option>');
newOption.attr('value', value);
newOption.text(value);
newOptions.push(newOption)
})
return newOptions
}
appendOptions = (options, element) => {
$(element).empty();
$(element).append(options)
}
currentFormValues = () => {
return {
class_name: $('#booking_class_name').val(),
date: $('#booking_date').val(),
time: $('#booking_time').val(),
attendees: $('#booking_attendees').val()
}
}
$('select#booking_class_name').on('change', () => {
let bodyData = {
class_name: $('select#booking_class_name').val(),
update_type: 'class_name'
}
let successCallback = (res) => {
let dateOptions = createOptions(res.date_options);
let dateSelect = $('select#booking_date');
let timeOptions = createOptions(res.time_options);
let timeSelect = $('select#booking_time');
appendOptions(dateOptions, dateSelect);
appendOptions(timeOptions, timeSelect);
}
getBookingFormData(bodyData, successCallback)
});
$('select#booking_date').on('change', () => {
let bodyData = {
class_name: $('select#booking_class_name').val(),
date: $('select#booking_date').val(),
update_type: 'date'
};
let successCallback = (res) => {
let timeOptions = createOptions(res.time_options);
let timeSelect = $('select#booking_time');
appendOptions(timeOptions, timeSelect);
}
getBookingFormData(bodyData, successCallback)
});
$('select#booking_time').on('change', () => {
let bodyData = {
time: $('select#booking_time').val(),
update_type: 'time'
};
getBookingFormData(bodyData);
});
$('select#booking_attendees').on('change', () => {
let bodyData = {
attendees: $('select#booking_attendees').val(),
update_type: 'attendees'
};
getBookingFormData(bodyData);
});
$('#create-booking').on('click',(e) => {
e.preventDefault();
bookingDefault = false
const stripe = Stripe(process.env.STRIPE_PUBLIC);
let requestHeaders = new Headers({
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
'Content-Type': 'application/json'
})
fetch('/create_checkout_session', {
method: 'POST',
headers: requestHeaders,
body: JSON.stringify(currentFormValues())
})
.then((res) => { return res.json() })
.then((session) => { return stripe.redirectToCheckout({ sessionId: session.id }) })
.then((result) => {
if (result.error) { alert(result.error.message) }
})
.catch((error) => { console.error('Error: ', error) })
})
From what Ive read i think it may be a problem related to caching which makes me think this is an issue with turbolinks but I could be completely wrong. Ive tried adding meta tags that disable turbolinks or force it to reload the page but they did not seem to work.
Any input at all would be really appreciated as Ive been stuck on this for days. Let me know if you need any more information
This isn't so much Stripe-related as it is related you your form value management. If you want to keep these values around, you'll need to build that into your front application, somehow. There are lots of options for this:
Using local storage
Using query parameters, if not sensitive info
Using a cookie and a server session you can re-retrieve and hydrate the f.select options with a default value.

Trying to pass/fetch data for stripe api

I'm quite new to Ruby on Rails and I have been following this tutorial on how to implement an online marketplace by using Stripe's Connect API. This tutorial guides you on how to make single item purchases, I have tried to advance myself past this tutorial and create a marketplace where a user can purchase multiple items and put them in a basket and checkout, a nice challenge as the tutorial is focused on one item. However, I am stuck on the checkout _form.html.erb.
Before, to fetch the publishable_key,
<%= form_with model: current_user, local: true, url: subscription_url, method: :post, html: { id: "payment-form", class: "stripe-form" }, **data: { stripe_key: product.user.publishable_key }** do |form| %>
but now because I am submitting a cart full of products instead of one single item the data: { stripe_key: } is now dysfunctional. Instead, I thought it might be a good idea to do data: { stripe_key: #account.publishable_key } to fetch the publishable_key from the database but it is not working.
I am most likely forgetting something important. Any help would be sound!
http://localhost:3000/subscription/new?account=4&amount=17
The Error
NoMethodError in Subscriptions#new
Showing /Users/kaneandrewgibson/Desktop/Charlie/WOP/app/views/subscriptions/_form.html.erb where line #1 raised:
undefined method `publishable_key' for nil:NilClass
Subscriptions/_form.html.erb
<%= form_with model: current_user, local: true, url: subscription_url, method: :post, html: { id: "payment-form", class: "stripe-form" }, data: { stripe_key: #account.publishable_key } do |form| %>
<div>
<label for="card-element" class="label">
Credit or debit card
</label>
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
<!-- Used to display Element errors. -->
<div id="card-errors" role="alert" class="text-sm text-red-400"></div>
</div>
<input type="hidden" name="account" value="<%= params[:account] %>">
<button>Back <%= number_to_currency(params[:amount]) %> /mo toward <em><%= %></em></button>
<% end %>
SubscriptionsController
class SubscriptionsController < ApplicationController
before_action :authenticate_user!
def new
#account = User.find_by_id(params[:id])
end
# Reference:
# https://stripe.com/docs/connect/subscriptions
def create
#account = User.find_by_id(params[:id])
key = #account.user.access_code
Stripe.api_key = key
plan_id = params[:plan]
plan = Stripe::Plan.retrieve(plan_id)
token = params[:stripeToken]
customer = if current_user.stripe_id?
Stripe::Customer.retrieve(current_user.stripe_id)
else
Stripe::Customer.create(email: current_user.email, source: token)
end
Stripe::PaymentIntent.create({
customer: customer,
amount: #cart.total_price * 100,
confirm: true,
currency: 'gbp',
payment_method_types: ['card'],
application_fee_percent: 3,
}, {
stripe_account: 'key',
})
options = {
stripe_id: customer.id,
subscribed: true,
}
options.merge!(
card_last4: params[:user][:card_last4],
card_exp_month: params[:user][:card_exp_month],
card_exp_year: params[:user][:card_exp_year],
card_type: params[:user][:card_brand]
)
current_user.perk_subscriptions << plan_id
current_user.update(options)
# Update project attributes
project_updates = {
backings_count: #project.backings_count.next,
current_donation_amount: #project.current_donation_amount + (plan.amount/100).to_i,
}
#project.update(project_updates)
redirect_to root_path, notice: "Your subscription was setup successfully!"
end
def destroy
subscription_to_remove = params[:id]
plan_to_remove = params[:plan_id]
customer = Stripe::Customer.retrieve(current_user.stripe_id)
customer.subscriptions.retrieve(subscription_to_remove).delete
current_user.subscribed = false
current_user.perk_subscriptions.delete(plan_to_remove)
current_user.save
redirect_to root_path, notice: "Your subscription has been cancelled."
end
end
Kane.
It looks like your account ID isn't getting into your controller. I would expect the URL to look something like this (account_id instead of account):
http://localhost:3000/subscription/new?account_id=4&amount=17
And I would expect the SubscriptionsController#new to be looking in the Account model rather than the User model, and to be using the same account_id param:
#account = Account.find_by_id(params[:account_id])

Trouble Creating Rails Stripe Subscription

New-ish Ruby Rails programmer here, please help me learn. I am having a difficult time creating a subscription in Stripe. It is an app where schools will be registering to. I already created a plan in Stripe with an ID called, 'reach' and I am able to create a Stripe Customer Token, but not a Subscription.
On my registration form (in views), I have a hidden_field_tag with the plan name as 'reach' which is passed through the URL, params. I also have a hidden field in the form of the stripeToken.
I have a class called SchoolRegistration and the code underneath is here:
attr_accessor :stripeToken
attr_accessor :plan
def save_with_subscription
if valid?
customer = Stripe::Customer.create(description: email, plan: plan, source: stripeToken)
self.stripe_customer_token = customer.id
save!
end
end
What I discovered recently is the <%= hidden_field_tag :plan, params[:plan] %> in my views is NOT saving to my database. I can see it on my console when I hit submit, but it never gets saved to the database. How can I save that in the database?
Controller:
class SchoolRegistrationsController < ApplicationController
def new
#register = SchoolRegistration.new
end
def create
#register = SchoolRegistration.new(register_params)
if #register.save_with_subscription
flash[:success] = "Congratulations! You have registered your school!
redirect_to new_user_registration_path
else
flash[:danger] = #register.errors.full_messages.join(", ")
redirect_to new_registration_path
end
end
private
def register_params
params.require(:school_registration).permit(:name_of_person_completing_form, :email, :role_in_school, :school_name, :grade_levels, :street_name, :city, :state, :zip_code)
end
end
params.require is indented in my code...not sure why it wouldn't indent here.
JavaScript:
/* global $ Stripe */
//Document ready.
$(document).on('turbolinks:load', function(){
//Set Stripe public key.
var stripe = Stripe($('meta[name="stripe-key"]').attr('content'));
var elements = stripe.elements();
// Custom styling can be passed to options when creating an Element.
var style = {
base: {
// Add your base input styles here. For example:
fontSize: '16px',
color: "#32325d",
}
};
// Create an instance of the card Element
var card = elements.create('card', {style: style});
// Add an instance of the card Element into the `card-element` <div>
card.mount('#card-element');
card.addEventListener('change', function(event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the customer that there was an error
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server
stripeTokenHandler(result.token);
}
});
});
});
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('payment-form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
I know it is probably obvious, I am just having a difficult time and I did check out the documentation. Please help me learn and much thanks to all of you! Let me know if you need more info or code - using Rails 5.
create the subscription by associating the plan with the customer id witch you get when creating customer on stripe
Stripe::Subscription.create(
:customer => "cus_4fdAW5ftNQow1a",
:items => [
{
:plan => "basic-monthly",
},
],
)
For more information https://stripe.com/docs/subscriptions/quickstart
Ok, I'm trying to give a full stripe implementation solution, you follow this step by step, all code is tested and go to the live site for testing here the site
This example only Stripe payment
Add this on view/layouts/application.html.erb
<%= javascript_include_tag "https://js.stripe.com/v2/" %>
just above
<%= javascript_include_tag "application" %>
Create environment variable with Stripe keys
STRIPE_TEST_PUBLISHABLE_KEY: pk_test_xxxxxxxxxx
STRIPE_TEST_SECRET_KEY: sk_test_xxxxxxxxxxxxx
On the registration file, add the code below to the top of the file:
<script language="Javascript">
Stripe.setPublishableKey("<%= ENV['STRIPE_TEST_PUBLISHABLE_KEY'] %>");
</script>
And add this class on your form cc_form
Create a model for payment with references
rails g model Payment email:string token:string school_registration:references
Will generate a file under db like belo
class CreatePayments < ActiveRecord::Migration[5.0]
def change
create_table :payments do |t|
t.string :email
t.string :token
t.references :school_registration, foreign_key: true
t.timestamps
end
end
end
Then
rake db:migrate
#=> model/SchoolRegistration.rb
#=> add these two lines
has_one :payment
accepts_nested_attributes_for :payment
On the payment.rb
attr_accessor :card_number, :card_cvv, :card_expires_month, :card_expires_year
belongs_to :school_registration
def self.month_options
Date::MONTHNAMES.compact.each_with_index.map { |name, i| ["#{i+1} - #{name}", i+1]}
end
def self.year_options
(Date.today.year..(Date.today.year+10)).to_a
end
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 1000 cents that means 10 dollars
end
Now on your form
<%= fields_for( :payment ) do |p| %>
<div class="row col-md-12">
<div class="form-group col-md-4 no-left-padding">
<%= p.label :card_number, "Card Number", data: {stripe: "label"} %>
<%= p.text_field :card_number, class: "form-control", required: true, data: {stripe: 'number'} %>
</div>
<div class="form-group col-md-2">
<%= p.label :card_cvv, "Card CVV", data: {stripe: "label"} %>
<%= p.text_field :card_cvv, class: "form-control", required: true, data: {stripe: 'cvv'} %>
</div>
<div class="form-group col-md-6">
<div class="col-md-12">
<%= p.label :card_expires, "Caed Expires", data: {stripe: "label" } %>
</div>
<div class="col-md-3">
<%= p.select :card_expires_month, options_for_select(Payment.month_options),
{ include_blank: 'Month' },
"data-stripe" => "exp-month",
class: "form-control", required: true %>
</div>
<div class="col-md-3">
<%= p.select :card_expires_year, options_for_select(Payment.year_options.push),
{ include_blank: 'Year' },
class: "form-control",
data: { stripe: "exp-year" }, required: true %>
</div>
</div>
</div>
<% end %>
And now create JS file under javascripts folder named like stripe.js
$(document).ready(function() {
var show_error, stripeResponseHandler, submitHandler;
submitHandler = function (event) {
var $form = $(event.target);
$form.find("input[type=submit]").prop("disabled", true);
//If Stripe was initialized correctly this will create a token using the credit card info
if(Stripe){
Stripe.card.createToken($form, stripeResponseHandler);
} else {
show_error("Failed to load credit card processing functionality. Please reload this page in your browser.")
}
return false;
};
$(".cc_form").on('submit', submitHandler);
stripeResponseHandler = function (status, response) {
var token, $form;
$form = $('.cc_form');
if (response.error) {
console.log(response.error.message);
show_error(response.error.message);
$form.find("input[type=submit]").prop("disabled", false);
} else {
token = response.id;
$form.append($("<input type=\"hidden\" name=\"payment[token]\" />").val(token));
$("[data-stripe=number]").remove();
$("[data-stripe=cvv]").remove();
$("[data-stripe=exp-year]").remove();
$("[data-stripe=exp-month]").remove();
$("[data-stripe=label]").remove();
$form.get(0).submit();
}
return false;
};
show_error = function (message) {
if($("#flash-messages").size() < 1){
$('div.container.main div:first').prepend("<div id='flash-messages'></div>")
}
$("#flash-messages").html('<div class="alert alert-warning"><a class="close" data-dismiss="alert">×</a><div id="flash_alert">' + message + '</div></div>');
$('.alert').delay(5000).fadeOut(3000);
return false;
};
});
And finally, go to controller and add those lines
if #register.save
#payment = Payment.new({email: params["school_registration"]["email"],
token: params[:payment]["token"], school_registration_id: #register.id
})
flash[:error] = "Please check registration errors" unless #payment.valid?
begin
#payment.process_payment
#payment.save
rescue Exception => e
flash[:error] = e.message
#register.destroy
render :new and return #=> :new means your registration form
end
else
#=> Code
end
This is actually one-time subscription and Stripe basic implementation if you implement this carefully and succeed you can whatever which you need.
And for more go to Rails Checkout Guide
Hope to help

Stripe payments routes charging the same for different charge buttons

I am trying to add two buttons to my payment platform. But stripe is charging me the same amount even when the other button is pressed. This is my attempt but it isn't working. I am not sure what is going wrong.
charges_controller.rb
def create
# Amount in cents - €1000.00
#amount = 100000
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:source => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'Thanks, on behalf of CMRF',
:currency => 'eur'
)
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
end
View
<%= form_tag charges_path, id: 'chargeForm' do %>
<script src="https://checkout.stripe.com/checkout.js"></script>
<%= hidden_field_tag 'stripeToken' %>
<%= hidden_field_tag 'stripeEmail' %>
<button id="customButton" class="btn btn-large btn-primary">Sponsor a Hole</button>
<script>
var handler = StripeCheckout.configure({
key: '<%= ENV["PUBLISHABLE_KEY"] %>',
token: function(token, args) {
document.getElementById("stripeToken").value = token.id;
document.getElementById("stripeEmail").value = token.email;
document.getElementById("chargeForm").submit();
}
});
document.getElementById('customButton').addEventListener('click', function(e) {
// Open Checkout with further options
handler.open({
name: 'My Company',
description: 'Entry (€1000.00)',
currency: 'eur',
amount: 100000,
billingAddress: true,
});
e.preventDefault();
});
</script>
<% end %>
<%= form_tag charges_path, id: 'chargeForm1' do %>
<script src="https://checkout.stripe.com/checkout.js"></script>
<%= hidden_field_tag 'stripeToken' %>
<%= hidden_field_tag 'stripeEmail' %>
<button id="customButton1" class="btn btn-large btn-success">Enter A Team</button>
<script>
var handler = StripeCheckout.configure({
key: '<%= ENV["PUBLISHABLE_KEY"] %>',
// image: '/assets/my_logo.png',
token: function(token, args) {
document.getElementById("stripeToken").value = token.id;
document.getElementById("stripeEmail").value = token.email;
document.getElementById("chargeForm").submit();
}
});
document.getElementById('customButton1').addEventListener('click', function(e) {
// Open Checkout with further options
handler.open({
name: 'My Company',
description: 'Sponsor (€200.00)',
currency: 'eur',
amount: 20000,
billingAddress: true,
// shippingAddress: true
});
e.preventDefault();
});
</script>
<% end %>
Routes.rb
Rails.application.routes.draw do
resources :charges
root 'pages#index'
get 'pages/about'
get 'pages/contact'
get 'pages/pay'
The amount and currency parameters that you pass to Checkout (in the call to handler.open(...)) are used for display purposes only.
The actual amount and currency that are used for the charge are the ones you provide in the amount and currency parameters of the charge creation request that is sent by your backend code.
In this case, the charge is created in your charges_controller.rb file, and the amount is fixed. If you want to charge different amounts, you'd need to send some other field along with the token so that your backend code can know which amount to charge.
Unless the amount is explicitly set by the customer (e.g. if you're accepting donations), you should not rely on an amount that is provided by the customer's browser as it would be very easy to change it. Instead, you likely want to use some identifier that you can use in your backend code to retrieve the correct amount.

How to send email based on date time in Rails?

I am having a "send email" functionality in my application which sends email to the email addresses.
Later a new functionality is added where when the user checks on "Send email later" radio button, selects date and time and clicks on send email button so that the email is sent at particular time.
For this I added a field into database with column "send_reports_at", added to controller and required actions.
The date time is stored as : "2016-11-01 16:15".
How to add the condition of sending email based on the date time in my application(I am new to delayed job and all)?
Please help.
This is my view:
<%= f.input :additional_emails, :label => false, :input_html => {:id => 'sponge_contacts', :placeholder => 'Separate emails by comma', :class => 'deliver_page_email', :type => 'text', :rows => 2, :style=> 'width:300px; border-color:#ccc; font-size:13px; padding:10px 0 0 10px; width: 295px; height: 110px; resize: none;'} %>
<input type="radio" id="send_email_0" name="send_email" value="now" checked> Send email now<br>
<input type="radio" id="send_email_1" name="send_email" value="later"> Send email later<br>
<%= f.input :send_reports_at,:label => false, :input_html => {:placeholder => 'Click here to select date and times', :class => 'deliver_page_email', :type => 'text', :rows => 2} %>
<% content_for :javascript do %>
<script type="text/javascript">
$( function() {
$( "#report_send_reports_at" ).datepicker();
} );
$(document).ready(function() {
$("#report_send_reports_at").hide();
$("#send_email_0, #send_email_1").bind('change click',function(){
toggleResult();
});
});
function toggleResult(){
result = $("[name='send_email']:checked").val();
if(result == "later"){
$("#report_send_reports_at").show();
}else{
$("#report_send_reports_at").hide();
}
}
</script>
<% end %>
<% end %>
<%= link_to "Send Now", '#', :class => "pink_button _round_5", :id => 'send_now_button' %>
This is my controller method:
def send_report_email
send_to_agents = params.has_key?("send_to_agents") && params["send_to_agents"] == "true"
if #report.photos.empty?
Screenshot.delay.new(#user.id, #report.id, Rails.application.config.custom.indicator_screenshot_bucket)
else
Screenshot.delay.new(#user.id, #report.id, "photo_screenshots")
end
Screenshot.delay.new(#user.id, #report.id, Rails.application.config.custom.report_screenshot_bucket)
if #report.update_attributes(params[:report])
set_photo_position(false)
#report.save
if send_to_agents
#report.update_attribute(:duplicable, true)
#good_emails, #bad_emails, #unsubscribed_emails = #user.account.users.map(&:email), [], []
else
#good_emails, #bad_emails, #unsubscribed_emails = filter_emails(#report.additional_emails)
#user.delay.add_new_contacts(#good_emails)
end
#good_emails.each do |email|
send_to_agents == true ? ReportMailer.delay.additional_emails(email, #user, #report, "A new report is available: #{#report.title}") : ReportMailer.delay.additional_emails(email, #user, #report)
end
ReportMailer.delay.report_sent(#user, #report, #good_emails, #bad_emails, #unsubscribed_emails)
end
respond_to do |format|
format.json { render :json => { :success => true, :report_id => #report.id, :redirect => user_reports_url(current_user), :notice => 'Report was successfully sent!' } }
end
end
You could write a function (for instance a class method for ReportMailer) that sends all of the email whose scheduled time matches or is less than the current time, then set a cron job to use rails runner to run that function as often as necessary.

Resources