I'm trying to integrate a Braintree payment solution using Javascript & Node JS.
As per the Braintree documentation, I have the following in my html:
<div id="panel-payment">
<div id="payment-form"></div>
<input id="btn-checkout" type="submit" value="Process Order">
</div>
<script>
$(document).ready(function(){
console.log(braintree); <---- defined AOK
console.log(braintree.Environment); <---- undefined
console.log(braintree.Environment.Sandbox); <---- undefined
var clientToken = "...";
braintree.setup(clientToken, "dropin", {
container: $("#payment-form")
});
});
</script>
I can't get the environment variable, yet the braintree object seems to instantiate fine? Anyone have any ideas?
The braintree object returns:
Object {api: Object, cse: Object, paypal: Object, dropin: Object, Form: Object…}
I also get the dreaded "Unable to find valid container" when I call the braintree.setup() function, even though the container value $("#payment-form") is valid value and I am calling the setup function when the HTML has loaded..
The Environment variable is only available in the NodeJS package.
See here.
For the client side in javascript your need a valid token generated by your server.
So you need a dedicated route delivering a token with the Braintree node package as described here.
And for your container problem try to pass only the id of the div to braintree, not a jQuery element.
braintree.setup(clientToken, "dropin", {
container: "payment-form"
});
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
#chambo_e's answer is correct regarding the difference between the attributes accessible on the node package versus Braintree.js. The environment attribute is only available on the Braintree node module.
I am guessing that the error you are actually seeing is "Uncaught Unable to find a valid FORM" and that is because your payment forms are not enclosed by a form element. By default, Braintree.js looks for the nearest parent form.
<form id="checkout" method="post" action="/checkout">
<div id="panel-payment">
<div id="payment-form"></div>
<input id="btn-checkout" type="submit" value="Process Order">
</div>
</form>
<script src="https://js.braintreegateway.com/v2/braintree.js"></script>
<script>
$(document).ready(function(){
var clientToken = "client token generated form server";
braintree.setup(clientToken, "dropin", {
container: $("#payment-form")
});
});
</script>
If you'd like it to use a different form, you can specify that in the setup call. See these docs for more details
<div id="dropin-container"></div>
<form id="checkout-form">
<input type='submit' value='Pay'/>
</form>
<script>
braintree.setup("CLIENT-TOKEN-FROM-SERVER", "dropin", {
container: "dropin-container",
form: "checkout-form"
});
</script>
Related
I am building an app using express handlebars for server-side templating. On the client side, I want to use vue.js. However, they both share the same double brace notation {{ variable }}. Right now, my vue.js variables are not showing because my handlebars template is overriding it. For example:
home.html:
<div id="app">
{{message}} //this will not show up
</div>
home.js:
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
});
HOWEVER, if I define message in my server side controller:
res.render('../views/home', {
message: 'message from handlebars'
});
the message will show up.
So in summary, is there a way i can use vue.js for client side templating while still using express-handlebars for server side templating?
Thanks in advance!
This can be solved by escaping the first brace in your handlebars template.
<div id="app">
\{{message}} //this will not be replaced by handlebars
</div>
You can change the Vue demlimiters to something else, e.g.:
Vue.config.delimiters = ['${', '}']
You can "escape" the Vue delimiters in Handlebars by using a raw block helper:
Handlebars.registerHelper('vue-js', function(options) {
return options.fn();
});
Usage in the Handlebars template:
{{{{vue-js}}}}
<div id="app">
{{message}}
</div>
{{{{/vue-js}}}}
i have an Angular Storefront app set up. I have a shopping cart functionality in place and a stripe "pay with card" button etc. pretty much looks like this:
<form action="/#/order" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripeApiKey }}"
data-billingAddress=true
data-shippingAddres=true
data-amount="{{ amount }}"
data-name="StoreFront Name"
data-description="Custom-Made Jewellery"
data-image="../images/www/logo.png"
data-locale="auto">
</script>
</form>
Evrything up to this point is working fine. I submit the form and stripe returns the token but the form goes to the server following the route localhost/order (without the # symbol) instead of angular's localhost/#/order.
Why is stripe forcing this redirect? In other words why isn't angular capturing this return call?
Anyways. Then I create a route with Laravel to capture this and dump to inspect the returned data like so:
Route::post('/order', function($request){
dd($request);
});
Yep, data captured by stripe-generated form is returned except amount is missing... I mean everything including stripeToken, buyer's details such as: Name, Email, Billing and Shipping address are returned BUT detail regarding the amount is missing.
Is this normal or I'm I missing something?
Lastly currency is still showing the default: Where can I change currency from say USD to GBP?
Thanks in advance
1/ I don't think Checkout is forcing the redirect, but I don't know enough about Angular to explain what's going on, sorry.
2/ Yes, this is normal. The amount passed to Checkout in the data-amount configuration option is used for display purposes only. The actual amount that is charged is the one you pass in the amount parameter in the charge creation request in your server-side code.
If you need the amount to be user-specified (for instance, if you're taking donations), you'll need to add the amount to the form. Here is a simple JSFiddle to illustrate this case: https://jsfiddle.net/ywain/g2ufa8xr/
3/ You can use the data-currency parameter to change the currency displayed in the Checkout form. Just like data-amount, this is for display purposes only and the actual currency used for the charge is specified by the currency parameter in the charge creation.
This is what i managed to do.
I went with the custom form approach. I had a form template to capture both customer and card inputs in billing.template.html like so:
<form method="POST" id="payment-form">
<span class="payment-errors"></span>
<div>
<label>Name</label>
<input type="text" name="name" data-stripe="name">
</div>
<div>
<label>Email</label>
<input type="text" name="email" data-stripe="address_email">
</div>
<div>
<label>Address Line 1</label>
<input type="text" name="street" data-stripe="address_line1">
</div>
<div>
<label>Postcode</label>
<input type="text" name="postcode" data-stripe="address_zip">
</div>
<div>
<label for="country">Country</label>
<select ng-include="'../templates/_partials/_countrylist.html'"
id="countries" name="country" class="form-control"
name="country" ng-model="country" id="country" size="2"
data-stripe="address_country" required></select>
</div>
<div class="form-row">
<label>
<span>Card Number</span>
<input type="text" name="cardNumber" size="20" data-stripe="number"/>
</label>
</div>
<div class="form-row">
<label>
<span>CVC</span>
<input type="text" name="cvc" size="4" data-stripe="cvc"/>
</label>
</div>
<div class="form-row">
<label>
<span>Expiration (MM/YYYY)</span>
<input type="text" name="expMonth" size="2" data-stripe="exp-month"/>
</label>
<span> / </span>
<input type="text" name="expYear" size="4" data-stripe="exp-year"/>
</div>
<button id="customButton">Pay with Card</button>
</form>
I know we are not supposed to use name attribute in those form inputs but i left them so i could use angular validation, but i remove them using jquery before submitting to server.
Now i created a controller to handle the form: BillingController.js. In there i had an "on click" handler which kick started things by getting a hold of the form and doing some preparatory work: disabling button to prevent further clicks and removing those 'dreaded' name attributes, comme ca:
$('#customButton').on('click',function(event) {
var $form = $('#payment-form');
// Disable the submit button to prevent repeated clicks
$form.find('button').prop('disabled', true);
//NOW REMOVE THOSE NAME ATTRIBUTES
$form.find('input').removeAttr('name');
// call Stripe object and send form data to get back the token.
// NOTE first argument is $form
Stripe.card.createToken($form, stripeResponseHandler);
// Prevent the form from submitting with the default action
return false;
});
Now let me quote the documentation here as this is very important to understand: https://stripe.com/docs/tutorials/forms
The important code to notice is the call to Stripe.card.createToken.
The first argument is the form element containing credit card data
entered by the user. The relevant values are fetched from their
associated inputs using the data-stripe attribute specified in the
form.
Next we create stripeResponseHandler(). Remember it was the second argument in Stripe.card.createToken($form, stripeResponseHandler); above which gets called when Stripe returns the token.
function stripeResponseHandler(status, response) {
var $form = $('#payment-form');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
$form.find('button').prop('disabled', false);
} else {
// 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="stripeToken" />').val(token));
// and submit
$form.get(0).submit();
}
};
This is copy and paste stuff from stripe's own documentation: https://stripe.com/docs/tutorials/forms. Now, I want to say that, this is where a lot of us were tripping over the fact that form was performing a redirect etc. - notice final line $form.get(0).submit(); . Thats what caused the auto submit, redirecting to what ever action was on form, if u had any (in my case action attribute wasn't necessary as i was doing redirects in my controller).
So i decided to remove $form.get(0).submit() and implemented my own redirect after i was done sending data to the server.
NOTE: Stripe's response will have included data from the $form - try console.log(response); to have an idea of what's being posted back.
FINALLY:
We check if there were any errors returned and if so display them. Otherwise its all good, send data to the server.
The final code looks like:
function stripeResponseHandler(status, response) {
var $form = $('payment-form');
if (response.error) {
// Show the errors on the form
$form.find('.payment-errors').text(response.error.message);
} else {
// response contains id and card, which contains additional card details
var token = response.id;
// prepare data
var data = {
stripeToken: token,
fullName: response.card.name,
street: response.card.address_line1,
postcode: response.card.address_zip,
town: response.card.address_city,
country: response.card.address_country,
last4: response.card.last4
};
// send to server
$http.post('/checkout', data).then(function(result){
// here you can redirect yourself.
window.location.href = "/#/order-complete";
});
}
};
Angular really playing well with stripe here. Check out this link also: https://gist.github.com/boucher/1750368 - learn a lot from it.
I hope it helps someone today. Happy coding!
Stripe doesn't get involved with your form aside from preventing the default action on form submit event and stopping event propagation. Once the checkout process completes, it appends the relevant data to your form and then triggers a form submit event that is handled by HTML / Javascript natively.
I recommend using something like https://github.com/tobyn/angular-stripe-checkout to get your Stripe response handled correctly by Angular.
Otherwise you could add ng-submit="handleStripeCheckout($event)" to your form instead of action="/#/form". When Stripe's checkout process completes, your $scope.handleStripeCheckout method will be run and you can analyze the new form data inside that method.
Edit: Stripe checkout.js actually triggers form.submit(). That's a pretty bad bug on their part considering that almost no browsers handle that correctly. (Form submitted using submit() from a link cannot be caught by onsubmit handler)
iam total new at meteor and i try to build an Meteor application that should show the data another Mongo Database. The app it self can use its own metor database. So i found, that with MongoInternals.RemoteCollectionDriver() its to connect with my second database.
Next step is to make it work in the meteor tutorial. But i dont get back any data from the second database. For a test, simple arrays are returned correct from my function and placed right into the webapp. And the expression in .find() should be also ok. I tested it in the Mongo console.
If the connection to the second database is placed in if (Meteor.isClient) or outside of the client/server parts, the error "ReferenceError: MongoInternals is not defined" appears. If its set inside of if (Meteor.isServer) sometimes an exception appears in the console:
Exception in template helper: .ris_sessions#http://localhost:3000
/risdd_mongo.js?4fc7111851b4ed2182782e0a368b366cc4e89745:15:17
bindDataContext/<#http://localhost:3000/packages
/blaze.js?77c0809654ee3a10dcd5a4f961fb1437e7957d33:2693:14
...... and a lot more messages
I cant see, why dont getting back any data from the second database.
Here are the sources:
Tasks = new Mongo.Collection("tasks");
//////////////////////
if (Meteor.isClient) {
Template.body.helpers({
tasks: function() {
return Tasks.find({}, {sort: {createdAt: -1 }});
}
,
s_sessions: function() {
return ris_sess.find( {},{description:1} ).fetch();
}
});
}
//////////////////////
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
var risdb_drv = new MongoInternals.RemoteCollectionDriver("mongodb://172.0.0.1:27017/ris");
var ris_sess = new Mongo.Collection("sessions", { _driver: risdb_drv });
});
}
<head>
<title>foo</title>
</head>
<body>
<div class="container">
<header>
<h1>todo list</h1>
<!-- add a FORM !-->
<form class="new-task">
<input type="text" name="text" placeholder="ad your task here" />
</form>
</header>
<ul>
{{#each tasks}}
{{>task}}
{{/each}}
{{#each ris_sessions}}
{{>ris_session}}
{{/each}}
</ul>
</div>
</body>
<template name="task">
<li>{{text}}</li>
</template>
<template name="ris_session">
<li>{{description}}</li>
</template>
If you use local MongoDB server try change
mongodb://172.0.0.1:27017/ris
to
mongodb://127.0.0.1:27017/ris
Typo in IP: 127
I have installed meteor-typeahead via npm. https://www.npmjs.org/package/meteor-typeahead
I have also installed
meteor add sergeyt:typeahead
from https://atmospherejs.com/sergeyt/typeahead
I am trying to get the data-source attribute example to function so I can display a list of countries when the user begins to type. I have inserted all countries into the collection :-
Country = new Meteor.Collection('country');
The collection is published and subscribed.
When I type into the input field, no suggestions appear. Is it something to do with activating the API? if so how do I do this? Please reference the website https://www.npmjs.org/package/meteor-typeahead
My form looks like this:
<template name="createpost">
<form class="form-horizontal" role="form" id="createpost">
<input class="form-control typeahead" name="country" type="text" placeholder="Country" autocomplete="off" spellcheck="off" data-source="country"/>
<input type="submit" value="post">
</form>
</template>
client.js
Template.createpost.helpers({
country: function(){
return Country.find().fetch().map(function(it){ return it.name; });
} });
In order to make your input to have typeahead completion you need:
Activate typeahead jQuery plugin using package API
Meteor.typeahead call in template rendered event handler.
Meteor.typeahead.inject call to activate typeahead plugin for elementes matched by CSS selector available on the page (see demo app).
Write 'data-source' function in your template understandable by typeahead plugin. It seems your 'data-source' function is correct.
Add CSS styles for typeahead input(s)/dropdown to your application. See example here in demo app.
Try this way in your template:
<input type="text" name="country" data-source="country"
data-template="country" data-value-key="name" data-select="selected">
Create template like country.html (for example /client/templates/country.html) which contains:
<template name="country">
<p>{{name}}</p>
</template>
In your client javascript:
Template.createpost.rendered = function() {
Meteor.typeahead.inject();
}
and
Template.createpost.helpers({
country: function() {
return Country.find().fetch().map(function(it){
return {name: it.name};
});
},
selected: function(event, suggestion, datasetName) {
console.log(suggestion); //or anything what you want after selection
}
})
When entering the security words in recaptcha and clicking the Submit button, the PHP secure e-mail function is executed - even when the recaptcha verification fails and returns the error message. The basic code follows. What can I do to NOT execute the e-mail function? Thank you.
<form name="contactform" method="POST" onSubmit="return validateCaptcha()"
action="send_form_email.php">
</form>
<script type="text/javascript">
jQuery(document).ready(function (){
Recaptcha.create(" --- my key ---_",
"recpatcha_show",
{
theme: "red",
callback: Recaptcha.focus_response_field
}
);
});
</script>
<div id="recpatcha_show"></div>
<div id="captchaStatus" style="color:red;font:16px;"></div>
<br />
<input type="submit" value="Submit">
I don't know exactly how recaptcha works, but I'm guessing you'll have to prevent the form from submitting when the chaptcha characters don't match.
With jQuery its:
if(characters dont match){
event.preventDefault();
} else {
submit form
}
Also, you might want to read this:
https://developers.google.com/recaptcha/docs/php?hl=nl
You can make recaptcha completely back-end, that way you can completly validate the user imput before sending the e-mail.