Problems charging a card with Stripe in my Rails app - javascript

I'm building a Rails app that takes credit cards and I'm trying to use Stripe to do it. I'm having some issues passing the data from my app to Stripe in order to charge. That's what I'm hoping to get help with on this topic.
First, I have a standard form (with values instead of placeholders for quick submitting for testing purposes). The form successfully enters the name and email into the DB and the customer's "plan" is hardcoded in the controller for the time being:
<%= form_for #customer do |f| %>
<div class="payment-errors"></div>
<div class="name field">
<%= f.label :name %>
<%= f.text_field :name, :value => "Your name" %>
</div>
<div class="email field">
<%= f.label :email %>
<%= f.text_field :email, :value => "yourname#example.com" %>
</div>
<div class="cc_number field">
<%= label_tag 'cc_number' %>
<%= text_field_tag 'cc_number', nil, :value => "4242424242424242" %>
</div>
<div class="ccv field">
<%= label_tag 'ccv' %>
<%= text_field_tag 'ccv', nil, :value => "123" %>
</div>
<div class="cc_expiration field">
<%= label_tag 'cc_month', "Expiration date" %>
<%= text_field_tag 'cc_month', nil, :value => "12" %>
<%= text_field_tag 'cc_year', nil, :value => "2012" %>
</div>
<div class="actions">
<%= f.submit "Continue", :class => 'btn' %>
</div>
<% end %>
Also in my signups_view where the above code is, I have this JS, mostly provided by Stripe:
<script type="text/javascript">
// this identifies your website in the createToken call below
Stripe.setPublishableKey('<%= STRIPE['public'] %>');
function stripeResponseHandler(status, response) {
if (response.error) {
// show the errors on the form
$(".payment-errors").text(response.error.message);
$("input[type=submit]").removeAttr("disabled");
} else {
var form$ = $("form");
// token contains id, last4, and card type
var token = response['id'];
// insert the token into the form so it gets submitted to the server
form$.append("<input type='hidden' name='customer[stripe_token]' id='stripeToken' value='" + token + "'/>");
// and submit
$('.cc_number.field, .ccv.field, .cc_expiration.field').remove();
form$.get(0).submit();
}
}
$(document).ready(function() {
$("form").submit(function(event) {
// disable the submit button to prevent repeated clicks
$('input[type=submit]').attr("disabled", "disabled");
Stripe.createToken({
number: $('#cc_number').val(),
cvc: $('#ccv').val(),
exp_month: $('#cc_month').val(),
exp_year: $('#cc_year').val()
}, stripeResponseHandler);
// prevent the form from submitting with the default action
return false;
});
});
</script>
There seems to be a problem with the line form$.append("<input type='hidden' name='customer[stripe_token]' id='stripeToken' value='" + token + "'/>");, as my Ruby app breaks when it gets to customer[stripe_token].
Finally, in my `customers_controller`, I have:
def create
#customer = Customer.new(params[:customer])
#customer.product =
if #customer.save
save_order
redirect_to #customer
else
render action: 'new'
end
def save_order
Stripe.api_key = STRIPE['secret']
charge = Stripe::Charge.create(
:amount => 20,
:currency => "usd",
:card => #customer.stripe_token,
:description => "Product 1"
)
end
Whenever I submit the form, it hits the else clause in the controller each time and after plenty of debugging, Googling around and stripping out this from and rebuilding from scratch, I'm still stumped.
Any help would be very very much appreciated.
Edit: Added the customer model
attr_accessible :name, :email, :stripe_token, :product
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence => true,
:format => { :with => email_regex },
:length => { :minimum => 6, :maximum => 60 },
:uniqueness => { :case_sensitive => false }
validates :name, :length => {:minimum => 2, :maximum => 80 }

It would help to see your Customer model to get an idea of what's going on. If #customer.save returns false, it means that a validator is likely failing.
Also, do you have stripe_token as an accessible attribute on your model? Otherwise you won't be able to assign it form the form like you're doing. Note that the token should not be stored in the database, since it can only be used once.
class Customer
attr_accessor :stripe_token # do you have this?
end
One more note: you will probably want to store a Stripe ID field so that you can retrieve customer payments and cancel their account later.

Related

Rails - How to submit a form without changing the page and updating the view?

