EDIT: To clarify, the charge does actually process, the only thing I can't do is dynamically set the charge amount.
I've worked all day and haven't found any solution. Here is my server.js:
app.post('/charge', function(req, res) {
var stripeToken = req.body.stripeToken;
var amount = 12000;
console.log(req);
stripe.charges.create({
card: stripeToken,
currency: 'usd',
amount: amount
},
function(err, charge) {
if (err) {
res.send(500, err);
} else {
res.send(204);
}
});
});
And here is my dynamic stripe button.
$('#calcTransAmount').click(function(event) {
var amount = $('#transAmount').scope().totall;
//console.log(amount);
var holder = document.getElementById('hello');
var script = document.createElement('script');
script.src = 'https://checkout.stripe.com/checkout.js';
script.setAttribute('class', 'stripe-button');
script.setAttribute('data-amount', amount);
script.setAttribute('data-key',"KEY");
script.setAttribute('data-image', "IMG");
script.setAttribute('data-name', "NAME");
script.setAttribute('data-billing-address', "true");
script.setAttribute('data-description', "Purchase");
script.setAttribute('data-locale', "auto");
document.getElementById('btnCont').appendChild(script);
});
The button works with the dynamic amount and everything, now I just can't seem to figure out how the post to /charge receives the amount and sets it as a variable.
This is my req object:
{ stripeToken: 'tok_19t9jcLhf04QCVXXXXXXXX',
stripeTokenType: 'card',
stripeEmail: 'me#gmail.com',
stripeBillingName: 'Clark',
stripeBillingAddressCountry: 'United States',
stripeBillingAddressCountryCode: 'US',
stripeBillingAddressZip: 'Zip',
stripeBillingAddressLine1: 'My address',
stripeBillingAddressCity: 'City',
stripeBillingAddressState: 'State' }
And I can't find a way to get my data-amount attribute from the button to the server to charge a dynamic amount.
Thanks
Special thanks to #koopajah for the inspiration for the solution.
In the form on the client side, in addition to the javascript button, I passed a key value using a hidden input box that would allow my server side to grab the cart total from our DB.
<form action="/charge" method="POST">
<input name="coKey" value ="{{currentUser.coKey}}">
</form>
Then on the server side, I can pull the appropriate key like this: var coKey = req.body.coKey;
Related
This might be a stupid question but here we go.
I've set up Stripe Elements (https://stripe.com/docs/elements) to collect credit card info, and tockenize it.
Now I'm trying to set up charges, but I'm unsure of how to set up my "server-side" code.
Submitting the form in my controller.js:
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
var form = document.getElementById('payment-form');
var hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
https://stripe.com/docs/charges:
"On your server, grab the Stripe token in the POST parameters submitted by your form."
From my Nodecharge.js:
// Set your secret key: remember to change this to your live secret key in
production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_111111111111111111");
// Token is created using Stripe.js or Checkout!
// Get the payment token ID submitted by the form:
var token = request.body.stripeToken; // Using Express
// Charge the user's card:
stripe.charges.create({
amount: 1000,
currency: "sek",
description: "Example charge",
source: token,
}, function(err, charge) {
// asynchronously called
});
My HTML-form:
<form action="/charge" method="post" id="payment-form">
<div class="form-row">
<label for="card-element">
Credit or debit card
</label>
<div id="card-element">
<!-- a Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors -->
<div id="card-errors" role="alert"></div>
</div>
<button>Submit Payment</button>
</form>
Submitting a payment with the test card, I get redirected to /charge with a 404.
I'm new to this, and I've obviously copy/pasted some code, but I'm trying hard to wrap my head around it, and I want to understand it, not just make it work.
I kinda get how the credit card info retrieval works with js, but I'm a bit confused when it comes to the charging/redirecting/404/.
I mean, this action-line points me to a non-existing page on my end, right? Do I need to create this page?
<form action="/charge" method="post" id="payment-form">
Sorry for the length of this post, please help me understand what's going on here, or what I need to fix.
Appreciate any help.
How are you serving your backend --- Express?
If you're seeing a 404 when you submit your form to /charge it sounds like you might not have a app.post route setup for /charge in Express.
You can read through the guide on routing for a little more detail
https://expressjs.com/en/guide/routing.html
If you want to see a simple working example, take a look at this (make sure to replace the pk_test and sk_test with your actual test keys):
var stripe = require("stripe")("sk_test_xxxyyyzzz");
var express = require('express'), bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({ extended: false })
var app = express();
app.get('/',function(req, res) {
// for kicks, just sending checkout
res.send('<form action="/charge" method="POST">Buy it !<script src="https://checkout.stripe.com/checkout.js" class="stripe-button" data-key="pk_test_xxxyyyyzzzz"></script></form>')
});
app.post('/charge',urlencodedParser, function(req, res) {
// grab a token
var token = req.body.stripeToken;
// creating a charge, for real use add things like error handling
stripe.charges.create({
amount: 2000,
currency: "usd",
source: token, // obtained with Stripe.js
description: "Charge"
}, function(err, charge) {
res.send("You made a charge: "+ charge.id);
});
});
app.listen(5000)
To create source token via stripe, first need to refer stripe.js from stripe.com and it must be from stripe.com.
And then add below code to add your card info and generate a token.
var stripe = Stripe('Your stripe publisheable key');
var elements = stripe.elements;
stripe.createToken(elements[0], additionalData).then(function (result) {
example.classList.remove('submitting');
if (result.token) {
// If we received a token, show the token ID.
example.querySelector('.token').innerText = result.token.id;
example.classList.add('submitted');
}
Here, you will get a token, that will be necessary to create your customer. Use below code to create your customer. I used C#.NET
StripeConfiguration.SetApiKey("sk_test_JTJYT2SJCb3JjLQQ4I5ShDLD");
var options = new CustomerCreateOptions {
Description = "Customer for jenny.rosen#example.com",
SourceToken = "tok_amex"
};
var service = new CustomerService();
Customer customer = service.Create(options);
Now, you can get your required amount form this user from the card token you got from stripe like below:
StripeConfiguration.SetApiKey("sk_test_JTJYT2SJCb3JjLQQ4I5ShDLD");
var options = new ChargeCreateOptions {
Amount = 2000,
Currency = "aud",
Description = "Charge for jenny.rosen#example.com",
SourceId = "tok_amex" // obtained with Stripe.js, }; var service = new ChargeService();
Good day everyone,
This app works fine when running in my laptop, but when launched from my tablet, the following code leads to a connection error. I am assuming its because the following requires too much processing power for the tablet to handle. Hence, I am looking for ways to optimize the following code.
Essentially I am trying to display a list of item for the user to select. The list in this case is the longestdimensions such as 11, 18, 24.
I am using express app, and for the view I am using handlebars (hbs).
Below is the route code
connection.connect();
// Run the query to retrieve the box name where the dimension of the box is equivilant to the computer name of the tablet
// The computer name of the tablet should reflect the location of the box which is indentified by
let sql = `SELECT box_id, longestDimension
FROM box
WHERE longestDimension != ''
AND LOWER(box_id) = LOWER(?)`;
connection.query(sql, computerName, function(err, rows, fields) {
if (!err) {
// Check to see if the user entered code is found in the database
// Create a variable to track if the item was found
for(var i=0; i< rows.length; i++) {
var data = {
rows: rows,
userHashtag: userEnteredHashtag
}
res.render('delivery/chooseBox', data);
}
// If the query fails to execute
} else {
console.log('Error while performing Query.');
res.render('error/errorConnection', {});
}
});
connection.end();
Below is the view code
{{#each rows}}
<form method="post" action="/delivery/chooseBoxSelected">
<input type="hidden" name="boxSelectedValue" value="{{this.box_id}}">
<input type="hidden" name="boxSelectedDimension" value="{{this.longestDimension}}">
<input type="hidden" name="userHashtag" value="{{userHashtag}}">
<button class="btn-dimension" type="submit">
<i class="fa fa-cube" aria-hidden="true"></i>
Longest dimension {{this.longestDimension}}"
</button>
</form>
{{/each}
For starters, you should only call res.render() once rather than separately for each row.
connection.query(sql, computerName, function (err, rows, fields) {
if (!err) {
// Check to see if the user entered code is found in the database
var data = {
rows: rows,
userHashtag: userEnteredHashtag
}
res.render('delivery/chooseBox', data);
// If the query fails to execute
} else {
console.log('Error while performing Query.');
res.render('error/errorConnection', {});
}
});
The {{#each rows}} in your template will repeat for every row. There is no need to call res.render() for each row. In fact, doing so is just a waste and may even cause a server error.
I'm trying to insert the checkbox value (as boolean) into a subschema of my collection. Not clear on 1) how to pass the checkbox value (can do it for normal input field) and 2) how to insert into subschema. I am using collection2 and handlebars.
1-This is what I have in the HTML form that needs to be submitted:
`<div class="checkbox">
<label><input type="checkbox" id="byow" checked="{{isChecked}}" value="">Bring Your Own Wine</label></div>`
2-This is what I have in my helper (in controller) to get the value of the form and the checkbox value, submit it and call the method that inserts it into the collection:
`BackendController.events({
//Add Venue - Add New Venue Submit Form Helper
'submit #add-venue-form' : function(event) {
event.preventDefault();
var venueName = event.target.venueName.value;
var byow = event.target.byow.checked;
var params = {
venueName: venueName,
byow: byow
}
//Insert Venue
Meteor.call('addVenue', params);
toastr.success('VenueAdded');
Router.go('/admin/manage-venues')
}
3-This is my method that is called to insert into my Venues collection (first part) and the structure of my collection and sub-collection:
`Meteor.methods({
'addVenue': function (params) {
Venues.insert(params);
}
// MAIN SCHEMA for the Venues colleciton.
Schema.Venues = new SimpleSchema({
venueName: {
type: String,
label: "Venue Name",
max: 200,
optional: false
},
//Attach schema for venue attributes (cuisine type, amenities, etc)
venueAttributes: {
type: Schema.VenueAttributes,
optional: true
}
});
//schema for venue attributes. Attached to main schema
Schema.VenueAttributes = new SimpleSchema({
byow: {
type: Boolean,
optional: true
}
});
Would really appreciate any help - I've managed to get the venueName to be passed successfully (so all my permissions/pub/sub is correct) but stuck at checkbox and subcollection.
Thanks!
Dan.
Finally figured it out with the help of #lokenx on Meteor Chef Slack channel.
The checkbox code (first part) returned the correct value (true) when written as above.
My error was that I was not specifying byow as a subproperty (was treating it as root property of the simpleschema). The correct params I should have been passing are:
var params = {
venuaName: venueName,
venueAttributes: {
byow: byow
}
}
I would like to output data from two collections using a reactive join into my template, then pair the users, post and comments through a common id.
So far, I can see with Mongo commands that the JSON data exist, but my template doesn't render any data. What am I doing wrong?
FYI, the meteorpad doesn't compile but the github repo will.
Repo:
https://github.com/djfrsn/frontend-interview-test-long/tree/master/ontra/ontra
Meteor Pad Example:
http://meteorpad.com/pad/SvwkNrv5grgv2uXxH/Copy%20of%20Leaderboard
There's so much wrong that it's hard to know where to start.
1) When you're loading the initial post and user data you're inserting the whole returned array as one element rather than inserting each element individually into your posts collection.
2) You're creating a publish subscription with the name "postsSet", but you're trying to subscribe to it with a different name.
3) You're not calling publishComposite correctly at all. You should be publishing the user required for each post as part of the children array.
4) The template needs updating based on the above
5) The username needs to be supplied via a helper.
6) You should really map the "id" attributes to Mongo's "_id" instead.
Here's come code which works. Note that you'll need to call meteor reset everytime you restart, otherwise you'll get duplicate id errors since you currently reimport the data every time.
Posts = new Mongo.Collection("Posts");
var groundPosts = new Ground.Collection(Posts);
Users = new Mongo.Collection("Users");
var groundUsers = new Ground.Collection(Users);
if (Meteor.isClient) {
Meteor.subscribe("postsSet");
console.log('POSTS DATA = ' + Posts.find().fetch());
console.log('USERS DATA = ' + Users.find().fetch());
Template.body.events({
"submit .ontra": function (event) {
// This function is called when the new task form is submitted
var text = event.target.text.value;
Posts.insert({
content: text,
date: new Date() // current time
});
// Clear Form
event.target.text.value = "";
// Prevent default form submit
return false
}
});
Template.body.helpers({
posts: function() {
return Posts.find();
},
});
Template.post.helpers({
username: function() {
return Users.findOne({_id: this.userId}).username;
}
});
}
Meteor.methods({
'fetchJSONData': function() {
var postsResponse = Meteor.http.call("GET","https://raw.githubusercontent.com/djfrsn/frontend-interview-test-long/master/codetest/data/posts.json");
var usersResponse = Meteor.http.call("GET","https://raw.githubusercontent.com/djfrsn/frontend-interview-test-long/master/codetest/data/users.json");
var postsData = JSON.parse(postsResponse.content);
var usersData = JSON.parse(usersResponse.content);
postsData.forEach(function (post) {
post.date = new Date();
post._id = String(post.id)
delete post.id
post.userId = String(post.userId)
Posts.insert(post);
});
usersData.forEach(function (user) {
user.date = new Date() // current time
user._id = String(user.id)
delete user.id
Users.insert(user);
});
}
});
if (Meteor.isServer) {
Meteor.publishComposite('postsSet', {
find: function () {
return Posts.find({});
},
children: [
{
find: function (post) {
console.log("%j", post.userId);
console.log("%j", Users.findOne({ _id: post.userId }));
return Users.find({ _id: post.userId });
}
}
]
});
Meteor.call("fetchJSONData");
//console.log('POSTS DATA = %j', Posts.find().fetch());
//console.log('USERS DATA = %j', Users.find().fetch());
}
HTML:
<head>
<title>ontra</title>
</head>
<body>
<div class='container'>
<header>
<h1>ontra</h1>
<form class='ontra'>
<input type='text' name='text' placeholder="Type to add new post">
</form>
</header>
<ul>
{{#each posts}}
{{> post}}
{{/each}}
</ul>
</div>
</body>
<template name='post'>
<li>
<span class="text">{{content}}</span>
<span class="text">{{username}}</span>
</li>
</template>
Your code doesn't run on meteorpad because the fetchJSONData method is executed on the server before it is defined in the common.js file. You should probably be calling the method after an event triggered on the client, or not use a method at all and simply fetch your JSON data on Meteor.startup.
Regarding the reactive join, it seems you want to do something very similar to Example 1 of the documentation: https://github.com/englue/meteor-publish-composite
I'm working with node.js express and mongodb, I have a input data from client, I need to pass the data to server look for its property and send to the client in another page.
Now I have problem with req.body.age that suppossed to get the data from client's input and use find() to get its appropriate property.
Server side code:
functions are routed in another .js file
exports.find_user = function(req, res) {
res.render('find_user.jade');
};
exports.user = function(req, res){
member = new memberModel();
member.desc.age = req.body.age; //problem
console.log(req.body.age); //undefined
memberModel.find({desc: {age: '7'}}, function(err, docs){
res.render('user.jade', { members: docs });
console.log(docs);
});
};
memberModel.find({desc: {age: '7'}} just hardcode picking up user with age 7 (works)
client side code (jade):
page for data input:
find_user.jade
form(action='/', method='post')
fieldset
lable(for="age") Find user by age:
input(type="text", size="30", name="age", required="required")
input(type='button', value='Find', onclick='location.href=\'find_user/user/\'')
page for data output with its property:
user.jade
tr
th Name
th Age
tbody
- members.forEach(function(member){
tr
td= member['name']
td= member['desc']
- });
You are not submitting your data in find_user.jade file when the user clicks the button. Instead, the client is only redirected to another page.
This is how your find_user.jade file should look like:
form(action='find_user/user/', method='post')
fieldset
label(for="age") Find user by age:
input(type="text", size="30", name="age", required="required")
input(type='submit', value='Find', name="submit")