问题
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
ShoppingCartObject
has the details of the order and also thecardToken
that is generated byStripe.js
when you feed it to the customer's credit card details.A new customer is created with the
cardToken
saved, essentially saving their CC info for later use.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