Stripe RailsJS, Cannot charge a customer that has no active card - javascript

I keep getting the "Cannot charge a user that has no active card" error when trying to bill a test user.
The app works so that a user gets charged $10 on sign up after they input their CC credentials.
I know the customers are being created but the charges aren't being sent
Here is the Stripe error
My credit_card_form.js file which is what collects the credit card info and sends it to stripe without it hitting my database.
$(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);
show_error("CC token generated")
} else {
show_error("Failed to load credit card processing functionality. Please reload this page in your browser.")
}
return false;
};
// calling the SUBMIT
$(".cc_form").on('submit', submitHandler);
// RESPONSE HANDLER
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;
};
//ERROR HANDLING
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 payment.rb
class Payment < ApplicationRecord
attr_accessor :card_number, :card_cvv, :card_expires_month, :card_expires_year
belongs_to :user
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"
end
end
And a snippet of the devise registrations new.html.erb which is where the user will be charged from
<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>
I know that the customers are being created but I have no clue why my customers aren't paying when I use the test credit card numbers. No answers on stack overflow have helped me as of yet.

Jack and I had a chat and discovered that Stripe.js wasn't loading, so the token wasn't being sent in the request.
The issue was <%= javascript_include_tag 'https://js/stripe.com/v2' %> being a mistyped js link in application.html.erb
The fix was making it <%= javascript_include_tag 'https://js.stripe.com/v2/' %>

Related

How to POST stripe custom checkout token to flask backend

