Sporadic issues with Stripe javascript in Rails app - javascript

I have deployed a custom Stripe payment form in our app, and 97% of the time it works just fine. Occasionally, I see Stripe report an error on an attempted payment.
POST /v1/customers
error:
type: "invalid_request_error"
message: "You passed an empty string for 'card'. We assume empty values are an attempt to unset a parameter; however 'card' cannot be unset. You should remove 'card' from your request or supply a non-empty value"
param: "card"
This is raised when creating a customer with the Stripe token that should've been generated from the javascript:
sale = current_user.sales.create(
plan_id: plan.id,
amount: plan.price_in_cents,
stripe_token: params[:stripeToken]
)
After much troubleshooting I've confirmed that this is happening because Stripe is not getting called in the first place to create the token. In other words, this is not an issue of passing params, where the token is generated but simply not passed to the controller. For some reason, something in the javascript is not running, so the form simply submits without it calling Stripe.
Again remember that 97% of the time this issue does not occur. When it does occur it happens to the same user over and over again. I have not found any pattern with browsers (it has happened on Chrome, IE, Firefox). Also, eventually users facing this problem have been able to successfully pay using the same browser and without any settings adjustments. This makes me think there's something potentially with my server environment and not the client.
Here is my full javascript:
subscription.js
var stripeResponseHandler = function(status, response) {
var $form = $('#paid_subscription');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
$form.find('button').prop('disabled', false);
} else {
// token contains id, last4, and card type
var token = response.id;
// Insert the token into the form so it gets submitted to the server
$('#stripe_card_token').val(token);
//$form.append($('<input type="hidden" name="stripeToken" />').val(token));
// and submit
$form.get(0).submit();
}
};
jQuery(function($) {
$('#card_number').payment('formatCardNumber')
$('#paid_subscription').submit(function(event) {
// var expiration = $("#card-expiry").payment("cardExpiryVal")
// $('#card-exp-month').val(expiration.month);
// $('#card-exp-year').val(expiration.year);
var $form = $(this);
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
Stripe.card.createToken($form, stripeResponseHandler);
// Prevent the form from submitting with the default action
return false;
});
});
And here is the controller code, just in case:
charges#create
def create
token = params[:stripeToken]
bundle = Bundle.find_by_id(params[:bundle_id])
if bundle
process_bundle_sale(bundle, token)
else
plan = Plan.find(params[:plan_id])
begin
sale = current_user.sales.create(
plan_id: plan.id,
amount: plan.price_in_cents,
stripe_token: params[:stripeToken]
)
sale.process!
if sale.finished?
if current_user.subscribe_with_referer({plan_id: plan.id}, session[:http_referer])
TrackSubscriptions.track_paid_subscription(cookies, plan, sale)
flash[:success] = "Success! You now have full access to \"#{plan.title}\""
redirect_to plan_path(plan)
else
flash[:alert] = "Oops something went wrong. Please contact support and we'll get to the bottom of it."
redirect_to plan_path(plan)
end
else
flash[:alert] = sale.error
redirect_to new_plan_subscription_path(plan.slug)
#redirect_to plan_path(plan)
end
end
end
end
Finally here's the form code:
<%= simple_form_for #charge = Sale.new, :url => charges_path, :method => :post, html: { id: :paid_subscription } do |f| %>
<fieldset class = "inputWrapper">
<h2 class="sub-header">Payment</h2>
<span class="payment-errors"></span>
<%= hidden_field_tag :plan_id, #plan.id %>
<%= hidden_field_tag :stripeToken, nil,id: :stripe_card_token %>
<div class="payment-fields">
<div class="field card-number-field">
<label class="control-label">Card Number</label>
<%= text_field_tag :card_number, nil, name: nil, placeholder: "4444 1234 1234 1234",:data => {:stripe => 'number' } %>
</div>
<div class="field security-code-field">
<label class="control-label">Security Code</label>
<%= text_field_tag :card_code, nil, name: nil, placeholder: "123", :data => {:stripe => 'cvc' } %>
</div>
<div class="field expiry-field">
<label class="control-label">Exp (MM/YYYY)</label>
<div class="month-field">
<%= text_field_tag :exp_month, nil, name: nil, placeholder: "10", id: "card-exp-month", maxlength: 2, data: { stripe: "exp-month" } %>
<span class="slash"> / </span>
</div>
<div class="year-field">
<%= text_field_tag :exp_year, nil, name: nil, placeholder: "2016", id: "card-exp-year", maxlength: 4, data: { stripe: "exp-year" } %>
</div>
</div>
<div style="clear:both"></div>
</div>
<%= render 'end_form', :plan => #plan %>
<div class="submit-button">
<%= f.submit 'Register for Course', :class => "button greenButton", :error => false %>
</div>
</fieldset>
<% end %>

