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!
Related
I'm learning Turbo Frames and Streams + Stimulus so it's possible I'm not 100% on track. I have a form for creating a new object, but within the form I'd like to have a select component that will display certain fields depending on the selection. It's important to note that due to this, I do not want to submit the form until the user has made this selection.
This is what I have:
_form.html.erb
<div class="form-group mb-3">
<%= form.label :parking, class: 'form-label' %>
<%= form.number_field :parking, class: 'form-control' %>
</div>
<%= turbo_frame_tag "turbo_transactions" do %>
<%= render 'property_transactions' %>
<% end %>
_property_transactions.html.erb
<div class="form-group mb-3" data-controller="property-transaction">
<%= label_tag :property_transactions, 'Property in:', class: 'form-label' %>
<%= select_tag :property_transactions, options_for_select(#property_transactions.collect {|p| [p.name, p.id]}), { data:
{ action: "property-transaction#redraw", property_transaction_target: 'transaction', turbo_frame: 'turbo_transactions' },
class: 'form-control', prompt: '', autocomplete: 'off' } %>
</div>
<% if #property_transaction %>
<%= turbo_frame_tag #property_transaction.en_translation_key do %>
<div class="form-group mb-3">
<%= render #property_transaction.en_translation_key %>
</div>
<% end %>
<% end %>
property_transaction_controller.js
import { Controller } from "#hotwired/stimulus";
import Rails from "#rails/ujs";
export default class extends Controller {
static targets = [ "transaction" ];
redraw() {
const params = { property_transaction: this.transaction };
Rails.ajax({
type: 'post',
dataType: 'json',
url: "/set_property_transaction",
data: new URLSearchParams(params).toString(),
success: (response) => { console.log('response', response) }
});
}
get transaction() {
return this.transactionTarget.value;
}
}
property_controller.rb
def set_property_transaction
respond_to do |format|
format.json
format.turbo_stream do
if #property_transactions
#property_transaction = #property_transactions.select { |p| p.id == property_transaction_params }
else
#property_transaction = PropertyTransaction.find(property_transaction_params)
end
end
end
end
set_property_transaction.turbo_stream.erb
<%= turbo_stream.replace #property_transaction.en_translation_key %>
_rent.html.erb
<%= turbo_frame_tag "rent" do %>
<!-- some input fields -->
<% end %>
_rent_with_option_to_buy.html.erb
<%= turbo_frame_tag "rent-with-option-to-buy" do %>
<!-- other input fields -->
<% end %>
_sale.html.erb
<%= turbo_frame_tag "sale" do %>
<!-- more input fields -->
<% end %>
When selecting the option, this error happens:
Started POST "/set_property_transaction" for ::1 at 2022-09-07 19:49:03 -0600
Processing by PropertiesController#set_property_transaction as JSON
Parameters: {"property_transaction"=>"2"}
Completed 406 Not Acceptable in 223ms (ActiveRecord: 0.0ms | Allocations: 1879)
ActionController::UnknownFormat (PropertiesController#set_property_transaction is missing a template for this request format and variant.
request.formats: ["application/json", "text/javascript", "*/*"]
request.variant: []):
My understanding to this is that I'm missing the set_property_translation template, but I do have it. Not sure what else could I do to make it recognizable.
Les Nightingill's comment definitely sent me in the right direction. I'll put the changes needed here.
_property_transactions.html.erb
<div class="form-group mb-3" data-controller="property-transaction">
<%= label_tag :property_transactions, 'Propiedad en:', class: 'form-label' %>
<%= select_tag :property_transactions, options_for_select(#property_transactions.collect {|p| [p.name, p.id]}), { data:
{ action: "property-transaction#redraw", property_transaction_target: 'transaction', turbo_frame: "turbo_transactions" },
class: 'form-control', prompt: '', autocomplete: 'off' } %>
</div>
<%= turbo_frame_tag "dynamic_fields" %>
property_transaction_controller.js
import { Controller } from "#hotwired/stimulus";
import { post } from "#rails/request.js";
export default class extends Controller {
static targets = [ "transaction" ];
async redraw() {
const params = { property_transaction: this.transaction };
const response = await post("/set_property_transaction", {
body: params,
contentType: 'application/json',
responseKind: 'turbo-stream'
});
if (response.ok) {
console.log('all good', response); // not necessary
}
}
get transaction() {
return this.transactionTarget.value;
}
}
set_property_transaction.turbo_stream.erb
<%= turbo_stream.update "dynamic_fields" do %>
<%= render partial: #property_transaction.en_translation_key %>
<% end %>
I am trying to get a table to refresh on my Rails 5 application. It is being hosted on a Linux CentOS 7 system and accessed via my Firefox browser on Windows 7. I have tried much research and tried the Rails-AJAX gem to no avail (this gem also broke my links for some reason so I disabled it for now). In my console it claims to be rendering the partial after I make a change to the database, but this change is not reflected until I manually refresh the page in the browser.
The partial I'm trying to refresh is an entire table filled with data from the database and it has an image of an on/off light depending on if the status in the database says 'on' or 'off'. This image is what I look for if the table is actually being refreshed. Unfortunately, this image only changes when I manually refresh. Note: Svc/svc is the name of the model/table from the database that I'm using.
This is the console message I get when clicking my 'start' button:
Started GET "/boundbooks/1" for *IP ADDRESS* at 2018-06-29 17:51:10 -0400
Cannot render console from *IP ADDRESS*! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by BoundbooksController#startServer as JS
Parameters: {"id"=>"1"}
Svc Load (0.8ms) SELECT "svc".* FROM "svc" WHERE "svc"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/boundbooks_controller.rb:26
(0.2ms) begin transaction
↳ app/controllers/boundbooks_controller.rb:30
Svc Update (4.2ms) UPDATE "svc" SET "STATUS" = ? WHERE "svc"."id" = ? [["STATUS", "on"], ["id", 1]]
↳ app/controllers/boundbooks_controller.rb:30
(23.8ms) commit transaction
↳ app/controllers/boundbooks_controller.rb:30
Svc Load (0.5ms) SELECT "svc".* FROM "svc"
↳ app/views/boundbooks/_boundbooks_table.html.erb:8
Rendered boundbooks/_boundbooks_table.html.erb (17.8ms)
Completed 200 OK in 83ms (Views: 36.3ms | ActiveRecord: 29.5ms)
Code from views/boundbooks/index.html.erb:
<div id="bb-table">
<%= render partial: 'boundbooks_table', locals: { boundbooks: #boundbooks } %>
</div>
<br><br>
<% #boundbooks.each do |svc| %>
<%= link_to 'Refresh', boundbookUpdate_path(:bb_id => svc.id), id: 'refresh-button', class: 'btn', remote: true %>
<% end %>
Yes, I know the refresh button is printed multiple times, but I did not know how to link one button to the database.
HTML for table partial:
<table id="boundbooktable" border="1">
<thead><th>Status</th><th>Name</th><th>IP Address</th><th>Port</th><th>Action</th></thead>
<tbody>
<%# btnCounter gives each button a different ID by incrementing at the end of the loop %>
<% btnCounter = 1 %>
<%# statusOn = true %>
<%# creates a for-loop that utilizes the svc database table %>
<% #boundbooks.each do |svc| %>
<% status = svc.STATUS %>
<tr>
<td class="status-cell">
<% if status == "on" %>
<%= image_tag ("statusOn.png") %>
<% else %>
<%= image_tag ("statusOff.png") %>
<% end %>
</td>
<td><%= svc.NAME %></td>
<td><%= svc.IP %></td>
<td><%= svc.PORT %></td>
<td>
<% if status == "off" %>
<%= link_to "Start", boundbookStart_path(svc.id), id: 'start-button' + btnCounter.to_s, class: 'btn-start',
data: { confirm: "Start " + svc.NAME + "?" }, remote: true %>
<% else %>
<%= link_to "Start", boundbookStart_path(svc.id), id: 'start-button' + btnCounter.to_s, class: 'btn-start',
data: { confirm: svc.NAME + " is already on." }, remote: true %>
<% end %>
<% if status == "on" %>
<%= link_to "Stop", boundbookStop_path(svc.id), id: 'stop-button' + btnCounter.to_s, class: 'btn-stop', remote: true,
data: { confirm: "ALERT: Stop " + svc.NAME + "?" }, onclick: 'myFunction()' %>
<% else %>
<%= link_to "Stop", boundbookStop_path(svc.id), id: 'stop-button-already-off' +btnCounter.to_s, class: 'btn-stop',
remote: true, data: { confirm: svc.NAME + " is already off." } %>
<% end %>
<%= link_to "Log", boundbooks_path, id: 'log-button' + btnCounter.to_s, class: 'btn btn-log', remote: true %>
</td>
</tr>
<% btnCounter += 1 %>
<% end %> <%# end for-loop going through database %>
</tbody>
</table>
Code for boundbooks controller update and startServer methods:
def update
#boundbooks = Svc.all
#selected = Svc.where(:boundbook_id => params[:bb_id])
respond_to do |format|
format.js
end
end
def startServer
server = Svc.find(params[:id])
if server.STATUS == "off"
#boundbooks = Svc.all
server.update_attribute(:STATUS, "on")
refresh_dom_with_partial('div#bb-table', 'boundbooks_table')
render partial: 'boundbooks_table', locals: { boundbooks: #boundbooks }
else
puts ">>>> >>>> >>>> Server already on"
end
end
JavaScript code for AJAX in update.js.erb in the boundbooks views folder:
$.ajax({ url: "boundbooks_controller/update",
success: function() {
$("#boundbookstable").html("<%= escape_javascript(render partial: 'boundbooks_table', locals: { boundbooks: #selected } ) %>");
}});
these are the relevant routes in routes.rb:
get '/welcome' => 'welcome#index'
get '/boundbooks/:id/', controller: 'boundbooks', action: 'startServer', as: 'boundbookStart'
get '/boundbooks/:id/edit', controller: 'boundbooks', action: 'stopServer', as: 'boundbookStop'
get '/boundbooks/:bb_id/update' => 'boundbooks#update', as: 'boundbookUpdate'
get '/boundbooks_controller/update' => 'boundbooks#update'
How would I get this to actually refresh the table properly?
Your error seems simple
you are trying to change the html of the div with id 'boundbookstable'
$("#boundbookstable").html(...
but in your html, the div has the id of 'boundbooktable'
<table id="boundbooktable" border="1">
just be sure that they are exactly the same on both, change the html or the js, but be sure they are both equal on both files.
I have included validation for my sign up page, but when I load the sign-up page
Error appears. It should show errors after clicking submit button
What i need to do?
My sign-up page
<% if !flash[:notice].blank? %>
<div class="notice">
<%= flash[:notice] %>
</div>
<% end %>
<%= form_for #user,validate: true do |f| %>
<% if !#user.valid? %>
<% if #user.errors.any? %>
<ul class="Signup_Errors">
<% for message_error in #user.errors.full_messages %>
<li>* <%= message_error %></li>
<% end %>
</ul>
<% end %>
<% end %>
<%= f.label("Name:") %>
<%= f.text_field(:user_name, class: "form-control") %></br>
<%= f.label("Email Address:") %>
<%= f.text_field(:email_id, class: "form-control") %></br>
<%= f.label("Password:") %>
<%= f.password_field(:password, class: "form-control") %></br>
<%= f.label("Confirm Password:") %>
<%= f.password_field(:password_confirmation, class: "form-control") %></br>
<%= f.submit("Register",class: "btn btn-primary") %>
<a class="btn btn-primary" style="margin-left:20px" href="/login" >Login</a>
<% end %>
My model where I have included my validations
User.rb
class User < ApplicationRecord
has_many :reviews
has_secure_password
EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :user_name, :presence => true, :on => :create, :uniqueness => true, :length => { :in => 3..20 }
validates :email_id, :presence => true, :on => :create, :uniqueness => true, :format => EMAIL_REGEX
validates :password, :presence => true, :on => :create, :confirmation => true #password_confirmation attr
validates :password_confirmation, :presence => true
end
Controller
def new
puts "****Inside New Method******"
#user = User.new
end
def create
puts "****Inside create Method******"
#user = User.new(user_params)
puts #user.user_name
if #user.save
puts "** USER DETAILS SAVED SUCCESSFULLY****"
flash[:notice] = "Registration successful, please login"
flash[:color] = "valid";
redirect_to "/login"
else
flash[:notice] = "Invalid Form"
flash[:color] = "invalid"
end
end
When I load the page, Error appears before clicking the submit button
I have included :on => :create but it doesn't work
please help!!
if !#user.valid? on the form might be causing this. This is not necesarry, since you are only checking if the record is valid in the controller, just before saving your record.
You should also add render :new when the record could not be saved, otherwise you get a view missing error.
Open rails console and try to manually create the user. Is it create or not?
eg:
user = User.new(username: "test", email_id: "test#test.com", password: ...)
and check the validation by using
user.valid!
you will get the error message if validation fails. If no error then save it
user.save!
I currently have a basic to do list app that displays tasks "to do" and also tasks "completed" on the index page of the app. The model "tasks" has the attribute "completed" which I have a link to in the show page of the task:
<%= link_to "Completed", complete_task_path, method: :patch%>
The lists update on page refresh, but I'm having trouble actually making an AJAX request to update this information on the index page.
I'm unsure as to what my url should be, as I've tried to use the one provided using "rake routes" in the console.
My complete.js.erb:
$(document).ready(function() {
console.log("working");
$('.task_submit_box').click(function() {
$.ajax({url: '/tasks/:id/complete', success: function(result) {
$('.task_area').html(result);
console.log(result);
}});
});
});
My html for displaying the tasks:
<ul class="wrapper_task_list">
<% #task.each do |task| %>
<% if task.completed == false %>
<li>
<div class="task_area", id="incomplete_task_area">
<%= link_to task.title, task_path(task), class: 'wrapper_task_name'%>
<%= form_for task, remote: true do |f| %>
<%= f.check_box :completed, class: 'task_check_box' %>
<%= f.submit 'update', class: 'task_submit_box' %>
<% end %>
</div>
</li>
<% end %>
<% end %>
</ul>
And my complete method in my controller:
def complete
#task = current_user.tasks.find(params[:id])
#task.update_attribute(:completed_at, Time.now)
#task.update_attribute(:completed, true)
respond_to do |format|
format.js
format.html
end
redirect_to root_path
end
regarding the route, this is what is displayed for "rake routes" command:
complete_task PATCH /tasks/:id/complete(.:format) tasks#complete
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 %>