Braeintree Client Set Up with JS v3 - payment_method_nonce null and the form isn't submitting - javascript

I've tried to integrate Braintree in my Laravel 5.2 app and everything works fine with JS v2 client setup, but I would like to upgrade it to v3.
This is from the docs (I've customized a bit):
<form id="checkout-form" action="/checkout" method="post">
<div id="error-message"></div>
<label for="card-number">Card Number</label>
<div class="hosted-field" id="card-number"></div>
<label for="cvv">CVV</label>
<div class="hosted-field" id="cvv"></div>
<label for="expiration-date">Expiration Date</label>
<div class="hosted-field" id="expiration-date"></div>
<input type="hidden" name="payment-method-nonce">
<input type="submit" value="Pay">
</form>
<!-- Load the Client component. -->
<script src="https://js.braintreegateway.com/web/3.0.0-beta.8/js/client.min.js"></script>
<!-- Load the Hosted Fields component. -->
<script src="https://js.braintreegateway.com/web/3.0.0-beta.8/js/hosted-fields.min.js"></script>
<script>
var authorization = '{{ $clientToken }}'
braintree.client.create({
authorization: authorization
}, function (clientErr, clientInstance) {
if (clientErr) {
// Handle error in client creation
return;
}
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'font-size': '14pt'
},
'input.invalid': {
'color': 'red'
},
'input.valid': {
'color': 'green'
}
},
fields: {
number: {
selector: '#card-number',
placeholder: '4111 1111 1111 1111'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: '10 / 2019'
}
}
}, function (hostedFieldsErr, hostedFieldsInstance) {
if (hostedFieldsErr) {
// Handle error in Hosted Fields creation
return;
}
form.addEventListener('submit', function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (tokenizeErr, payload) {
if (tokenizeErr) {
// Handle error in Hosted Fields tokenization
return;
}
document.querySelector('input[name="payment-method-nonce"]').value = payload.nonce;
form.submit();
});
}, false);
});
});
</script>
But when I click the submit button, nothing happens.event.preventDefault() stops the submission and the payment_method_nonce token is generated, but I can't submit the form after that, because form.submit() isn't works
How can I submit the form after event.preventDefault()?
Or how can I send the payment_method_nonce token to my controller?
Thanks!

When I copied your snippet and tried to run the example, I got (index):69 Uncaught ReferenceError: form is not defined in the console. When I added
var form = document.getElementById('checkout-form');
it worked just fine.
Best guess, you just forgot to assign the form variable to reference the form dom element. If that's not the case, be sure to let me know.

Related

Bootstrap V5 Form Validation & Sweetalert2 : Showing success message after successful submission

I have simple form based on Bootstrap 5 with a validation option I'm trying to display alert message if the form field is successfuly submited using Sweatalert2.
Here is my Code :
HTML
<form action="" method="POST" class="needs-validation" novalidate>
<label for="validationCustomUsername" class="form-label">Username</label>
<div class="input-group has-validation mb-3">
<span class="input-group-text" id="inputGroupPrepend">#</span>
<input type="text" class="form-control" id="validationCustomUsername" placeholder="Username *" aria-describedby="inputGroupPrepend" required />
<div class="invalid-feedback">
Please choose a username.
</div>
</div>
<button class="btn btn-primary" type="submit">Submit form</button>
</form>
JS
(function () {
'use strict'
// Fetch all the forms we want to apply custom Bootstrap validation styles to
var forms = document.querySelectorAll('.needs-validation')
// Loop over them and prevent submission
Array.prototype.slice.call(forms)
.forEach(function (form) {
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault()
event.stopPropagation()
}
form.classList.add('was-validated')
}, false)
})
})()
Live Example
I was having the same issue and came across this post which quite helpful.
Below code might help:
......
form.classList.add('was-validated');
if (form.reportValidity()) {
event.preventDefault()
Swal.fire({
position: 'center',
icon: 'success',
title: 'Your application has been submitted successfully!',
showConfirmButton: false,
timer: 2500
}).then((result) => {
// Reload the Page
location.reload();
});
}
It will reload the page after form submission.
This is may be too late, but I hope it can help others. I have the same issue. Then, I tried to combine bootstrap validation inside the SweetAlert Confirmation Box before submitting the form.
I create the code like below:
$('#submitForm').on('click', function (e) {
e.preventDefault();// prevent form submit
/*this part is taken from bootstrap validation*/
var forms = document.getElementsByClassName('needs-validation');
var validation = Array.prototype.filter.call(forms, function (form) {
if (form.checkValidity() === false) {
form.classList.add('was-validated');
}
else {
Swal.fire({
title: 'Are you sure?',
text: "It cannot be undone",
type: 'warning',
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, send it!'
}).then((result) => {
if (result.value) {
/*submit the form*/
$("#formsubmitsekali").submit();
}
});
}
}, false);
});
submitForm is the ID name for the button ID for submit the form.
formsubmitsekali is the form ID.
By doing so, if the required field is not filled, it will show the bootstrap validation without showing Sweetalert confirmation box. But if all of required fields are filled, the Sweetalert will show up.
The same behavior also happens if you have email input type, but it is filled by non-email, it will run the bootstrap validation first. It is also work if you use HTML5 pattern inside the input type and the user fills with the wrong pattern.