Hello all !
Im using rails and in the steps I would like the user to (on the same page) :
Enter his address
Filling the form
Submit the form
Click to submit
Update the view to show the address
The view updated
Should I use Stimulis or Ajax? I don’t quite understand which would be more useful!
Because i try to use simply JS but it’s was not DRY and not really simple:
// file.js
document.querySelector(".form").addEventListener('submit', function () {
adress_form = document.querySelector(".adress_form");
adress_form.style.display="none";
document.location.reload();
display_adress = document.querySelector(".display_adress");
display_adress.style.display = "block";
});
#file.html.erb
<div class="display_adress">
<% if current_user.line1? && current_user.postal_code? && current_user.city? %>
<p>Mon adresse actuel : <%= current_user.line1 %>, <%= current_user.city %> <%= current_user.postal_code %></p>
<% end %>
</div>
<div class="address_form">
<%= simple_form_for(current_user, remote: true, html: { id: "addddd"}) do |f| %>
<%= f.input :line1, label: 'Mon adresse' %>
<%= f.input :city, label: 'Ville' %>
<%= f.input :postal_code, label: 'Code Postal' %>
<%= f.submit %>
<% end %>
</div>
To resume, i want the user to enter his address on the form, to submit, to update my database and to update the view with the new address the user submitted
Thanks for helping!
You can respond in the controller javascript. For example:
file.html.erb
<%= simple_form_for(current_user, remote: true, format: :js, html: { id: "addddd"}) do |f| %>
users_controller.rb
def create
# do your business logic
render 'create
end
users/create.js.erb
document.getElementById("formId").innerText = "Othe html text or you can call render(*)"

Cannot Attach ajax:error handler to Form ID

