Ошибка: код метеора должен всегда запускаться внутри волокна

Я использую полосу для платежей в своем приложении, я хочу создать документ квитанции в моей собственной базе данных после успешной транзакции

Мой код:

Meteor.methods({
  makePurchase: function(tabId, token) {
    check(tabId, String);
    tab = Tabs.findOne(tabId);

    Stripe.charges.create({
      amount: tab.price,
      currency: "USD",
      card: token.id
    }, function (error, result) {
      console.log(result);
      if (error) {
        console.log('makePurchaseError: ' + error);
        return error;
      }

      Purchases.insert({
        sellerId: tab.userId,
        tabId: tab._id,
        price: tab.price
      }, function(error, result) {
        if (error) {
          console.log('InsertionError: ' + error);
          return error;
        }
      });
    });
  }
});

Однако этот код возвращает ошибку:

Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.

Я не знаком с Fibers, любая идея, почему это так?

Ответ 1

Проблема здесь в том, что функция обратного вызова, которую вы передаете в Stripe.charges.create, называется асинхронно (конечно), поэтому она происходит за пределами текущего метеорного Fiber.

Один из способов исправить это, чтобы создать собственное Fiber, но самое легкое, что вы можете сделать, это обернуть обратный вызов с помощью Meteor.bindEnvironment, поэтому в основном

Stripe.charges.create({
  // ...
}, Meteor.bindEnvironment(function (error, result) {
  // ...
}));

редактировать

Как было предложено в другом ответе, другой и, вероятно, лучший образец, который следует здесь, это использовать вспомогательный метод Meteor.wrapAsync (см. Docs), который в основном позволяет превратить любой асинхронный метод в функцию, осведомленную о волокнах, и может использоваться синхронно.

В вашем конкретном случае эквивалентное решение было бы написать:

let result;
try {
  result = Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)({ /* ... */ });
} catch(error) {
  // ...
}

Обратите внимание, что второй аргумент передан Meteor.wrapAsync. Именно там, чтобы убедиться, что первоначальный Stripe.charges.create получит надлежащее this контекст, только в случае, если это необходимо.