Related

Submit stops working after first successful submit

I have a simple form in a rails app that is rendered within a partial called roster_panel:
<div id = 'active_admin_content'>
<%= semantic_form_for #roster_item do |form| %>
<h3>Add a counselor</h3>
<div id="counselor_status_message"><%= #status_message %></div>
<%= form.inputs do %>
<%= form.input :first_name, input_html: {id: 'counselor_first_name'} %>
<%= form.input :last_name, input_html: {id: 'counselor_last_name'} %>
<%= form.input :email, input_html: {id: 'counselor_email'} %>
<div class="button_container" >
<input id="submit_counselor_add" type="button" value = "Send forms packet" class = "button" >
</div>
<% end %>
<% end %>
</div>
In my jquery code, I tie the submit click to this:
$( document ).on('turbolinks:load', function() {
$("#submit_counselor_add").click(function(){
$.get("add_counselor_info" , { id: $("#roster_id").text(), first_name: $("#counselor_first_name").val(),
last_name: $("#counselor_last_name").val(), counselor_email: $("#counselor_email").val() },
function(data){ $("#roster_panel").html(data);
}
)
});
});
This routes to this controller method:
def add_counselor_info
#roster = Roster.find(params[:id])
#group = ScheduledGroup.find(#roster.group_id)
#liaison = Liaison.find(#group.liaison_id)
#items = RosterItem.where(roster_id: #roster.id)
#roster_item = RosterItem.new(group_id: #group.id, roster_id: #roster.id,
first_name: params[:first_name], last_name: params[:last_name],
email: params[:counselor_email], youth: false, item_status: 'Unconfirmed' )
if #roster_item.save
#error_count = 0
#status_message = 'This counselor has been added and the forms package has been emailed. You may enter another counselor.'
#items = RosterItem.where(roster_id: #roster.id)
#roster_item = RosterItem.new
else
#error_count = #roster_item.errors.size
#status_message = "#{#error_count} errors prevented saving this information: "
#roster_item.errors.full_messages.each { | message | #status_message << message << ' '}
#items = RosterItem.where(roster_id: #roster.id)
end
render partial: 'roster_panel'
end
After a fresh page load, this process works fine and redisplays the form as expected. However, at that point the submit button no longer triggers the action in jquery. Other jquery functions on the page still work, however. This may have something to do with turbolinks, which I am not very familiar with.
Any help would be appreciated!
After submitting the form, the DOM is replaced by the new one, so on click event binding is being lost.
Try to bind this event through the parent element, which won't be overridden by javascript (e.g. body):
$( document ).on('turbolinks:load', function() {
$("body").on('click', '#submit_counselor_add', function() {
$.get("add_counselor_info", {
id: $("#roster_id").text(),
first_name: $("#counselor_first_name").val(),
last_name: $("#counselor_last_name").val(),
counselor_email: $("#counselor_email").val()
},function(data) {
$("#roster_panel").html(data);
});
});
});

Only allow Stripe Purchases on certain model parameters

