Wrapping Stripe create customer callbacks in Fibers in Meteor

独自空忆成欢 提交于 2019-12-13 18:57:45

问题


I'm having trouble getting Stripe.js to work when creating a new customer. Here is their Node.js code in their tutorial:

// Set your secret key: remember to change this to your live secret key in production
// See your keys here https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_9999999999999999999999");

// (Assuming you're using express - expressjs.com)
// Get the credit card details submitted by the form
var stripeToken = request.body.stripeToken;

stripe.customers.create({
  source: stripeToken,
  description: 'payinguser@example.com'
}).then(function(customer) {
  return stripe.charges.create({
    amount: 1000, // amount in cents, again
    currency: "usd",
    customer: customer.id
  });
}).then(function(charge) {
  saveStripeCustomerId(user, charge.customer);
});

This is my attempt. I wrapped all the callbacks in Meteor.bindEnvironment because async callbacks need to run in a fiber. I get an error in the server console:

Exception while invoking method 'submitOrder' Error: Stripe: Unknown arguments (function (/* arguments */) {  

Can anyone point me in the right direction for wrapping this in fibers? Or utilizing Meteor.wrapAsync?

var createStripeCustomer = function(ShoppingCartObject){
    check(ShoppingCartObject, Object);
    var stripe = Stripe("sk_test_9999999999999999");
    // (Assuming you're using express - expressjs.com)
    // Get the credit card details submitted by the form
    var stripeToken = ShoppingCartObject.charge.token;

    stripe.customers.create(
        {
          source: stripeToken,
          description: ShoppingCartObject._id,
          email: ShoppingCartObject.customerInfo.agentEmail,
        }, 
        Meteor.bindEnvironment(function(customer){
            return stripe.charges.create({
            amount: ShoppingCartObject.totalPrice, // amount in cents, again
            currency: "usd",
            customer: customer.id
            });
        }), 
        Meteor.bindEnvironment(function(charge){
            ShoppingCartObject.charge.customer = charge.customer;
            submitOrder(ShoppingCartObject);
        })
    );
};

var submitOrder = function(ShoppingCartObject){
  check(ShoppingCartObject, Object);
  var data = _.omit(ShoppingCartObject, '_id');
  var setHash = { $set: data };
  ShoppingCarts.update({_id: ShoppingCartObject._id}, setHash);
};

回答1:


Here is a simplified version of an approach that has worked for me. I basically create a function for each Stripe call that returns a Future.

// Server

var Future = Npm.require('fibers/future');

function createCustomer(token){
    var future = new Future;
    Stripe.customers.create({
      card: token.id,
      email: token.email
    }, function(error, result){
      if (error){
        future.return(error);
      } else {
        future.return(result);
      }
    });
    return future.wait();
  }

Meteor.methods({
  purchase: function(token){
    check(token: Object);
    try {
      var customer = createCustomer(token);
    } catch(error) {
      // error handle
    }
    // create charge, etc. repeating same pattern
  }
});



回答2:


I decided to work out everything using Meteor.wrapAsync(). The following code:

EDIT

After thinking about this, wrapAsync() seems to have some serious error-handling limitations, and especially for Stripe, where errors will be very common, my implementation below might be less-than-desireable.

Relevant discussion here: https://github.com/meteor/meteor/issues/2774

User faceyspacey has this code to create a "better" wrapAsync that deals with errors more intuitively, although I haven't tried it yet.

Meteor.makeAsync = function(fn, context) {
  return function (/* arguments */) {
    var self = context || this;
    var newArgs = _.toArray(arguments);
    var callback;

    for (var i = newArgs.length - 1; i >= 0; --i) {
      var arg = newArgs[i];
      var type = typeof arg;
      if (type !== "undefined") {
        if (type === "function") {
          callback = arg;
        }
        break;
      }
    }

    if(!callback) {
      var fut = new Future();
            callback = function(error, data) {
               fut.return({error:  error, data: data});
            };

      ++i; 
    }

    newArgs[i] = Meteor.bindEnvironment(callback);
    var result = fn.apply(self, newArgs);
    return fut ? fut.wait() : result;
  };
};

Original Code Below

  1. ShoppingCartObject has the details of the order and also the cardToken that is generated by Stripe.js when you feed it to the customer's credit card details.

  2. A new customer is created with the cardToken saved, essentially saving their CC info for later use.

  3. Lastly, a charge is created on the customer using their CC.

code below

var createStripeCustomerAsync = function(ShoppingCartObject, callback){

    var stripe = Stripe("sk_test_999999999999999999");

    stripe.customers.create({
        // this is the token generated on the client side from the CC info
      source: ShoppingCartObject.charge.cardToken,
      email: ShoppingCartObject.customer.email
    }, function(err, customer) {
      callback(err, customer);
    });

};

var createStripeCustomerSync = Meteor.wrapAsync(createStripeCustomerAsync);

var createStripeChargeAsync = function(customer, ShoppingCartObject, callback){

  var stripe = Stripe("sk_test_999999999999999999");

    stripe.charges.create({
      amount: ShoppingCartObject.totalPrice, // amount in cents, again
      currency: "usd",
      customer: customer.id
    }, function(error, charge){
        callback(error, charge);
    });

};

var createStripeChargeSync = Meteor.wrapAsync(createStripeChargeAsync);

var submitOrder = function(ShoppingCartObject){
    check(ShoppingCartObject, Object);

    var customer = createStripeCustomerSync(ShoppingCartObject);
    console.log("customer: ", customer);

    var charge = createStripeChargeSync(customer, ShoppingCartObject);
    console.log("charge: ", charge);

  var data = _.omit(ShoppingCartObject, '_id');
  var setHash = { $set: data };

  ShoppingCarts.update({_id: ShoppingCartObject._id}, setHash);
};


来源:https://stackoverflow.com/questions/31035175/wrapping-stripe-create-customer-callbacks-in-fibers-in-meteor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!