I am having trouble binding an ajax:error response to a form ID. Can someone point me in the right direction to solve this problem?
Here is the code.
<%= form_for #person,
html: {
id: '#person_form',
class: 'js_inline_validate'
},
data: { validate_url: validate_field_people_path },
remote: true,
url: people_path do |f| %>
<div class="form-group">
<div class='form-group-label'>
<%= f.label :first_name %>
</div>
<%= f.text_field :first_name,
:id => 'first_name',
class: 'js_new_person_frm_validate js_new_person_first_name' %>
<div class="text-error small">
<span>
<%= #person.errors.full_messages_for(:first_name).first if #person.errors[:first_name].any? %>
</span>
</div>
</div>
<div class="form-group">
<div class='form-group-label'>
<%= f.label :last_name %>
</div>
<%= f.text_field :last_name,
:id => 'last_name',
class: 'js_new_person_frm_validate js_new_person_last_name' %>
<div class="text-error small">
<span>
<%= #person.errors.full_messages_for(:last_name).first if #person.errors[:last_name].any? %>
</span>
</div>
</div>
<div class="form-group">
<%= f.submit "Create", data: { disable_with: false } %>
</div>
<% end %>
Controller
class PeopleController < ApplicationController
......
def create
#person = Person.new(create_params)
if(#person.save)
flash[:success] = "person created successfully"
redirect_to root_path
else
respond_to do |format|
format.html { render :action => 'new' }
format.any(:js, :json) {
render :json => { :error => #person.errors }, :status => 422
}
end
end
end
.....
end
CoffeeScript
$ ->
$("#person_form").on "ajax:error", (event, data, status, xhr) ->
alert "ajax:error!"
#do more stuff
The issue I cannot solve is this works as below when I use 'document', but I need this to be specific to my #person_form. And I get no response when I replace document with my form_id. Am I missing something simple here?
$ ->
$(document).on "ajax:error", (event, data, status, xhr) ->
alert "ajax:error!"
#do more stuff
Thank You!
I'm leaving this simple answer out here for anyone else who may run across this issue. Hopefully, it's this simple for the next person. It took me HOURS to track down the problem.
It turns out the error was happening because of a very simple mistake:
<%= form_for #person,
html: {
id: '#person_form', <= ERROR IS HERE!!
class: 'js_inline_validate'
},
data: { validate_url: validate_field_people_path },
remote: true,
url: people_path do |f| %>
The #person_form should have been person_form (excluding the pound sign). Just a very simple error in code I didn't catch when switching from script to ruby.
The global event handler would catch the ajax:error and execute the code properly. The pound sign in the identifier was preventing the proper registration of the local ajax event.

Rails 4 and Stripe Card Processing Error

I'm new to Rails and I have spent a good chunk of today struggling with the following issue. I'm integrating Stripe into my Rails application. All was going well until I try to actually finish the order and I get the following error in my local host
Stripe::InvalidRequestError in OrdersController#create
You must supply either a card or a customer id
I have tried various fixes all to no avail including disabling turbolinks on the whole app. I will really appreciate any help I can get since I'm not sure how to proceed from here. I have attached the relevant code below
Orders Controller
def create
#order = Order.new(order_params)
#place = Place.find(params[:place_id])
#seller = #place.user
#order.place_id = #place.id
#order.buyer_id = current_user.id
#order.seller_id = #seller.id
Stripe.api_key = ENV["STRIPE_API_KEY"]
token = params[:stripeToken]
puts "Token is #{token}"
begin
charge = Stripe::Charge.create(
:amount => (#place.rating * 100).floor,
:currency => "usd",
:card => token
)
flash[:notice] = "Thanks for ordering!"
rescue Stripe::CardError => e
flash[:danger] = e.message
end
respond_to do |format|
if #order.save
format.html { redirect_to root_url }
format.json { render :show, status: :created, location: #order }
else
format.html { render :new }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
# FURTHER DOWN BELOW
private
def set_order
#order = Order.find(params[:id])
end
def order_params
params.require(:order).permit(:address, :city, :state, :card_number, :card_code, :'data-stripe' )
end
end
CoffeeScript
jQuery ->
Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'))
payment.setupForm()
payment =
setupForm: ->
$('#new_order').submit ->
$('input[type=submit]').attr('disabled', true)
Stripe.card.createToken($('#new_order'), payment.handleStripeResponse)
false
handleStripeResponse: (status, response) ->
if status == 200
$('#new_order').append($('<input-type="hidden" name="stripeToken"/>').val(response.id))
$('#new_order')[0].submit()
else
$('#stripe_error').text(response.error.message).show()
$('input[type=submit]').attr('disabled', false)
Orders View
<div class="form-group">
<div class="row">
<div class="col-md-8">
<%= label_tag :card_number, "Credit Card Number" %>
<%= text_field_tag :card_number, nil, {:name => nil, :'data-stripe' => "number" , class: "form-control" } %>
</div>
<div class="col-md-4">
<%= label_tag :card_code, "CVC" %>
<%= text_field_tag :card_code, nil, {:name => nil, :'data-stripe' => "cvc" , class: "form-control" } %>
</div>
</div>
</div>
<div class="form-group">
<%= label_tag nil, "Expiration Date" %>
<div class="row">
<div class="col-md-3">
<%= select_month nil, { use_two_digit_numbers: true }, { :name => nil, :'data-stripe' => "exp-month", class: "form-control"} %>
</div>
<div class="col-md-3">
<%= select_year nil, { start_year: Date.today.year, end_year: Date.today.year+10 }, { :name => nil, :'data-stripe' => "exp-year", class: "form-control"} %>
</div>
</div>
</div>
Also upon another recommendation, I added the following
puts "Token is #{token}"
to the controller and I get
Token is _____
So I get a blank space in the terminal next to where the token is. Is the token not passing through here or what is going on?
Can anyone please help me to solve this? I'm not sure what to do anymore and I have spent the better half of today on this problem. Thank you!
The createToken method requires a JS object with number and expiry date values. I don't believe you're sending those to it. Try passing those explicitly, and putting breakpoints after the method in your browser debugger to see what happens.

RoR how to submit form with :remote=>true and then execute javascript code

I want to be able to submit a form remotely (so the page does not refresh) and then have some javascript code get executed. Here's what I got so far
<script>
$(function() {
// Subscribe to receive messages!
var client = new Faye.Client('http://localhost:9292/faye');
// Our own private channel
var private_subscription = client.subscribe('<%=request.path + #id1.to_s + #id2.to_s%>',
function(data) {
$('<p></p>').html(data.username + ": " + data.msg).appendTo('#chat_room');
});
// Handle form submission to publish messages.
$('#new_message').submit(function(){ // THIS LINE GIVING TROUBLE
client.publish('<%=request.path + #id1.to_s + #id2.to_s%>', {
username: '<%= #user.fname+' '+#user.lname%>',
msg: $('#message_message').val()
});
// Clear the message box
$('#message_message').val('');
return false;
});
});
</script>
<div class="chat_container">
<div id="chat_room">
<p>Chat with <%= #other.fname+' '+#other.lname%> </p>
</div>
<%= simple_form_for Message.new, :id => "new_message",:controller => "messages", :action => "create", :remote => true do |f| %>
<%= f.text_field :message, :autocomplete => "off" %>
<%= f.hidden_field :sender_id, :value => #user.user_id %>
<%= f.hidden_field :receiver, :value => #other.user_id %>
<%= f.hidden_field :sent_at, :value => Time.now %>
<%= f.submit "Send", class: "btn btn-medium btn-primary" %>
<% end %>
</div>
If I change the name of my javascript function "#new_message" to anything else, the form submits properly into my database, but I lose the chatroom functionality. (The code that transmits the message to all clients in that chat room, via FAYE, and clears the text_box will not get called.)
If I keep the name of my javascript function "#new_message", the message will get transmitted to all clients in the chatroom, and the textbox will get cleared, but nothing will get stored into my database.
What can I do so that I can accomplish both the data storage and the chatroom functionality?
EDIT:Attempted this to no avail
$('#new_message').bind('ajax:success', function() {
client.publish('<%=request.path + #id1.to_s + #id2.to_s%>', {
username: '<%= #user.fname+' '+#user.lname%>',
msg: $('#message_message').val()
});
// Clear the message box
$('#message_message').val('');
return false;
});
});
I think you just need to hook into one of the ajax callbacks for the form submit:
$("#new_message").bind("ajax:success", function(evt, data, status, xhr) {
// Your client code and message clearing code
});

Validation before saving Rails

I would like to carry out a validation before saving by determining if a User has filled in a particular field, the Payment amount field below and chosen status = "Closed" before submitting the form. If he does one without the other then the form should not save
Edit page
<%= simple_form_for #invoice, :html => { :class => 'form-horizontal' } do |f| %>
<%= render "shared/error_messages", :target => #invoice %>
<%= f.association :customer, disabled: #invoice.persisted? %>
<%= f.input :due_date, as: :string, input_html: { class: "datepicker" }, disabled: #invoice.persisted? %>
<%= f.input :invoice_date, as: :string, input_html: { class: "datepicker" }, disabled: #invoice.persisted? %>
<%= f.input :payment_method, as: :select, :collection => [['Cash','Cash'],['Cheque','Cheque'],['In-House transfer','In-House transfer'],['Account Ledger','Account ledger']], :selected => ['Cash','Cash'] %>
<%= f.input :reference_no, :label => 'Payment Reference No', as: :string %>
<%= f.input :amount, as: :string %>
<%= f.input :payment_date, as: :string, input_html: {class: "datepicker"} %>
<%= f.input :status, as: :select, collection: Invoice::VALID_STATUS %>
VALID_STATUS = [ 'Draft', 'Open', 'Closed', 'Void' ] in Invoice.rb
I would like that if the user changes the Status to Closed he should have entered an amount in the form. A user should not be able to change status to closed without entering an amount
In the model (app/models/invoice_model.rb) put
validate :close_must_have_amount
Then define it (same file)
def close_must_have_amount
:status == 'closed' && :amount # May need to tweak this
end
To have the model level validations applied client side you can use
https://github.com/bcardarella/client_side_validations/
1) Javascript Form Validation is generally done by names.
function ValidateForm(){
var form = document.forms['myForm'];
if ((form['status'].value == "Closed") && !(form['amount'].value)){
alert("You gave a 'Closed' status value, but did not provide an amount, please rectify this problem!");
return(false);
} else {
return(true);
}
}
And then:
<%= simple_form_for #invoice, :onsubmit => "ValidateForm();", :html => { :class => 'form-horizontal', :name => 'myForm' } do |f| %>
<%= f.input :amount, :html => { :name => 'amount'}, as: :string %>
<%= f.input :status, as: :select, :html => { :name => 'status'}, collection: Invoice::VALID_STATUS %>
A brief walkthrough onSubmit triggers when a form is submitted, but before it is actually posted to the server.
A javascrtipt function that is trigered by an event and terminates with return(false); will immediately terminate the event, while return(true); (or pretty much anything else really) makes the event continue as planned.
Finally, be aware that relying exclusively on client side validation is a terrible idea, as a determined user could do something like:
1) Make a perfectly legitimate submission with firebug open and inspect the headers etc.
2) Craft their own HTTP request containing bogus/bad data.
3) Submit it through any one of the myriad HTTP tools.
Clientside Validation is a "nice to have".
Serverside Validation is a "must have".
If you want to do it in client side:
<script>
$(document).ready(function(){
$('#status').change(function(){
if($(this).val() == "Closed" && ($('#amount').val() == null || $('#amount') == "")){
alert("Amount must be needed when status is closed")
}
});
});
</script>

Categories

Resources