Rails newbie here, working with Stripe's API. I have a model in my rails app called pieces. Each piece has an integer called status. I only want people to be able to purchase the piece if the piece has a status of 1. In my current code, I have hid the buy button on the pieces unless the piece of the status is 1. This works most of the time, but, if two people view the piece at the same time, then they can both buy them. This is because the status of the piece on the other page does not update until the page is reloaded.
My request: I want to find a way to check that the piece's status is 1, right before someone buys it. If it is not 1, I want there to be a rails flash message saying that the piece has already been bought. This should prevent the user from being charged and prevent a charge from being created.
Here is my charges controller:
class ChargesController < ApplicationController
def create
piece = Piece.find(params[:piece_id])
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:source => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => piece.total_price_in_cents,
:description => piece.title,
:currency => 'usd'
)
purchase = Purchase.create(
customer_email: params[:stripeEmail],
total_transaction: piece.total_price,
stripe_fee: piece.stripe_fee,
taxes: piece.taxes,
artist_cut: piece.artist_cut,
charity_cut: piece.charity_cut,
our_cut: piece.our_cut,
currency: charge.currency,
card: params[:stripeToken],
description: charge.description,
customer_id: customer.id,
piece_id: piece.id,
customer_name: params[:stripeShippingName],
customer_address_line_1: params[:stripeShippingAddressLine1],
customer_city: params[:stripeShippingAddressCity],
customer_state: params[:stripeShippingAddressState],
customer_zip_code: params[:stripeShippingAddressZip],
customer_country: params[:stripeShippingAddressCountry],
seller_name: piece.user.name,
seller_email: piece.user.email,
seller_address_line_1: piece.user.address_line_1,
seller_address_line_2: piece.user.address_line_2,
seller_city: piece.user.city,
seller_state: piece.user.state,
seller_zip_code: piece.user.zip_code
)
purchase.ship_by = purchase.created_at + 7.days
purchase.arrive_by = purchase.created_at + 21.days
purchase.save!
piece.status = 3
piece.save!
redirect_to pieces_path, notice: "Thanks for buying #{piece.title} for $#{'%.2f' % piece.total_price}. You should get an email shortly."
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to new_charge_path
end
end
And here is my show page for the pieces, which has the stripe buy button on it:
<div class="container">
<div class="row">
<div class="col-md-offset-2 col-md-8">
<div class="panel panel-default">
<div class="panel-body">
<!-- Stripe Form -->
<% if current_user != #piece.user && #piece.status == 1 %>
<%= 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="btn-buy-show" type="button" class="btn btn-success btn-lg btn-block">Buy for $<%= number_with_precision(#piece.total_price, :precision => 2, :delimiter => ',')%></button>
<script>
var handler = StripeCheckout.configure({
key: '<%= Rails.configuration.stripe[:publishable_key] %>',
shippingAddress: true,
token: function(token, arg) {
document.getElementById("stripeToken").value = token.id;
document.getElementById("stripeEmail").value = token.email;
document.getElementById("chargeForm").submit();
}
});
document.getElementById('btn-buy-show').addEventListener('click', function(e) {
handler.open({
name: 'Metallic Palette',
description: '<%= #piece.title %> ($<%= number_with_precision(#piece.total_price, :precision => 2, :delimiter => ',')%>)',
amount: document.getElementById("amount").value
});
e.preventDefault();
})
</script>
<% end %>
<% end %>
<% if #piece.status == 3 %>
<p>This piece has already been bought.
<% if current_user == #piece.user || admin_user_signed_in? %>
<%= render 'pieces/purchase_details' %>
<% end %>
</p>
<% end %>
</div>
</div>
</div>
Thank you guys so much.
You just need to check it :)
def create
piece = Piece.find(params[:piece_id])
if piece.status != 1
flash[:error] = 'Piece is not available :('
return redirect_to peaces_path # or whatever
end
...
end
However, few suggests for you:
1) Don't use numbers in your code. Make constants (like Piece::AVAILABLE) and methods (piece.available?) or use enum. You will have lots of problems in future, if you will just use your numbers
2) Extract form object - you don't want to have so large methods in your controller. If you extract it to separate service - it will be much more testable, predictable and updatable.
3) Extract Piece.find and piece status check into before_filters.

Can't save class on Parse.com

