I am using Stripe and "custom forms" from the API. The following code is throwing errors if something is wrong, in English, but I want to translate some of the error messages into Norwegian to make it more user friendly for my customers. For example "invalid_expiry_year" and "invalid_expiry_month" which is currently in English.
Is it possible to achieve and if so, how?
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script type="text/javascript">
Stripe.setPublishableKey('pk_test_2iA9ERjj5lVuUgvOS9W5fNtV');
$(function() {
var $form = $('#payment-form');
$form.submit(function(event) {
// Disable the submit button to prevent repeated clicks:
$form.find('.submit').prop('disabled', true);
// Request a token from Stripe:
Stripe.card.createToken($form, stripeResponseHandler);
// Prevent the form from being submitted:
return false;
});
});
function stripeResponseHandler(status, response) {
function stripeHandler( status, response ){
if ( response.error && response.error.type == 'card_error' ){
$( '.errors' ).text( errorMessages[ response.error.code ] );
}
else {
// do other stuff (and handle api/request errors)
}
}
// 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();
}
};
</script>
There is an option for you to set up the stripe form in a different language than English, but only for a few other languages Stripe supports. For a custom integration, you will have to pass locale: 'auto' when calling StripeCheckout.configure() so the language will be detected automatically. There’s more info in the docs.
However, since Norwegian isn't supported yet, what I am suggesting is mapping the response codes and providing your own translations for the errors.
var errorMessages = {
incorrect_number: "The card number is incorrect.",
....
};
You can find a complete list with all error codes here
Related
I am using Recurly's JavaScript API to process subscriptions payments.
I want to implement Google's reCaptcha V3 API to the Recurly's self-hosted page.
<script src="https://js.recurly.com/v4/recurly.js"></script>
recurly.configure({
publicKey : 'xxx-xxx',
required : ['cvv', 'address1', 'city', 'state', 'country', 'postal_code'],
});
// When a customer hits their 'enter' key while in a field
recurly.on('field:submit', function (event) {
$('form').submit();
});
// On form submit, we stop submission to go get the token
$('form').on('submit', function (event) {
// Prevent the form from submitting while we retrieve the token from Recurly
event.preventDefault();
// Reset the errors display
$('#errors').text('');
$('input').removeClass('error');
// Disable the submit button
$('button').prop('disabled', true);
var form = this;
// Now we call recurly.token with the form. It goes to Recurly servers
// to tokenize the credit card information, then injects the token into the
// data-recurly="token" field above
recurly.token(form, function (err, token) {
// send any errors to the error function below
if (err) error(err);
// Otherwise we continue with the form submission
else form.submit();
});
});
Things is, Google's API implementation is something like this :
<input type="hidden" name="recaptcha_response" id="recaptchaResponse">
<button type="submit" id="btn-submit" class="g-recaptcha" data-sitekey="xxxxxxxxx" data-callback='onSubmit' data-action='submit'>Submit</button>
<script>
function onSubmit(token)
{
document.getElementById("recaptchaResponse").value = token;
document.getElementById("frm-subscribe").submit();
}
</script>
Both have their own version of onSubmit. How do I include Google's one into Recurly's ?
<input type="hidden" name="recaptcha_response" id="recaptchaResponse">
<button type="submit" id="btn-submit">Submit</button>
recurly.token(form, function (err, token) {
// send any errors to the error function below
if (err) error(err);
// Otherwise we continue with the form submission
else
{
grecaptcha.ready(function()
{
grecaptcha.execute('xxx-xxx-site-key', {action: 'submit'}).then(function(token)
{
document.getElementById("recaptchaResponse").value = token;
form.submit();
});
});
}
});
I am trying to set up reCaptcha v3 and it sort of works. For some reason the first time I submit the form it fails but from the second submit onwards it is fine. I can't figure out why this is happening?
<script src="https://www.google.com/recaptcha/api.js?render=MY_SITE_KEY"></script>
<script>
grecaptcha.ready(function () {
grecaptcha.execute('MY_SITE_KEY', { action: 'contact' }).then(function (token) {
var recaptchaResponse = document.getElementById('captcha-response');
recaptchaResponse.value = token;
});
});
</script>
<input type="hidden" name="captcha-response" id="captcha-response">
PHP
$verifyResponse = file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret='.$secretKey.'&response='.$_POST['captcha-response']);
$responseData = json_decode($verifyResponse);
if(!$responseData->score < 0.5) {
$message .= "Verification failed " . $responseData->score;
}
When I submit the form the first time, I get the validation error but my score is 0.9.
Why you have added "!" with "$responseData->score"? you may need to replace your condition with the following:
Replace this:
if(!$responseData->score < 0.5) {
$message .= "Verification failed " . $responseData->score;
}
With this one:
if($responseData->score < 0.5) {
$message .= "Verification failed " . $responseData->score;
}
P.S: Following code takes few seconds to properly load and get a "captcha-reponse" code, so you may need to disable all submit button and wait till you got a "captcha-reponse" to enable the submit button in form or you needs to implementent another way to delay the submit to execute only once you got a "captcha-response" code otherwise you will keep getting "missing-input-response" error message
<script src="https://www.google.com/recaptcha/api.js?render=MY_SITE_KEY"></script>
<script>
grecaptcha.ready(function() {
grecaptcha.execute('MY_SITE_KEY', {
action: 'contact'
}).then(function(token) {
var recaptchaResponse = document.getElementById('captcha-response');
recaptchaResponse.value = token;
});
});
</script>
You should re-generate the reCaptcha token after error form validation occured.
The token reCaptcha only valid for ONE TIME.
So, you have two options to fixes this issue.
1. Reload the page when error occured
This is the easiest way. You only need to reload the page whenever form validation error occured.
Of course, this will trigger the reCaptcha to generate new token.
2. Handle with AJAX (Non-reload page)
This is the best approach, since this will helps the user not losing the form data and continue to fill the form.
So, here's what you should do.
<!-- Put this hidden input inside of your form tag -->
<input name="_recaptcha" type="hidden">
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITEKEY_HERE"></script>
<script>
// This will generate reCaptcha token and set to input hidden
const generateRecaptcha = function() {
grecaptcha.execute(
"YOUR_SITEKEY_HERE", {
action: "YOUR_ACTION_NAME"
}).then(function(token) {
if (token) {
document.querySelector("input[name='_recaptcha']").value = token;
}
});
}
// Call it when page successfully loaded
grecaptcha.ready(function() {
generateRecaptcha();
});
// Do your AJAX code here
$.ajax({
url: "https://example.com",
success: function(response) {
console.log(response);
},
error: function(error) {
// Call again the generator token reCaptcha whenever error occured
generateRecaptcha();
}
</script>
Don't forget to put your Site key and your action name. Make sure the action name matches with your Backend action name.
Medium Article
I'm currently taking the upskill tutorial "The Essential Web Developer Course"(currently on lesson 162) where i try to connect Stripe with a payment for a premium membership. This tutorial is working with older versions of the gems and libraries, etc., thats why I'm telling you that.
The problem is that after Stripe sends me back a costumer token. It won't get stored in my db and it won't sign in to the membership. Instead my submit button will stay disabled and shows the string "Processing".
The dev-console shows the: "Uncaught TypeError: Cannot read property 'submit' of undefined" message.
What do I need to change? Has it sth. to do with my save_with_subscription method?
attr_accessor :stripe_card_token
def save_with_subscription
if valid?
customer = Stripe::Customer.create(description: email, plan: plan_id, card: stripe_card_token)
self.stripe_customer_token = customer.id
save!
end
The js file for it:
/* global $, Stripe */
//Document ready.
$(document).on('turbolinks:load', function(){
var theForm = $('#pro_form');
var submitBtn = $('#form-signup-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("Sign Up");
} 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();
}
});
All of my code can be found at https://github.com/JakkSwords/upskill_saas_tutorial/tree/user_memberships
I have a payment form that has the following JS to generate a stripe token.
var form = $( "#new_subscription" );
form.validate();
jQuery(function($) {
$('#new_subscription').submit(function(event) {
var $form = $(this);
$form.find('#formSubmit').prop('disabled', true);
Stripe.card.createToken($form, stripeResponseHandler);
return false;
});
});
function stripeResponseHandler(status, response) {
var $form = $('#new_subscription');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
$form.find('.payment-errors').removeClass('hidden');
$form.find('#formSubmit').prop('disabled', false);
} else {
$form.find('.payment-errors').addClass('hidden');
// response contains id and card, which contains additional card details
var token = response.id;
// Insert the token into the form so it gets submitted to the server
$form.append($('<input type="hidden" name="stripe_token" />').val(token));
// Remove Stripe cc fields
$("[data-stripe=number]").remove();
$("[data-stripe=cvc]").remove();
$("[data-stripe=exp-year]").remove();
$("[data-stripe=exp-month]").remove();
// and submit
$form.get(0).submit();
}
};
in my controller, I have:
if !#subscription.errors.blank?
#user = User.new(user_params)
#subscription_item = SubscriptionItem.new(subscription_item_params)
flash.now[:error] = #subscription.errors[:base][0]
render :new
When I use Stripe test cc number 4000000000000341, it renders the correct Payment Declined error message on the new subscription form. And in the Stripe dashboard, it shows a failed payment associated with this card.
However, if I then enter a test card number that should successfully charge (4000000000000077) and submit, I get the same error as before. The test charges in the dashboard show that it is still trying to use the same card.
It's only after a page refresh that the form will send the correct card.
I'm not understanding why my page is sending the card number from the first submission when I enter a new card.
A bit of a newbie here. I've been looking for an answer that works and found some similarities in a Jade problem but I'm not using Jade. I have passed an "user" attribute into an HTML view as so:
app.get('/profile', isLoggedIn, function(req, res) {
res.render('profilePage/profilePage.html', {
user : req.user // get the user out of session and pass to template
});
});
Then, in my profile HTML, I can access my user property like so:
<%=user.local.firstname%>'s Profile
However, I want to allow Stripe to send the user's credit card info via the Stripetoken. I have managed to include a variable amount from a text field the user inputs. However, I want to append the user property so I can use it in my callback. Here is the javascript/jquery that's included in the profile html:
<!-- New section -->
<script type="text/javascript">
<!-- Fill in your publishable key -->
Stripe.setPublishableKey('pkkey');
var stripeResponseHandler = function(status, response) {
var $form = $('#contactForm');
var $amount = $('#amount').val();
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
$form.append($('<input type="hidden" name="stripeToken" />').val(token));
$form.append($('<input type="hidden" name="amount" />').val($amount));
// and re-submit
$form.get(0).submit();
}
};
jQuery(function($) {
$('#contactForm').submit(function(e) {
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;
});
});
</script>
As you can see, I have managed to append the $amount variable so I can access it in the callback:
module.exports = function(app, passport) {
app.post('/stripe', function(req,res) {
// =====STRIPETOKEN======
var transaction = req.body;
var stripeToken = transaction.stripeToken;
var donationAmount = transaction.amount;
stripe.customers.create({
source : stripeToken,
account_balance : 0
},function(err, customer) {
if (err) {
console.log(err);
} else {
console.log("Success!");
}});
// ====CREATE CHARGE======
var charge =
{
amount : donationAmount,
currency : 'USD',
card : stripeToken
};
stripe.charges.create(charge, function(err, charge) {
if(err)
console.log(err);
else
{
res.json(charge);
console.log('Successful charge sent to Stripe!');
console.log(charge);
};
});
// ====PROFILE PAGE REDIRECT=====
res.render('profilePage/profilePage.html', {
});
});
So here's my problem. I want to pass the user's information, kind of like I did the amount, into the post method so when it redirects on success, I can pass it back in the res.render function, as well as send it to Stripe for description purposes. The only thing I can think of is to put the user info in a hidden field in HTML and access it like that, but that sounds messy and not proper.
This is my first time posting here so I apologize if it was too lengthy or not specific enough. Thanks!
The answer was in the way I was declaring passport and stripe in my application. Make sure you declare passport after everything to make the user variable available to stripe and all views.