Braintree JSv3 payment_method_nonce Value Bad With HostedFields

I have looked at a few posts on here with the same issue but under different circumstances that don't supply me with an answer to my particular issue...
I was using Braintree JSv2 with my Django project and all was working fine. Since I have migrated over to v3 of Braintree, the only issue I seem to have right now is that the value inputted to "payment_method_nonce" is not there...
Here is the code that is supposed to be dumping the payment_method_nonce value:
document.querySelector('input[name="payment_method_nonce"]').value = payload.nonce;
And here is the code that is supposed to be grabbing it on the python side:
client_payment_nonce = request.POST['payment_method_nonce']
When submitting this in my dev environment, I get an error (MultiValueDictKeyError) for "payment_method_nonce".
I am using Django 1.9 and Python 2.7. I am also using the example given by Braintree for a simple integration using HostedFields...
Small test
So I manually added an input field in my form with name "payment_method_nonce" just to see if not having a field was causing some issue. I know it is injected by Braintree but just testing a thought. It seems that although the value of payment_method_nonce is supposed to be my nonce, I didn't type anything into the input box and it was still coming back as null.
Full Snippets of Form and HostedFields
<form action="/booking/" method="post" id="checkout_form">
{% csrf_token %}
<div class="payment">
<span>Payment</span>
<!--input elements for user card data-->
<div class="hosted-fields" id="card-number"></div>
<div class="hosted-fields" id="postal-code"></div>
<div class="hosted-fields" id="expiration-date"></div>
<div class="hosted-fields" id="cvv"></div>
<div class="btns">
<input type="hidden" name="payment_method_nonce">
<input type="submit" value="Complete Booking" id="pay-button">
</div>
</div>
</form>
Note: I had just changed the payment_method_nonce field to type="hidden" instead of type="text" but still have the same effect...
<!-- load the required client component -->
<script src="https://js.braintreegateway.com/web/3.15.0/js/client.min.js"></script>
<!-- load the hosted fields component -->
<script src="https://js.braintreegateway.com/web/3.15.0/js/hosted-fields.min.js"></script>
<!-- Braintree setup -->
<script>
var client_token = "{{ request.session.braintree_client_token }}"
var form = document.querySelector('#checkout-form');
var submit = document.querySelector('input[type="submit"]');
braintree.client.create({
authorization: client_token
}, function (clientErr, clientInstance) {
if (clientErr) {
// Handle error in client creation
return;
}
braintree.hostedFields.create({
client: clientInstance,
styles: {
'input': {
'font-size': '14px'
},
'input.invalid': {
'color': 'red'
},
'input.valid': {
'color': 'green'
}
},
fields: {
number: {
selector: '#card-number',
placeholder: 'Credit Card Number'
},
cvv: {
selector: '#cvv',
placeholder: '123'
},
expirationDate: {
selector: '#expiration-date',
placeholder: '10/2019'
},
postalCode: {
selector: '#postal-code',
placeholder: '10014'
}
}
}, function (hostedFieldsErr, hostedFieldsInstance) {
if (hostedFieldsErr) {
// handle error in Hosted Fields creation
return;
}
submit.removeAttribute('disabled');
form.addEventListener('submit', function (event) {
event.preventDefault();
hostedFieldsInstance.tokenize(function (tokenizeErr, payload) {
if (tokenizeErr) {
// handle error in Hosted Fields tokenization
return;
}
// Put `payload.nonce` into the `payment_method_nonce`
document.querySelector('input[name="payment_method_nonce"]').value = payload.nonce;
document.querySelector('input[id="pay-button"]').value = "Please wait...";
form.submit();
});
}, false);
});
});
</script>
Note: the line document.querySelector('input[id="pay-button"]').value = "Please wait..."; doesn't fire (I know this because the button does not change values). Maybe these querySelector lines just aren't working?
Something New Noticed
I just went back to my page and hit the submit button without even entering any information. In v2 of Braintree, I would not be able to click the submit button until all fields were filled in... Maybe the values in my form aren't even being sent to braintree to receive a nonce and that's why there is an empty string being returned..?
Moral of the story
Review your code... Multiple times. As pointed out by C Joseph, I have my form ID as something different than what my var form is referencing...
<form action="/booking/" method="post" id="checkout_form">
var form = document.querySelector('#checkout-form');

