1
if (Meteor.isClient) {

  Template.hello.events({
    'click input': function () {

      //create a new customer
      Meteor.call('createCustomer', function (error, result) { 
        console.log("Error: " + error + "  Result: " + result); } );
    }
  });
}

if (Meteor.isServer) {
  Meteor.methods({
    createCustomer: function () {
      try {
      balanced.configure('MyBalancedPaymentsTestKey');
      var customer = Meteor._wrapAsync(balanced.marketplace.customers.create());
      var callCustomer = customer();
      var returnThis = console.log(JSON.stringify(callCustomer, false, 4));
      return returnThis;
    } catch (e) {
      console.log(e);
      var caughtFault = JSON.stringify(e, false, 4);
    }
    return caughtFault;
    }
  });
}

And I just used the default hello world without the greetings line.

<head>
  <title>testCase</title>
</head>

<body>
  {{> hello}}
</body>

<template name="hello">
  <h1>Hello World!</h1>
  <input type="button" value="Click" />
</template>

On the client side the log prints

Error: undefined Result: {}

On the server side the log prints

[TypeError: Object [object Promise] has no method 'apply']

Any idea how I can wait for that promise instead of returning the blank result?

JoshJoe
  • 1,482
  • 2
  • 17
  • 35

3 Answers3

4

I'm assuming balanced.marketplace.customers.create returns a Promises/A+ promise. This is an object with a method .then(fulfillmentCallback, rejectionCallback) - the fulfillmentCallback is called when the operation succeeds, and the rejectionCallback is called if the operation had an error. Here's how you could use Futures to synchronously get the value out of a promise:

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

function extractFromPromise(promise) {
  var fut = new Future();
  promise.then(function (result) {
    fut["return"](result);
  }, function (error) {
    fut["throw"](error);
  });
  return fut.wait();
}

Then you can just call balanced.marketplace.customers.create normally (no _wrapAsync) to get a promise, then call extractFromPromise on that promise to get the actual result value. If there's an error, then extractFromPromise will throw an exception.

By the way, code in if (Meteor.isServer) blocks is still sent to the client (even if the client doesn't run it), so you don't want to put your API key in there. You can put code in the server directory, and then Meteor won't send it to the client at all.

user3374348
  • 4,101
  • 15
  • 29
  • Wow, bravo, you are my hero. You have no idea how much time I've spent trying to solve this problem. Thank you! I put in on JSFiddle.net so that you can see the code better. http://jsfiddle.net/zhvTq/1/ Then of course to access the settings.json file with the API key I ran mrt --settings settings.json – JoshJoe May 21 '14 at 14:46
0

Update this line

 var customer = Meteor._wrapAsync(balanced.marketplace.customer.create)();
zhaoyou
  • 308
  • 4
  • 19
  • When I change that line I get nothing on both the client and server sides. I even added console logs between each step within the method and I get nothing printed to the console on the server or client side... – JoshJoe May 21 '14 at 04:13
  • Show me the code: `balanced.marketplace.customers.create` Define – zhaoyou May 21 '14 at 09:34
  • I am using the package balanced-payments-production, which wraps the balanced-official node package so that I can call balanced from anywhere in my code. To see that you can look at the node package https://www.npmjs.org/package/balanced-official – JoshJoe May 21 '14 at 14:24
0

Another approach is to use Futures. I use this a lot on the server side to wait for results to return back to the client.

Here's a small example of that I use for logins:

Accounts.login(function (req, user) {
    var Future = Npm.require("fibers/future");
    var fut = new Future();
    HTTP.call("POST", globals.server + 'api/meteor-approvals/token',
        {
            timeout: 10000, followRedirects: true,
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            params: {
                username: userName,
                password: req.password
            }},
        function (err, result) {
            if (err) {
                logger.error("Login error: " + err);
                fut.throw(err);
            }
            else {
                fut.return("Success");
            }
        }
    );
    return fut.wait();
}
occasl
  • 5,303
  • 5
  • 56
  • 81
  • Ok, I have tried to use futures before. I'm not sure how to write my function. Here is how I rewrote it, but I know this isn't right. ```createCustomerFuture: function () { var Future = Npm.require("fibers/future"); var fut = new Future(); function runThisHere(function (err, result) { balanced.configure('TESTKEYHERE'); balanced.marketplace.customer.create(); }); return fut.wait(); }``` – JoshJoe May 21 '14 at 04:32
  • I change the code a little and now I get the same result as the other solution posted by zhaoyou. ```createCustomerFuture: function () { var Future = Npm.require("fibers/future"); var fut = new Future(); function runThisHere(err, result) { balanced.configure('TESTKEYHERE'); var customer = balanced.marketplace.customer.create(); console.log(customer); }; return fut.wait(); }``` – JoshJoe May 21 '14 at 04:40
  • It's hard to read (not sure why stackoverflow doesn't allow comments w/code but that's a separate issue), but I get the gist. You need to put a `fut.return(customer)` in your `runThisHere` function assuming you want to return the customer to the calling function. HTH. – occasl May 21 '14 at 05:17
  • I added that and I still don't get any response. I now have _wrapAsync, futures and Async.wrap functions. I get no response from _wrapAsync and no response from futures. I get a ```Error: undefined Result: {} ``` from the Async.wrap function. I wonder if there is something outside of async coding that is wrong here. Maybe I'm using the API call to balanced wrong(although I'm following their document https://docs.balancedpayments.com/1.1/api/customers/#create-a-customer). Any other thoughts would be great. – JoshJoe May 21 '14 at 05:40
  • I'm not familiar with this but looking at those docs seems to indicate that you have to do an HTTP POST to your balancedpayments server, no? If that's the case, that you can use my code above that does a POST and plugin your own values for header, params and data. – occasl May 21 '14 at 05:49
  • You do need to do some kind of api call, or POST to the URI of the newly created records. The problem is that I first need the initial create to work so I can see the URI of the new customer, then do a POST or another API call using that URI. – JoshJoe May 21 '14 at 05:54