I'm trying to integrate Stripe custom checkout https://stripe.com/docs/checkout#integration-custom with Flask and WTForms. My problem at the moment is the payment form doesn't seem to be posting so the credit card charge cannot be created.
It seems the form is recognised because the token is being posted to stripe's api with a 200 response:
XHRPOST
https://api.stripe.com/v1/tokens
[HTTP/2.0 200 OK 1444ms]
Form data
card[cvc] 123
card[exp_month] 10
card[exp_year] 20
card[name] dev#local.host
card[number] 4242424242424242
email dev#local.host
guid 4a6cfd25-8c4b-4d98-9dd2-9e9c1770e290
key pk_test_DVVO0zxtWjXSZx4yHsZGJxtv
muid c6b9d635-20de-4fc6-8995-5d5b2d165881
payment_user_agent Stripe+Checkout+v3+checkout-manhattan+ (stripe.js/9dc17ab)
referrer http://localhost:8000/subscription/index
sid 494d70dd-e854-497b-945b-de0e96a0d646
time_on_page 26657
validation_type card
However the token (and the form) is not being posted to my server to create the charge that stripe requires.
Here is the javascript code to load stripe custom checkout, which is in /index.html:
<script src="https://checkout.stripe.com/checkout.js"></script>
<form role="form" id = "payment_form" action="{{ url_for('billing.charge') }}" method="post">
{{ form.hidden_tag }}
<input type="hidden" id="stripeToken" name="stripeToken" />
<input type="hidden" id="stripeEmail" name="stripeEmail" />
<div class="form-group">
<div class="col-md-12 button-field" style = "text-align: center;">
<button type="confirm" id = 'confirm' onclick = "runStripe('https://checkout.stripe.com/checkout.js')" class="btn btn-default btn-responsive btn-lg">Confirm Order</button>
</div>
</div>
<script>
var handler = StripeCheckout.configure({
key: "{{ stripe_key }}",
locale: 'auto',
token: function(token) {
// token ID as a hidden field
var form = document.createElement("form");
form.setAttribute('method', "POST");
form.setAttribute('action', "{{ url_for('billing.charge') }}");
form.setAttribute('name', "payment-form");
var inputToken = document.createElement("input");
inputToken.setAttribute('type', "hidden");
inputToken.setAttribute('name', "stripeToken");
inputToken.setAttribute('value', token.id);
form.appendChild(inputToken);
// email as a hidden field
var inputEmail = document.createElement("input");
inputEmail.setAttribute('type', "hidden");
inputEmail.setAttribute('name', "stripeEmail");
inputEmail.setAttribute('value', token.email);
form.appendChild(inputEmail);
document.body.appendChild(form);
}
});
document.getElementById('confirm').addEventListener('click', function(e) {
// Open Checkout with further options:
handler.open({
name: 'Stripe.com',
description: '2 widgets',
amount: '{{ amount }}'
});
e.preventDefault();
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
<script>
document.getElementsByClassName("stripe-button-el")[0].style.display = 'none';
</script>
I have attempted a post method within the html tag with no success. I have also tried adding a form variable within the javascript token to post to my charge route, adapted from this question: Stripe Checkout Link onClick does not process payment
Here is my index and charge routes for reference:
#billing.route('/index', methods=['GET', 'POST'])
def index():
stripe_key = current_app.config.get('STRIPE_PUBLISHABLE_KEY')
amount = 1010
form = CreditCardForm(stripe_key=stripe_key)
return render_template('billing/index.html', stripe_key=stripe_key, form=form)
#billing.route('/charge', methods=['GET', 'POST'])
def charge():
if request.method == 'POST':
customer = stripe.Customer.create(
email = current_user,
source = request.form['stripeToken']
)
charge = stripe.Charge.create(
customer = customer.id,
amount = 2000,
currency = 'usd',
description = 'payment'
)
return render_template('charge.html', customer=customer, charge=charge)
I decided to change the token to jquery, which now seems to work perfectly and is far simpler:
<script>
var handler = StripeCheckout.configure({
key: "{{ stripe_key }}",
locale: 'auto',
token: function(token) {
$(document).ready(function(){
$("#stripeToken").val(token.id);
$("#stripeEmail").val(token.email);
$("#payment_form").submit();
})
</script>
In order for the jquery to be recognised, I also added the script for the jquery package at the top of the html file:
script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
Finally, for anone else who needs help in flask, here is my adjusted route:
#billing.route('/index', methods=['GET', 'POST'])
#handle_stripe_exceptions
#login_required
def index():
stripe_key = current_app.config.get('STRIPE_PUBLISHABLE_KEY')
amount = 1010
form = CreditCardForm(stripe_key=stripe_key, name=current_user.name, amount=amount )
if request.method == 'POST':
customer = stripe.Customer.create(
email='customer#example.com',
source=request.form['stripeToken']
)
charge = stripe.Charge.create(
customer=customer.id,
amount=amount,
currency='usd',
description='Flask Charge'
)
return render_template('billing/index.html', stripe_key=stripe_key, form=form)

Get attribute from model of selected option

I have an employee dropdown that lists all the employees. I want to be able to select an employee and get the address of the employee from the model so that I may display it. the following is the code of my collection_select.
<div class="form-group col-md-2 field">
<%= form.label :employee_id %>
<%= form.collection_select :employee_id, Employee.all, :id, :full_name,{:prompt=>"Select Employee"},{:id=>"emp_select",class:"form-control",:onchange=>"getEmployee();"} %>
</div>
Next is the code I am using to grab the value of the employee that was selected and it does work.
function getEmployee() {
var selectedVal=$('#emp_select option:selected').val();}
From here what do I do to get the address of the employee that was selected?
You will have to retrieve the employee's address via ajax call. Here are the steps:
Define an action in your rails app to return employee's address by json.
Make an ajax request to that action and get the info needed.
Render result into view.
For more information, take a look at this link:
https://guides.rubyonrails.org/working_with_javascript_in_rails.html
routes.rb
controller :ajax do
get 'ajax/get_employee_address/:employee_id', action: :get_employee_address, as: :get_employee_address
end
ajax_controller.rb
class AjaxController < ActionController::Base
def get_employee_address
employee = Employee.find(params[:employee_id])
render json: employee.address.to_json
rescue ActiveRecord::RecordNotFound
render json: 'Employee not found', status: 422
end
end
Your js code
function getEmployee() {
var selectedVal=$('#emp_select option:selected').val();
$.ajax({
url: '/ajax/get_employee_address/' + selectedVal,
success: function (address) {
// Render your address to view
},
error: function () {
// Handle error here or just return nothing
return null;
}
})
}
Note: This ajax endpoint will expose your employee address to outside so be sure to make authentication to prevent leaking info.
Add address to option data-attribute:
<%= form.select :employee_id,
options_for_select(Employee.all.map {
|e| [e. full_name, e.id, { 'data-address' => e.address }]
}),
{ prompt: "Select Employee" },
{ id: "emp_select", class: "form-control", onchange: "getEmployee();" } %>
On change get it with js:
function getEmployee() {
var selectedVal=$('#emp_select option:selected').data("address");}
And insert it to needed place

Stripe Custom Checkout Issue with Rails

I'm currently creating a job board where when a customer posts a job board they pay for it in one form and it will then post the job and charge there card all at once.
First, I used the standard Stripe Checkout button along with my listings form and it submitted both my form and took payment.
Then I tried customizing the Stripe Checkout button using the custom docs found here but I can't seem to understand what I'm missing.
Now when I click on the button it pops up like normal but when I submit the button my Rails form doesn't create nor does it charge Stripe.
listings_controller.rb
def create
p #listing = Listing.new(listing_params)
#categories = Category.all.map{|c| [ c.title, c.id ] }
charge_error = nil
p #listing.valid?
p #listing.errors
if #listing.valid?
begin
customer = Stripe::Customer.create(
:email => params[:stripeEmail],
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:description => 'New Job Posting',
:currency => 'usd'
)
rescue Stripe::CardError => e
p 'rescue'
p e
charge_error = e.message
end
if charge_error
p 'change error not nil'
p charge_error
flash[:error] = charge_error
render :new
else
p 'trying to save listing'
p #listing.save
redirect_to #listing
end
else
p 'listing not valid'
flash[:error] = 'one or more errors in your orders'
render :new
end
end
listings/_form.html.erb
//Top of the page //
<script src="https://checkout.stripe.com/checkout.js"></script>
<button class='btn btn-block btn-primary' id='customButton'>
Pay and Post Your Job Posting!
</button>
<% end %>
<script>
var handler = StripeCheckout.configure({
key: "pk_test_iSItYFJUx04fB9Ax6yGQjRDP",
image: "https://stripe.com/img/documentation/checkout/marketplace.png",
name: "Example Name",
description: "Pro Subscription ($29 per month)",
panelLabel: "Subscribe",
allowRememberMe: false,
locale: 'auto',
token: function(token) {
token.id
}
});
document.getElementById('customButton').addEventListener('click', function(e) {
handler.open({
name: 'Example Name #2',
description: 'Subscripe ($99/monthly)',
amount: 2000
});
e.preventDefault();
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
If you need any other info please ask. Thank you!

Rails Stripe integration no active card error

Trying to charge a customer for a product registration. I've been following this tutorial upskillcourses.com. Which sets up a subscription. I' just trying to create a charge for a product.
I keep getting this error:
Stripe::CardError in Roadregistrations::RegistrationsController#create
Cannot charge a customer that has no active card
I've setup the stripe.js file:
app/assets/javascripts/
/* global $, Stripe */
//Document ready.
$(document).on('turbolinks:load', function(){
var theForm = $('#payment-form');
var submitBtn = $('#form-submit-btn');
//Set Stripe public key.
Stripe.setPublishableKey( $('meta[name="stripe-key"]').attr('content') );
//When user clicks form submit btn,
submitBtn.click(function(event){
//prevent default submission behavior.
event.preventDefault();
submitBtn.val("Processing").prop('disabled', true);
//Collect the credit card fields.
var ccNum = $('#card_number').val(),
cvcNum = $('#card_code').val(),
expMonth = $('#card_month').val(),
expYear = $('#card_year').val();
//Use Stripe JS library to check for card errors.
var error = false;
//Validate card number.
if(!Stripe.card.validateCardNumber(ccNum)) {
error = true;
alert('The credit card number appears to be invalid');
}
//Validate CVC number.
if(!Stripe.card.validateCVC(cvcNum)) {
error = true;
alert('The CVC number appears to be invalid');
}
//Validate expiration date.
if(!Stripe.card.validateExpiry(expMonth, expYear)) {
error = true;
alert('The expiration date appears to be invalid');
}
if (error) {
//If there are card errors, don't send to Stripe.
submitBtn.prop('disabled', false).val("Register and Pay");
} else {
//Send the card info to Stripe.
Stripe.createToken({
number: ccNum,
cvc: cvcNum,
exp_month: expMonth,
exp_year: expYear
}, stripeResponseHandler);
}
return false;
});
//Stripe will return a card token.
function stripeResponseHandler(status, response) {
//Get the token from the response.
var token = response.id;
//Inject the card token in a hidden field.
theForm.append($('<input type="hidden" name="user[stripe_card_token]">').val(token));
//Submit form to our Rails app.
theForm.get(0).submit();
}
});
Which it seems like the token is not being submitted with the form.
Not sure if I need both of these in my users_controller.rb:
# Only allow a trusted parameter "white list" through.
def roadregistration_params
params.require(:user).permit(:first_name, :last_name, :company, :street, :city, :state, :zip, :email, :phone, :roadshowcity, :stripe_card_token, :comments)
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:stripe_card_token, :password, :password_confirmation, :email, :first_name, :last_name, :company, :street, :city, :state, :zip, :phone, :roadshowcity, :comments) }
end
Then I have this in my user model:
attr_accessor :stripe_card_token
# If user passes validations (email, pass, etc.),
# Call stripe and tell stripe to set up a subscription
def save_with_registration
if valid?
#product_price = Objective.find(objective_id)
customer = Stripe::Customer.create(email: email, card: stripe_card_token, description: stripe_card_token.to_s)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => 9500,
:currency => "usd",
:description => "Roadshow Registration"
)
self.stripe_customer_token = customer.id
save!
end
end
customer = Stripe::Customer.create(email: 'example#gma.com')
=> #<Stripe::Customer:0x3ffd3a10e024 id=cus_A5CWbyto5ugmju> JSON: {
"id": "cus_A5CWbyto5ugmju",
"object": "customer",
"account_balance": 0,
"created": 1486585998,
"currency": null,
"default_source": null,
"delinquent": false,
"description": null,
"discount": null,
"email": "example#gma.com",
"livemode": false,
"metadata": {},
"shipping": null,
"sources": {"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/customers/cus_A5CWbyto5ugmju/sources"},
"subscriptions": {"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/customers/cus_A5CWbyto5ugmju/subscriptions"}
}
You just have to link card to the customer after creating the customer on stripe and before charging him:
customer = Stripe::Customer.create(email: email)
customer.sources.create(card: stripe_card_token) # <-- this
charge = Stripe::Charge.create(..
And I would advice to not send stripe_card_token in the customer object.
If you are on a version older than 2015-02-18 API version replace sources with cards.
Turbolinks was not being loaded in the app/assets/javascripts/application.js file. Fixing that issue allowed the stripe javascript to run and passed the stripe_card_token along.

Flash message doesn't show until refresh or secondary post?

I am jumping into Sails.js again and working through to create flash messages throughout my app (for errors, successes, or alerts). I was looking for a good way to do it and found this discussion, I implemented the solution they had suggested.
The general mechanism works great, however, the flash message is only seen after a secondary refresh or after another post. It does not show at first upon page load. Here is how I have everything structured and I am using "sails": "~0.10.0-rc7" currently:
In my api/policies folder, I have flash.js:
// flash.js policy
module.exports = function(req, res, next) {
res.locals.messages = {
success: [],
error: [],
warning: []
};
if(!req.session.messages) {
req.session.messages = { success: [], error: [], warning: [] };
return next();
}
res.locals.messages = _.clone(req.session.messages);
// Clear flash
req.session.messages = { success: [], error: [], warning: [] };
return next();
};
In my api/services, I have FlashService.js:
// FlashService.js
module.exports = {
success: function(req, message) {
req.session.messages['success'].push(message);
},
warning: function(req, message) {
req.session.messages['warning'].push(message);
},
error: function(req, message) {
req.session.messages['error'].push(message);
}
}
My config/policies.js is also configured with the flash policy:
// config/policies.js
module.exports.policies = {
'*': [true, 'flash'],
'UserController': {
'join': ['flash'],
},
};
Now, with all that setup, an example of how I am using it is in my UserController for my join action:
module.exports = {
join: function(req, res) {
// If username, email, and password compare come back true: create user.
if(newUser.username && newUser.email && newUser.password == newUser.confirmPassword) {
// Logic to create user.
res.view();
// If not, then report an issue.
} else {
FlashService.error(req, 'There was an issue.');
res.view();
};
}
};
Finally, my view is exactly the same code as that discussion I linked. I am using EJS on this:
<% if (messages && messages['error'].length > 0) { %>
<div class="alert alert-danger">
<% messages['error'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
<% if (messages && messages['warning'].length > 0) { %>
<div class="alert alert-warning">
<% messages['warning'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
<% if (messages && messages['success'].length > 0) { %>
<div class="alert alert-success">
<% messages['success'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
What could I be doing wrong? Any help would be much appreciated!
Thanks,
Your messages are being read before the the controller is executed (and just message is set after it is read). The req object is available in your view, you should just read req.session.messages['xxxx'] directly into your view.

Categories

Resources