Why doesn't this code allow me to save my user class from my website to parse.com?
When I run this code outside of the submit function it works fine and when I run it inside of the submit function, I get to the alert: "check" but never to "save". So for some reason inside of this function .save() does not work. I'm running this on Ruby On Rails if that helps.
Parse.initialize("xxxxx", "xxxxx");
$(document).ready(function() {
$("#new_user").submit(function() {
var User = Parse.Object.extend("_User");
var user = new User();
user.set("name","john doe");
user.set("username","fff29#test.edu");
user.set("email","fff29#test.edu");
user.set("password","password123");
alert("check");
user.save();
alert("save");
});
});
I changed my code to this but it's still not working:
Parse.initialize("xxxxx", "xxxx");
$(document).ready(function() {
$("#new_user").submit(function() {
var user = new Parse.User();
user.set("username", "my name");
user.set("password", "my pass");
user.set("email", "email#example.com");
user.signUp(null, {
success: function(user) {
// Hooray! Let them use the app now.
},
error: function(user, error) {
// Show the error message somewhere and let the user try again.
alert("Error: " + error.code + " " + error.message);
}
});
});
});
This is copied and pasted off the parse website so it must be something wrong with my webpage and not my parse code i'm guessing. Here's my view for the sign up page:
<!DOCTYPE html>
<html>
<head>
<% content_for :title, "Join Dot" %>
<%= javascript_include_tag "user-bundle" %>
<script src="//www.parsecdn.com/js/parse-1.6.12.min.js"></script>
</head>
<div class="title">
<p>Sign Up</p>
</div>
<%= form_for #user do |f| %>
<%= f.text_field :name, :placeholder => "Full Name", :id => "name" %>
<%= f.email_field :username, :placeholder => "Email", :id => "username" %>
<%= f.password_field :password, :placeholder => "Password", :id => "password"%>
<%= f.submit "Join", class: "btn-submit" %>
<% end %>
</html>
Step through the process. Log out 'user' before saving and make sure it's a valid parse class. You may need to change Parse.Object.extend("_User") to new Parse.User() and user.save() to user.signUp().

Completely Stuck on AJAX Form Submission With Stripe

