javascript - Meteor: uso adecuado de Meteor.wrapAsync en el servidor
stripe-payments (4)
Como necesitará casi todas las funciones para envolver en Async, lo que debe hacer es usar este paquete
https://atmospherejs.com/copleykj/stripe-sync
que preenvuelve todas las funciones de banda con WrapAsync, lo que hace que su vida sea más fácil y limpia el código.
Antecedentes
Estoy tratando de integrar pagos en línea en mi sitio. Necesito crear un usuario de banda usando mi clave de banda privada. Estoy almacenando esta clave en mi servidor y llamo a un método de servidor para crear el usuario. Tal vez hay otra forma de lograr esto? Aquí está la api de la raya (copiada a continuación para mayor comodidad): https://stripe.com/docs/api/node#create_customer
//stripe api call
var Stripe = StripeAPI(''my_secret_key'');
Stripe.customers.create({
description: ''Customer for [email protected]'',
card: "foobar" // obtained with Stripe.js
}, function(err, customer) {
// asynchronously called
});
Mis intentos y resultados
He estado usando el mismo código de cliente con un código de servidor diferente. Todos los intentos dan inmediatamente undefined en el console.log del cliente (...) pero dan la respuesta adecuada en el servidor console.log (...):
//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
}));
}
});
También probé ambos sin Meteor.wrapAsync.
EDITAR: también estoy usando este paquete: https://atmospherejs.com/mrgalaxy/stripe
Desde
Meteor.wrapAsync
http://docs.meteor.com/#meteor_wrapasync
puede ver que necesita pasarle una función y opcionalmente un contexto, mientras que en sus dos intentos está pasando el RESULTADO de llamar a la versión asíncrona 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
transforma una función asíncrona en una conveniente función de aspecto sincrónico que permite escribir código de aspecto secuencial.
(Underhood todo todavía se ejecuta dentro del bucle de eventos asíncrono Node.js).
Necesitamos pasar a
Meteor.wrapAsync
nuestra función API (
Stripe.customers.create
) junto con el contexto de la función, es decir,
this
dentro del cuerpo de la función API, que en este caso es
Stripe.customers
.
EDITAR:
¿Cómo recuperar errores?
Las funciones de API de estilo de nodo tradicional generalmente toman una devolución de llamada como último argumento que se llamará en última instancia cuando se complete la tarea requerida. Esta devolución de llamada toma 2 argumentos: error y datos, cualquiera de los dos será nulo según el resultado de la llamada.
¿Cómo accedemos al objeto de error utilizando las funciones sincrónicas
Meteor.wrapAsync
devueltas por
Meteor.wrapAsync
?
Tenemos que confiar en el uso de bloques try / catch, porque en caso de error, será arrojado por la función de sincronización en lugar de pasarlo como primer argumento de la devolución de llamada de la función asíncrona.
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);
});
¿Por qué no se necesita pasar Stripe?
JavaScript no tiene un concepto de "espacio de nombres", por lo que los desarrolladores de API utilizan el truco común de definir un objeto global que actúa como espacio de nombres de API, las propiedades definidas en este objeto son "submódulos" de la API.
Significa que
Stripe.customers
es un submódulo de la API Stripe que expone funciones relacionadas con los clientes, y como tales funciones,
this
contexto es
Stripe.customers
, no
Stripe
.
Puede probarlo usted mismo copiando y pegando este código de burla en la consola de su navegador:
Stripe={
customers:{
create:function(){
console.log(this==Stripe.customers);
}
}
};
Y luego llamando a la función de código auxiliar en la consola de su navegador de esta manera:
> Stripe.customers.create();
true
En primer lugar, gracias a @saimeunt por su respuesta, que deja claros algunos conceptos difíciles. Sin embargo, tuve el problema de querer una devolución de llamada asíncrona clásica (err, result) que mostrara tanto el error como el resultado en el cliente, para poder dar mensajes informativos en el navegador.
Lo resolví de esta manera:
Código del servidor:
var Stripe = StripeAPI(STRIPE_SECRET_KEY);
Meteor.methods({
createCust: Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)
});
Codigo del cliente:
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);
});
Se ve bien. Sin embargo, lamentablemente, wrapAsync tiene un error abierto, (consulte https://github.com/meteor/meteor/issues/2774 ) porque no restablece el error adecuado para la persona que llama. Un genio llamado Faceyspacey ha escrito un reemplazo llamado Meteor.makeAsync () que encontrará en la página de error que mencioné, que sin embargo devuelve el resultado O el error a la variable ''resultado'', dejando la variable ''error'' indefinida. Eso está bien para mí por ahora, al menos tengo un gancho en el objeto de error adecuado.
Si usa makeAsync () necesitará importar futuros como este:
Meteor.startup(function () {
//this is so that our makeAsync function works
Future = Npm.require(''fibers/future'');
});
Otra opción es este package que logra los objetivos similares.
meteor add meteorhacks:async
Del paquete README:
Async.wrap (función)
Envuelva una función asincrónica y permita que se ejecute dentro de Meteor sin devoluciones de llamada.
//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;
}
});