title: Stripe: Must provide source or customer
body: <p>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. </p>
  
  <p>This is from Stripe's log:<br>
  <strong>Parsed Request POST Body</strong></p>
  
  <pre><code>{
    "amount": "21000",
    "currency": "usd",
    "description": "Test Charge"
  }
  </code></pre>
  
  <p><strong>Response Body</strong></p>
  
  <pre><code>{
    "error": {
      "type": "invalid_request_error",
      "message": "Must provide source or customer."
    }
  } 
  </code></pre>
  
  <p><strong>This is my app.js</strong></p>
  
  <pre><code>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;
  </code></pre>
  
  <p><strong>This is my index.js:</strong></p>
  
  <pre><code>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 &lt; 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;
  </code></pre>
  
  <p><strong>This is my checkout.js:</strong></p>
  
  <pre><code>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($('&lt;input type="hidden" name="stripeToken" /&gt;').val(token));
  
      // Submit the form:
      $form.get(0).submit();
    }
  };
  </code></pre>
  
  <p><strong>This is my checkout form (.hbs):</strong></p>
  
  <pre><code>&lt;div class="row"&gt;
    &lt;div class="col-sm-6 col-md-6 col-md-offset-3 col-sm-offset-3"&gt;
      &lt;h1&gt;Checkout&lt;/h1&gt;
      &lt;h4&gt;Your Total: ${{total}}&lt;/h4&gt;
      &lt;div id="charge-error" class="alert alert-danger {{# if noError}}hidden{{/if}}"&gt;
          {{errMsg}}
      &lt;/div&gt;
      &lt;form action="/checkout" method="post" id="checkout-form"&gt;
        &lt;div class="row"&gt;
            &lt;div class="col-xs-12"&gt;
              &lt;div class="form-group"&gt;
                &lt;label for="name"&gt;Name&lt;/label&gt;
                &lt;input type="text" id="name" class="form-control" required&gt;
              &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="col-xs-12"&gt;
              &lt;div class="form-group"&gt;
                &lt;label for="name"&gt;Address&lt;/label&gt;
                &lt;input type="text" id="address" class="form-control" required&gt;
              &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="col-xs-12"&gt;
              &lt;div class="form-group"&gt;
                &lt;label for="name"&gt;Name on Card&lt;/label&gt;
                &lt;input type="text" id="card-name" class="form-control" required&gt;
              &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="col-xs-12"&gt;
              &lt;div class="form-group"&gt;
                &lt;label for="name"&gt;Credit Card Number&lt;/label&gt;
                &lt;input type="text" id="card-number" class="form-control" required&gt;
              &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="col-xs-6"&gt;
              &lt;div class="form-group"&gt;
                &lt;label for="name"&gt;Expiration Month&lt;/label&gt;
                &lt;input type="text" id="card-expiry-month" class="form-control" required&gt;
              &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="col-xs-6"&gt;
              &lt;div class="form-group"&gt;
                &lt;label for="name"&gt;Expiration Year&lt;/label&gt;
                &lt;input type="text" id="card-expiry-year" class="form-control" required&gt;
              &lt;/div&gt;
            &lt;/div&gt;
            &lt;div class="col-xs-12"&gt;
                &lt;div class="form-group"&gt;
                &lt;label for="name"&gt;
                CVC&lt;/label&gt;
                &lt;input type="text" id="card-cvc" class="form-control" required&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;button type="submit" class="btn btn-success"&gt;Buy Now&lt;/button&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  
  &lt;script type="text/javascript" src="https://js.stripe.com/v2/"&gt;&lt;/script&gt;
  &lt;script type="text/javascript" src="javascripts/checkout.js"&gt;&lt;/script&gt;
  </code></pre>