Meteor: utilisation correcte de Meteor.wrapAsync sur le serveur

arrière-plan

j'essaie d'intégrer les paiements à rayures dans mon site. Je dois créer un utilisateur de rayure en utilisant ma clé de rayure privée. Je stocke cette clé sur mon serveur, et j'appelle une méthode de serveur pour créer l'utilisateur. Peut-être qu'il y a un autre moyen d'accomplir ça? Voici l'api stripe (copiée ci-dessous pour plus de commodité)): https://stripe.com/docs/api/node#create_customer

//stripe api call
var Stripe = StripeAPI('my_secret_key');

Stripe.customers.create({
  description: 'Customer for test@example.com',
  card: "foobar" // obtained with Stripe.js
}, function(err, customer) {
  // asynchronously called
});

mes tentatives et résultats

j'ai utilisé le même code client avec un code serveur différent. Toutes les tentatives donnent immédiatement non défini sur la console du client.journal.(..), mais donner la bonne réponse sur la console du serveur.journal.(..):

//client
Meteor.call('stripeCreateUser', options, function(err, result) {
  console.log(err, result);
});

//server attempt 1
var Stripe = StripeAPI('my_secret_key');

Meteor.methods({
    stripeCreateUser: function(options) {  
        return Meteor.wrapAsync(Stripe.customers.create({
            description: 'Woot! A new customer!',
            card: options.ccToken,
            plan: options.pricingPlan
        }, function (err, res) {
            console.log(res, err);
            return (res || err);
        }));
    }
});

//server attempt 2
var Stripe = StripeAPI('my_secret_key');

Meteor.methods({
    stripeCreateUser: function(options) {  
        return Meteor.wrapAsync(Stripe.customers.create({
            description: 'Woot! A new customer!',
            card: options.ccToken,
            plan: options.pricingPlan
        }));
    }
});

j'ai aussi essayé les deux sans météore.wrapAsync.

EDIT - j'utilise aussi ce paquet: https://atmospherejs.com/mrgalaxy/stripe

49
demandé sur Brett McLain 2014-10-07 03:53:30

4 réponses

From the Meteor.wrapAsync http://docs.meteor.com/#meteor_wrapasync vous pouvez voir que vous avez besoin de passer une fonction et optionnellement un contexte, alors que sur vos deux tentatives vous passez le résultat de l'appel de la version async de Stripe.customers.create .

Meteor.methods({
  stripeCreateUser: function(options) {
    // get a sync version of our API async func
    var stripeCustomersCreateSync=Meteor.wrapAsync(Stripe.customers.create,Stripe.customers);
    // call the sync version of our API func with the parameters from the method call
    var result=stripeCustomersCreateSync({
      description: 'Woot! A new customer!',
      card: options.ccToken,
      plan: options.pricingPlan
    });
    // do whatever you want with the result
    console.log(result);
  }
});

Meteor.wrapAsync transforme une fonction asynchrone en une fonction pratique d'apparence synchrone qui permet d'écrire du code d'apparence séquentielle. (sous le capot, tout est encore exécuté dans le noeud asynchrone.js boucle d'événement).

nous devons passer à Meteor.wrapAsync notre fonction API ( Stripe.customers.create ) avec le contexte de la fonction, c'est-à-dire this dans le corps de L'API func, qui dans ce cas est Stripe.customers .

EDIT:

Comment récupérer les erreurs ?

les fonctions traditionnelles D'API de style noeud prennent généralement un rappel comme dernier argument qui sera appelée en fin de compte lorsque la tâche est terminée. Ce callback prend 2 arguments: error et data, l'un ou l'autre sera nul selon le résultat de l'appel.

comment accéder à l'objet d'erreur en utilisant les fonctions enveloppées synchrones retournées par Meteor.wrapAsync ?

