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.
Related
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
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!
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/' %>
I'm trying to integrate stripe.js into a web app I'm working on, however I'm being thrown the following error:
Cannot read property 'stripeToken' of undefined
The clientside is setting the hidden input of the token but for some reason, the server can't pull it this:
var stripeToken = request.body.stripeToken;
Any ides as to why this might be?
Client-side JS
jQuery(function($) {
$('#payment-form').submit(function(event) {
var $form = $(this);
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
Stripe.card.createToken({
number: $('.card-number').val(),
cvc: $('.card-cvc').val(),
exp_month: $('.card-expiry-month').val(),
exp_year: $('.card-expiry-year').val()
}, stripeResponseHandler);
// Prevent the form from submitting with the default action
return false;
});
});
function stripeResponseHandler(status, response) {
// Grab the form:
var $form = $('#payment-form');
if (response.error) { // Problem!
// Show the errors on the form:
$form.find('.payment-errors').text(response.error.message);
$form.find('.submit').prop('disabled', false); // Re-enable submission
} else { // Token was created!
// Get the token ID:
var token = response.id;
// Insert the token ID into the form so it gets submitted to the server:
$form.append($('<input type="hidden" name="stripeToken">').val(token));
// Submit the form:
$form.get(0).submit();
}
};
jQuery(function($) {
$('[data-numeric]').payment('restrictNumeric');
$('.cc-number').payment('formatCardNumber');
$('.cc-exp').payment('formatCardExpiry');
$('.cc-cvc').payment('formatCardCVC');
$.fn.toggleInputError = function(erred) {
this.parent('.form-group').toggleClass('has-error', erred);
return this;
};
$('form').submit(function(e) {
e.preventDefault();
var cardType = $.payment.cardType($('.cc-number').val());
$('.cc-number').toggleInputError(!$.payment.validateCardNumber($('.cc-number').val()));
$('.cc-exp').toggleInputError(!$.payment.validateCardExpiry($('.cc-exp').payment('cardExpiryVal')));
$('.cc-cvc').toggleInputError(!$.payment.validateCardCVC($('.cc-cvc').val(), cardType));
$('.cc-brand').text(cardType);
$('.validation').removeClass('text-danger text-success');
$('.validation').addClass($('.has-error').length ? 'text-danger' : 'text-success');
});
});
Server-side JS
app.post('/', function(req, res) {
var stripeToken = request.body.stripeToken;
var charge = stripe.charges.create({
amount: 1000, // amount in cents, again
currency: "usd",
source: stripeToken,
description: "Example charge"
}, function(err, charge) {
if (err && err.type === 'StripeCardError') {
// The card has been declined
}
});
});
Form (jade)
form(novalidate='', autocomplete='on', method='POST' id="payment-form")
.form-group
label.control-label(for='cc-number')
| Card number formatting
small.text-muted
| [
span.cc-brand
| ]
input#cc-number.input-lg.form-control.cc-number(type='tel', autocomplete='cc-number', placeholder='•••• •••• •••• ••••', required='')
.form-group
label.control-label(for='cc-exp') Card expiry formatting
input#cc-exp.input-lg.form-control.cc-exp(type='tel', autocomplete='cc-exp', placeholder='•• / ••', required='')
.form-group
label.control-label(for='cc-cvc') Card CVC formatting
input#cc-cvc.input-lg.form-control.cc-cvc(type='tel', autocomplete='off', placeholder='•••', required='')
button.btn.btn-lg.btn-primary(type='submit' class='submit') Submit
h2.validation
our request is in the variable req not request
this var stripeToken = request.body.stripeToken; should be var stripeToken = req.body.stripeToken;
I'm following this example integration from Stripe Docs (slightly modified in order to be able to add click handlers to more than one button:
<script src="https://checkout.stripe.com/checkout.js"></script>
<button id="customButton">Purchase</button>
<script>
var handler = StripeCheckout.configure({
key: 'pk_test_jPVRpCB1MLjWu2P71eTvXBZD',
image: '/square-image.png',
token: function(token) {
// Use the token to create the charge with a server-side script.
// You can access the token ID with `token.id`
}
});
$('.pay-deposit').click( function(e) {
// Open Checkout with further options
handler.open({
name: 'Demo Site',
description: '2 widgets ($20.00)',
amount: 2000
});
e.preventDefault();
});
In my particular case I have a few buttons like:
<button class='pay-deposit' booking-id='3455'>Pay Deposit</button>
<button class='pay-deposit' booking-id='335'>Pay Deposit</button>
<button class='pay-deposit' booking-id='34'>Pay Deposit</button>
... and obviously I'd like to pass a booking-id of clicked button somehow to token callback. Couldn't find any example or explanation covering this seemingly simple case... any help much appreciated. thanks!
This is a little bit late, but maybe it will help someone else. This is modified from a Rails example:
# HTML file
<script src="https://checkout.stripe.com/checkout.js"></script>
<button class='pay-deposit' data-booking-id='3455'>Pay Deposit</button>
<button class='pay-deposit' data-booking-id='335'>Pay Deposit</button>
<button class='pay-deposit' data-booking-id='34'>Pay Deposit</button>
# JS file
$('.pay-deposit').on('click', function(event) {
event.preventDefault();
// Get booking information from database
var booking_id = $(this).data('booking-id');
$.getJSON("/bookings/"+booking_id, function(data) {
// Open Checkout with further options
handler = stripe_checkout(booking_id);
handler.open({
name: "My Bookings",
description: data["description"],
amount: data["amount"],
email: data["email"],
});
// Close Checkout on page navigation
$(window).on('popstate', function() {
handler.close();
});
});
});
function stripe_checkout(booking_id) {
var handler = StripeCheckout.configure({
key: 'pk_test_jPVRpCB1MLjWu2P71eTvXBZD',
token: function(token) {
// Send the charge through
$.post("/charges/create",
{ token: token.id, booking_id: booking_id }, function(data) {
if (data["status"] == "ok") {
window.location = "/some-url";
} else {
// Deal with error
alert(data["message"]);
}
});
}
});
return handler;
}
# Bookings controller
class BookingsController < ApplicationController
def show
#booking = Booking.find(params[:id])
attrs = #booking.attributes
attrs.merge!("email" => current_user.email)
respond_to do |format|
format.json { render json: attrs.to_json }
end
end
end
# Charges controller
class ChargesController < ApplicationController
def create
booking = Booking.find(params[:booking_id])
customer = Stripe::Customer.create(card: params[:token])
charge = Stripe::Charge.create(
customer: customer.id,
amount: booking.amount,
description: booking.description,
currency: 'usd'
)
if charge.paid
# Do some things for successful charge
respond_to do |format|
format.json { render json: {status: "ok"}.to_json }
end
else
respond_to do |format|
format.json { render json: {status: "fail", message: "Error with processing payment. Please contact support."}.to_json }
end
end
end
end
Move your token initializer from configure to open.
var handler = StripeCheckout.configure({
key: 'pk_test_jPVRpCB1MLjWu2P71eTvXBZD',
image: '/square-image.png'
});
$('.pay-deposit').click( function(e) {
var data = $(this).data('booking-id');
// Open Checkout with further options
handler.open({
name: 'Demo Site',
description: '2 widgets ($20.00)',
amount: 2000,
token: function(token) {
// here you go!
alert(data);
}
});
e.preventDefault();
});
And switch to:
<button class='pay-deposit' data-booking-id='3455'>Pay Deposit</button>
<button class='pay-deposit' data-booking-id='335'>Pay Deposit</button>
<button class='pay-deposit' data-booking-id='34'>Pay Deposit</button>