I am so frustrated. I'm working with Stripe to create a form submission system for payments. Basically, the form makes an AJAX call to Stripe, which gives me a token on success, which I then use to resubmit the form, also through AJAX. If the form is successful, it redirects to a new page, if not, it populates the form with error messages without re-navigation. Here's my form:
<%= form_for([#issue, #issue_order]) do |f| %>
<% if #issue_order.errors.any? %>
<div class="error_messages">
<h2><%= pluralize(#issue_order.errors.count, "error") %> occurred. </h2>
<ul>
<% #issue_order.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% f.hidden_field :issue_id %>
<%= f.hidden_field :stripe_card_token %>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="field">
<%= label_tag :card_number, "Credit Card Number " %>
<%= text_field_tag :card_number, nil, name: nil %>
</div>
<div class="field">
<%= label_tag :card_code, "Security Code on Card (CVV) " %>
<%= text_field_tag :card_code, nil, name: nil %>
</div>
<div class="field">
<%= label_tag :card_month, "Card Expiration " %>
<%= select_month nil, {add_month_numbers_true: true}, {name: nil, id: "card_month"} %>
<%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year + 15}, {name: nil, id: "card_year"} %>
</div>
<div id="stripe_error"></div>
<div class="actions"><%= f.submit "Purchase Issue", id: "submit_issue_order" %></div>
<% end %>
<div class="errors"></div>
Here is the javascript that handles the form and sets up the stripe information:
var issueOrder;
$(function() {
Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'));
issueOrder.setupForm();
});
var issueOrder = {
setupForm: function() {
$('#new_issue_order').submit(function(e) {
e.preventDefault();
$('#submit_issue_order').attr('disabled', true);
issueOrder.processCard();
return false;
});
},
processCard: function() {
var card;
card = {
number: $('#card_number').val(),
cvc: $('#card_code').val(),
expMonth: $('#card_month').val(),
expYear: $('#card_year').val()
};
Stripe.createToken(card, issueOrder.handleStripeResponse)
},
handleStripeResponse: function(status, response) {
if (status == 200) {
$('#issue_order_stripe_card_token').val(response.id);
// $('#new_issue_order')[0].submit();
$.ajax({
type: "POST",
url: $('#new_issue_order').attr('action'),
data: { "issue_order": {
"stripe_card_token": $('#issue_order_stripe_card_token').val(),
"email": $('issue_order_email').val(),
},
"issue_id": $('#issue_order_issue_id').val()
},
dataType: "script"
}, issueOrder.processOrder);
}
else {
$('#stripe_error').text(response.error.message);
$('input[type=submit]').attr('disabled', false)
}
}
And here is my controller:
def create
charge = Stripe::Charge.create(
:amount => 400,
:currency => "usd",
:card => params['issue_order']['stripe_card_token']
)
if charge['paid'] == true
#issue_order = IssueOrder.new(email: params['issue_order']['email'], issue_id: params['issue_id'])
if #issue_order.save
#pdf_token = #issue_order.pdf_token
PdfMailer.pdf_email(params['issue_order']['email'], #issue_order).deliver
else·
flash[:error] = []
flash[:error].push("Your card was charged, but sadly we were unable to create·
a record in the database. Please contact us for your copy of the issue.")
respond_to do |format|
format.js
end
end
else
# run checks for errors and return error messages
flash[:error] = []
flash[:error].push("There was an error in processing your payment.")
render :json => {success: false}
end
end
Typical stripe setup stuff. Works fine when the stripe order is successfully processed. Well, it did before I hand-rolled the AJAX call, I'm assuming it'd be fine if I threw a respond_to |format| in the success case in the controller that redirected to the success page. However, for the error cases, my controller renders create.js.erb, which looks like this:
console.log('yo');
$('.errors').empty();
errors = xhr.getResponseHeader('X-Flash-Error').split(',');
<% flash[:error].each do |error| %>
$('.errors').append($('<p>' + <%= error %> + '</p>'));
<% end %>
setTimeout(function() {
$('.errors').empty();
}, 3500);
The controller clearly reaches the file and renders it, as evidenced by the logs:
Started POST "/issues/1/issue_orders" for 127.0.0.1 at 2013-11-12 23:33:17 -0500
Processing by IssueOrdersController#create as JS
Parameters: {"issue_order"=>{"stripe_card_token"=>"tok_102vmu2pSkyWUgPAToj334Oa"}, "issue_id"=>"1"}
(0.4ms) BEGIN
(0.4ms) ROLLBACK
Rendered issue_orders/create.js.erb (0.1ms)
["Your card was charged, but sadly we were unable to create \n a record in the database. Please contact us for your copy of the issue."]
Completed 200 OK in 1363ms (Views: 3.9ms | ActiveRecord: 0.8ms)
But NOTHING happens on my page, including the console log! If I don't hand roll the AJAX and use remote: true, it gets even worse -- it recognizes my submit() call as HTML and doesn't know what to do with the format, rendering an Unknown Format error.
Help!

Model validation on Popup Login form

I stuck here need help
Ruby on rails
I have one Pop-up for login it is coming when i click on link "Sign in" using jquery
Now i want to validate username and password entered by user from database
Login popup is a partial page sign in.html.erb
any solutions. plz
depends on which validation framework you are using.
the key is to keep the dialog not closed once server responses.
(NOT RECOMMENDED) if you are not using any javascript validation framework, you have to make the "form" a "remote form", then submit it, then display the error messages if validation failed.
(MY EXPERIENCE) if you are using an javascript validation framework such as RSV(really simple validation), just define the validation rules, then implement 2 ajax methods: 1 is used for validating the form, another is a callback function used to process the response from remote and display it in the dialog.
anyway, dealing with the error message for a dialog is not very easy and straightforward than dealing with it for a regular page. Anyway, I hope you got my idea.
Here is my login partial which i have render and also validate it.
<div class="clost_holder"> <%= link_to image_tag("/assets/close-new.png"), "javascript:;", :onclick => "close_popup()" %>
</div>
<%= form_for(#user, :as => #user, :url => session_path(#user), :html => {:id => 'sign_in_form', :onsubmit => "return false;"}) do |f| %>
<div class="login_input_holder mtop20 flt">
<label>Email</label>
<p><%= link_to 'dont have an account ?', :controller => 'devise/registrations', :action => 'new' %></p>
<%= f.email_field :email %>
</div>
<div class="login_input_holder mtop10 flt">
<label>Password</label>
<%= f.password_field :password %>
</div>
<div class="fogot_password "><%= link_to 'forgot password ?', :controller => 'devise/passwords', :action => 'new' %></div>
<div class="login_btn frt"><input type="submit" value="signin" id='sign_in_btn'></div>
<% end %>
And then validate it.But you need to add jquery.validate and jquery.form files also.
<script type="text/javascript">
$("#sign_in_btn").click(function() {
if (!jQuery("#sign_in_form").valid()) {
return false;
}
else {
var container = $("#home1_content");
$("#sign_in_form").submit(function() {
$(this).unbind('submit').ajaxSubmit({
success: function(data) {
container.html(data);
window.location.reload();
}
})
});
}
});
$(document).ready(function() {
$("#sign_in_form").validate({
rules:{
"user[email]":
{
required: true ,
email: true
},
"user[password]":
{
required: true
},
messages: {
"user[email]": "This field is required",
"user[password]": "This field is required"
}
}
});
});
</script>

Categories