I'm trying to integrate Stripe for my shopping cart project. I can't get my checkout form to submit. I keep getting this error message: "Must provide source or customer." Either I didn't set up my Stripe account correctly or I'm missing some parameters in my javascript. I've spent hours on this problem and still can't figure it out.
This is from Stripe's log:
Parsed Request POST Body
{
"amount": "21000",
"currency": "usd",
"description": "Test Charge"
}
Response Body
{
"error": {
"type": "invalid_request_error",
"message": "Must provide source or customer."
}
}
This is my app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var expressHbs = require('express-handlebars');
var mongoose = require('mongoose');
var session = require('express-session');
var passport = require('passport');
var flash = require('connect-flash');
var validator = require('express-validator');
var MongoStore = require('connect-mongo')(session);
var routes = require('./routes/index');
var userRoutes = require('./routes/user');
var app = express();
mongoose.connect('localhost:27017/shopping');
require('./config/passport');
// view engine setup
app.engine('.hbs', expressHbs({defaultLayout: 'layout', extname: '.hbs'}));
app.set('view engine', '.hbs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(validator());
app.use(cookieParser());
app.use(session({
secret: 'mysupersecret',
resave: false,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection }),
cookie: { maxAge: 180 * 60 * 1000 }
}));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(path.join(__dirname, 'public')));
app.use(function(req, res, next){
res.locals.login = req.isAuthenticated();
res.locals.session = req.session;
next();
});
app.use('/user', userRoutes);
app.use('/', routes);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
This is my index.js:
var express = require('express');
var router = express.Router();
var Cart = require('../models/cart');
var Product = require('../models/product');
/* GET home page. */
router.get('/', function(req, res, next) {
var successMsg = req.flash('success')[0];
Product.find(function(err, docs) {
var productChunks = [];
var chunkSize = 3;
for (var i = 0; i < docs.length; i += chunkSize) {
productChunks.push(docs.slice(i, i + chunkSize));
}
res.render('shop/index', { title: 'Shopping Cart', products: productChunks, successMsg: successMsg, noMessages: !successMsg});
});
});
router.get('/add-to-cart/:id', function(req, res, next) {
var productId = req.params.id;
var cart = new Cart(req.session.cart ? req.session.cart : {});
Product.findById(productId, function(err, product){
if (err) {
return res.redirect('/');
}
cart.add(product, product.id);
req.session.cart = cart;
console.log(req.session.cart);
res.redirect('/');
});
});
router.get('/shopping-cart', function(req, res, next) {
if (!req.session.cart) {
return res.render('shop/shopping-cart', {products: null});
}
var cart = new Cart(req.session.cart);
res.render('shop/shopping-cart', {products: cart.generateArray(), totalPrice: cart.totalPrice});
});
router.get('/checkout', function(req, res, next) {
if (!req.session.cart) {
return res.redirect('/shopping-cart');
}
var cart = new Cart(req.session.cart);
var errMsg = req.flash('error')[0];
res.render('shop/checkout', {total: cart.totalPrice, errMsg: errMsg, noError: !errMsg});
});
router.post('/checkout', function(req, res, next) {
if (!req.session.cart) {
return res.redirect('/shopping-cart');
}
var cart = new Cart(req.session.cart);
var stripe = require("stripe")(
"**hidden**"
);
stripe.charges.create({
amount: cart.totalPrice * 100,
currency: "usd",
source: req.body.stripeToken,
description: "Test Charge"
}, function(err, charge) {
if (err) {
req.flash('error', err.message);
return res.redirect('/checkout');
}
req.flash('success', 'Successfully bought product!');
req.cart = null;
res.redirect('/');
});
});
module.exports = router;
This is my checkout.js:
Stripe.setPublishableKey('**hidden**');
var $form = $('checkout-form');
$form.submit(function(event) {
$('#charge-errors').addClass('hidden');
$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(),
name: $('#card-name').val()
}, stripeResponseHandler);
return false;
});
function stripeResponseHandler(status, response) {
if (response.error) { // Problem!
// Show the errors on the form
$('#charge-errors').text(response.error.message);
$('#charge-errors').removeClass('hidden');
$form.find('button').prop('disabled', false); // Re-enable submission
} else { // Token was created!
// Get the token ID:
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));
// Submit the form:
$form.get(0).submit();
}
};
This is my checkout form (.hbs):
<div class="row">
<div class="col-sm-6 col-md-6 col-md-offset-3 col-sm-offset-3">
<h1>Checkout</h1>
<h4>Your Total: ${{total}}</h4>
<div id="charge-error" class="alert alert-danger {{# if noError}}hidden{{/if}}">
{{errMsg}}
</div>
<form action="/checkout" method="post" id="checkout-form">
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" class="form-control" required>
</div>
</div>
<div class="col-xs-12">
<div class="form-group">
<label for="name">Address</label>
<input type="text" id="address" class="form-control" required>
</div>
</div>
<div class="col-xs-12">
<div class="form-group">
<label for="name">Name on Card</label>
<input type="text" id="card-name" class="form-control" required>
</div>
</div>
<div class="col-xs-12">
<div class="form-group">
<label for="name">Credit Card Number</label>
<input type="text" id="card-number" class="form-control" required>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label for="name">Expiration Month</label>
<input type="text" id="card-expiry-month" class="form-control" required>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label for="name">Expiration Year</label>
<input type="text" id="card-expiry-year" class="form-control" required>
</div>
</div>
<div class="col-xs-12">
<div class="form-group">
<label for="name">
CVC</label>
<input type="text" id="card-cvc" class="form-control" required>
</div>
</div>
</div>
<button type="submit" class="btn btn-success">Buy Now</button>
</form>
</div>
</div>
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script type="text/javascript" src="javascripts/checkout.js"></script>
See Question&Answers more detail:
os