Two forms on one page - input types transferring

I have two forms on one page to handle a choice of 2 stripe subscriptions (basic and advanced) and I'm using javascript to submit each form on click of the button as below:
For the basic plan:
<form id="payment_form_basic" action="" method="POST">
<input type="hidden" name="subscription_purchase_non_trial" value="subscription_purchase_non_trial_basic" />
<button id="pricing__action_grey">Choose Plan</button>
</form>
<script src="https://checkout.stripe.com/checkout.js"></script>
<script>
// ******************************************
// ****** Basic Plan Form Processor *********
// ******************************************
var handler = StripeCheckout.configure({
key: '<?php echo $stripe['publishable_key']; ?>',
image: 'https://example.com/example.png',
locale: 'auto',
allowRememberMe: 'false',
token: function(token) {
$('#payment_form_basic').append($('<input type="hidden" name="stripeToken" />').val(token.id));
$('#payment_form_basic').append($('<input type="hidden" name="stripeEmail" />').val(token.email));
$("#payment_form_basic").submit();
}
});
document.getElementById('pricing__action_grey').addEventListener('click', function(e) {
// Open Checkout with further options:
handler.open({
name: 'Basic',
description: 'example description',
zipCode: true,
amount: 10
});
e.preventDefault();
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
And for the advanced plan:
<form id="payment_form_advanced" action="" method="POST">
<input type="hidden" name="subscription_purchase_non_trial" value="subscription_purchase_non_trial_advanced" />
<input type="hidden" name="subscription_type" value="advanced" />
<button id="pricing__action">Choose Plan</button>
</form>
<script>
// ******************************************
// ****** Advanced Plan Form Processor ******
// ******************************************
var handler = StripeCheckout.configure({
key: '<?php echo $stripe['publishable_key']; ?>',
image: 'https://example.com/example.png',
locale: 'auto',
allowRememberMe: 'false',
token: function(token) {
$('#payment_form_advanced').append($('<input type="hidden" name="stripeToken" />').val(token.id));
$('#payment_form_advanced').append($('<input type="hidden" name="stripeEmail" />').val(token.email));
//$('#payment_form_advanced').append($('<input type="hidden" name="subscription_type" value="advanced" />'));
$("#payment_form_advanced").submit();
}
});
document.getElementById('pricing__action').addEventListener('click', function(e) {
// Open Checkout with further options:
handler.open({
name: 'Advanced',
description: 'example descript',
zipCode: true,
amount: 12
});
e.preventDefault();
});
// Close Checkout on page navigation:
window.addEventListener('popstate', function() {
handler.close();
});
</script>
I'm trying to use the subscription_type field in the advanced form as a switch when the page posts onto itself to determine whether to process the basic plan price or the advanced plan however the form still post the "advanced" subscription type through when the basic form is submitted, it seems like the advanced from always overwrites the basic form, at list that' what appears to happen when I print the post variables received, which makes no sense to me. Does anyone know why this is happening?
Seems I needed to wrap all the script (in each scenario) in the on click function, that way the advanced settings didn't over write the basic settings for stripe. Hopefully this is a robust approach.

SilverStripe submit HTML form through Ajax

I want to pass data from a simple HTML form to a controller through Ajax, then process the data and return a response back.
At the moment I have the following:
HomePage.ss
<form method="POST" class="form-horizontal submit-form" onsubmit="return checkform(this);">
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="name">Name</label>
<div class="col-md-8">
<input id="name" name="name" type="text" placeholder="insert full Name" class="form-control input-md" required="" />
</div>
</div>
<!-- Button -->
<div class="form-group">
<label class="col-md-4 control-label" for="send-btn"></label>
<div class="col-md-8">
<button id="send-btn" name="send-btn" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
JavaScript
$('form.submit-form').submit(function() {
$.ajax({
type: 'POST',
url: 'processForm',
data: $(this).serialize(),
success: function(data) {
alert('data received');
}
});
});
HomePage.php
class HomePage_Controller extends Page_Controller {
public function events() {
$events = CalendarEvent::get();
return $events;
}
public function processForm() {
if (Director::is_ajax()) {
echo 'ajax received';
} else {
//return $this->httpError(404);
return 'not ajax';
}
}
}
In developer tools I can see that I got the xhr processForm with a 404 not found error.
How do I get this Ajax form working correctly with the SilverStripe controller?
Spider,
I've done something similar to below. This is a quick and dirty demo and hasn't been tested, but it may get you going in the right path. If you're unfamiliar with how forms work within SilverStripe there is a lesson for front end forms in SilverStripe. I've found the lessons useful personally and provide the code for the lesson as well: http://www.silverstripe.org/learn/lessons/introduction-to-frontend-forms?ref=hub
Page.php
<?php
class Page extends SiteTree
{
}
class Page_Controller extends Content_Controller
{
private static $allowed_actions = array(
'MyForm',
);
public function MyForm()
{
Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.min.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-validate/jquery.validate.min.js');
Requirements::javascript('/path/to/your/validation/script.js');
$fields = FieldList::create(
TextField::create('name')
->setTitle('Name')
);
$actions = FieldList::create(
FormAction::create('doSubmit')
->setTitle('Submit')
);
$requiredFields = RequiredFields::create(
'name'
);
$form = Form::create($this, 'MyForm', $fields, $actions, $requiredFields);
return $form;
}
public function doSubmit($data, $form)
{
//process $data or create your new object and simpley $form->saveInto($yourObject); then $yourObject->write()
//then deal with ajax stuff
if ($this->request->isAjax()) {
return $this->customise(array(
'YourTemplateVar' => 'Your Value'
))->renderWith('YourIncludeFile');
} else {
//this would be if it wasn't an ajax request, generally a redirect to success/failure page
}
}
}
YourValidationScript.js
(function ($) {
$(function () {
$('#MyForm_Form').validate({
submitHandler: function (form) {
$.ajax({
type: $(form).attr('method'),
url: $(form).attr('action') + "?isAjax=1",
data: $(form).serialize()
})
.done(function (response) {
$('.content').html(response);
})
.fail(function (xhr) {
alert('Error: ' + xhr.responseText);
});
},
rules: {
name: "required"
}
});
})
})(jQuery);
You need to understand how HTTP request routing is handled in SilverStripe.
When you send request POST /processForm, it is treated as page and managed by ModelAsController. That is why you get 404 error - there is no SiteTree record with URLSegment = processForm.
Solution 1
Use Form object. It creates all routing configuration automatically during runtime. Read more
https://docs.silverstripe.org/en/3.3/tutorials/forms/
https://docs.silverstripe.org/en/3.3/developer_guides/forms/
Solution 2
Use this approach, when you really want to go down to the simple one method request handler. Register custom controller and routing.
You specify your route in mysite/_config/routing.yml
---
Name: siteroutes
---
Director:
rules:
processCustomForm: CustomFormController
Handle your request
class CustomFormController extends Controller
{
public function handleRequest( SS_HTTPRequest $request, DataModel $model ) {
if (!$request->isPost()) {
// handle invalid request
}
$name = $request->postVar('name')
// process your form
}
}

How to charge a stripe card in meteor

Having a warm time trying to charge a card in Meteor. The error I get is: Exception while invoking method 'chargeCard' Error: Match error: Expected string, got object. I do get the modal where I typed in the email and card number but after pressing the pay button, in terminal I get the error message.
How to call the charge function properly? I cant find any tutorial that matches closely the way I implement it.
The setup is very basic. I also have jquery installed.
Template:
<template name="hello">
<form id="myForm">
<input type="text" id="amount" name="amount"/>
<input type="hidden" id="stripeToken" name="stripeToken"/>
<input type="hidden" id="stripeEmail" name="stripeEmail"/>
</form>
<hr>
<button id="customButton">Pay</button>
</template>
js:
if (Meteor.isClient) {
Template.hello.helpers({
});
Template.hello.events({
'click button': function (e) {
e.preventDefault();
var handler = StripeCheckout.configure({
key: 'pk_test_rand',
token: function(token) {
$("#stripeToken").val(token.id);
$("#stripeEmail").val(token.email);
$("#myForm").submit();
Meteor.call('chargeCard', token); // this seem not right?
}
});
// Showing the pop up Stripe dialog
var amount = $("#amount").val() *100;
// Open Checkout with further options
handler.open({
name: 'Demo Site',
description: '2 widgets ($20.00)',
amount: amount
});
// Close Checkout on page navigation
$(window).on('popstate', function() {
handler.close();
});
}
});
Meteor.startup(function(){
$.getScript('https://checkout.stripe.com/checkout.js', function(){
// script has loaded
});
});
}
if (Meteor.isServer) {
Meteor.methods({
'chargeCard': function(stripeToken) {
check(stripeToken, String);
var Stripe = StripeAPI('sk_test_rand');
Stripe.charges.create({
source: stripeToken,
amount: 5000, // this is equivalent to $50
currency: 'usd'
}, function(err, charge) {
console.log(err, charge);
});
}
});
}
It seems you're passing the whole token object:
Meteor.call('chargeCard', token);
But your chargeCard() method expects a string:
check(stripeToken, String);
So you need to either pass only the token id:
Meteor.call('chargeCard', token.id);
or change your chargeCard() method to expect and use the whole token object:
Meteor.methods({
'chargeCard': function(stripeToken) {
check(stripeToken, Object);
var Stripe = StripeAPI('sk_test_rand');
Stripe.charges.create({
source: stripeToken.id,
...

Categories

Resources