web-dev-qa-db-fra.com

Stripe: Doit fournir la source ou le client

J'essaie d'intégrer Stripe à mon projet de panier d'achat. Je n'arrive pas à obtenir mon formulaire de commande à soumettre. Je continue à recevoir ce message d'erreur: "Doit fournir la source ou le client." Soit je n'ai pas configuré mon compte Stripe correctement, soit il me manque des paramètres dans mon javascript. J'ai passé des heures sur ce problème et je n'arrive toujours pas à le comprendre. 

Ceci est tiré du journal de Stripe:
Requête analysée POST Body

{
  "amount": "21000",
  "currency": "usd",
  "description": "Test Charge"
}

Organisme d'intervention

{
  "error": {
    "type": "invalid_request_error",
    "message": "Must provide source or customer."
  }
} 

Ceci est mon 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;

Ceci est mon 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;

Ceci est mon 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();
  }
};

Ceci est mon formulaire de commande (.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>
6
vnguyen

Un flux de paiement avec Stripe est divisé en deux étapes:

  1. Côté client, dans votre code frontal, vous collectez et jetez les informations de paiement du client en utilisant Checkout ou Éléments , puis envoyez le jeton obtenu à votre serveur principal.

  2. Côté serveur, dans votre code d’arrière-plan, vous utilisez le jeton dans une demande d’API, par exemple. pour créer une charge ou un client .

Le code que vous avez partagé est pour la première étape. Cependant, le message d'erreur que vous avez mentionné:

Doit fournir la source ou le client.

se produit dans la deuxième étape. Cette erreur est renvoyée par l'API de Stripe lorsque vous envoyez un paramètre demande de création de charge sans paramètre source ou customer .

Vous devez vérifier votre code côté serveur pour déterminer le problème exactement. Vous pouvez également consulter les journaux de toutes les demandes envoyées par votre intégration dans votre tableau de bord: https://dashboard.stripe.com/test/logs?method=not_get _.

4
Ywain

J'ai donc envoyé un e-mail à l'équipe d'assistance de Stripe pour obtenir de l'aide sur ce problème il y a quelques jours et j'ai finalement trouvé la solution.

L'erreur était en réalité dans le front-end. Je n'ai pas fait référence à jquery dans le formulaire de commande. J'ai pu résoudre le problème en ajoutant simplement <script type="text/javascript" src="/javascripts/jquery-3.1.1.min.js"></script> avant d'appeler le fichier checkout.js. Mon formulaire de commande fonctionne maintenant. 

Des atouts énormes à Stripe pour aller au-delà de leurs objectifs!

2
vnguyen

Peut-être la même conférence vidéo, mais changer pour "tok_mastercard" a fonctionné pour moi

0
user10913556

Peut-être que je faisais également référence aux mêmes conférences vidéo que vous et j'ai rencontré le même problème. Je viens d'apporter des modifications à la route /checkout (demande de publication) dans le fichier index.js. Remplacer source: req.body.stripeToken par source: "tok_mastercard" et cela a fonctionné pour moi, mais je ne suis pas sûr de la raison derrière tout cela.

0
Dipesh Budhiraja