nous devons nous appuyer sur l'utilisation de blocs try/catch, parce qu'en cas d'erreur, il sera lancé par la fonction sync au lieu d'être passé comme premier argument de la asynchrone en fonction de rappel.

try{
  var result=syncFunction(params);
  console.log("result :",result);
}
catch(error){
  console.log("error",error);
}
// is the equivalent of :
asyncFunc(params,function(error,result){
  if(error){
    console.log("error",error);
    return;
  }
  console.log("result :",result);
});

pourquoi ne faut-il pas passer Stripe?

JavaScript n'a pas de concept" namespace", donc les développeurs D'API utilisent l'astuce courante de définir un objet global agissant en tant que namespace API, les propriétés définies sur cet objet sont des" sous-modules " de l'API. Cela signifie que Stripe.customers est un sous-module de L'API de bande exposant les fonctions liées aux clients, et en tant que tel ces fonctions this le contexte est Stripe.customers , pas Stripe .

vous pouvez le tester vous-même en copiant ce code de moquerie dans votre console de navigateur:

Stripe={
  customers:{
    create:function(){
      console.log(this==Stripe.customers);
    }
  }
};

et ensuite appeler la fonction de talon dans la console de votre navigateur comme ceci:

> Stripe.customers.create();
true
73
répondu saimeunt 2015-01-20 11:50:25

une autre option est ce paquet qui atteint les objectifs similaires.

meteor add meteorhacks:async

dans le paquet README:

Async.wrap(fonction)

envelopper une fonction asynchrone et lui permettre d'être exécuté à L'intérieur de météore sans callbacks.

//declare a simple async function
function delayedMessge(delay, message, callback) {
  setTimeout(function() {
    callback(null, message);
  }, delay);
}

//wrapping
var wrappedDelayedMessage = Async.wrap(delayedMessge);

//usage
Meteor.methods({
  'delayedEcho': function(message) {
    var response = wrappedDelayedMessage(500, message);
    return response;
  }
});
11
répondu FullStack 2015-01-13 03:52:20

tout d'abord, merci à @saimeunt pour sa réponse, qui rend certains concepts difficiles clairs. Cependant, j'ai eu le problème de vouloir un rappel async classique (err, résultat) montrant à la fois l'erreur et le résultat sur le client, de sorte que je peux donner des messages informatifs dans le navigateur.

Je l'ai résolu de cette façon:

Code serveur:

var Stripe = StripeAPI(STRIPE_SECRET_KEY);

Meteor.methods({
    createCust: Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)
});

code Client:

var stripeCallOptions = {
    description: 'Woot! A new customer!',
    card: ccToken,
    plan: pricingPlan
};


Meteor.call('createCust', stripeCallOptions, function(error, result){
    console.log('client error', error);
    console.log('client result', result);
});

semble soigné. Malheureusement wrapAsync a un bug ouvert, (voir https://github.com/meteor/meteor/issues/2774 ) parce qu'il ne restitue pas l'erreur correcte à l'appelant. Un génie appelé Faceyspacey a écrit un remplaçant appelé Meteor.makeAsync () que vous trouverez sur la page de bug que j'ai mentionnée, qui renvoie cependant le résultat ou l'erreur à la variable 'result', laissant la variable 'error' non définie. C'est bien pour moi pour l'instant, au moins j'ai un crochet sur la objet d'erreur approprié.

si vous utilisez makeAsync() vous devrez importer des Futures comme ceci:

Meteor.startup(function () {
    //this is so that our makeAsync function works
    Future = Npm.require('fibers/future');
});
7
répondu mwarren 2015-08-19 15:21:48

puisque vous allez avoir besoin de presque toutes les fonctions à envelopper dans Async ce que vous devez faire est d'utiliser ce paquet https://atmospherejs.com/copleykj/stripe-sync il pré-enveloppe toutes les fonctions de bande avec WrapAsync rendant votre vie plus facile et le code plus propre.

1
répondu Patrick Mencias-lewis 2016-06-10 20:22:06