Trouble returning a valid result while integrating Stripe API into my Meteor app - meteor

Here's the rundown:
I'm trying to run Stripe API on my Meteor app asynchronously
Long story short, everything works (i.e. subscription and charge is/are created normally and shows up in my Stripe dashboard)
When errors occur, the errors throw normally and show on client via user friendly alerts
I have a problem when there is a success and customer subscription is created, the result is not present in client and instead always returns as an error, despite it being a successful process
Here's what my method looks like on the server:
createCustomer: function(token, email, plan){
try{
let createCustomer = Meteor.wrapAsync(stripe.customers.create);
let result = createCustomer({
source: token,
email: email,
plan: plan
});
let subscription = {
customer: result.id,
sub: result.subscriptions.data[0].id,
plan: result.subscriptions.data[0].plan.name
};
Meteor.users.update({_id: Meteor.userId()}, {$set: subscription});
} catch(error){
if(error.code === "incorrect_cvc"){
throw new Meteor.Error("incorrect_cvc", error.message);
}
// More of such errors follows
}
}
Here's what it looks like on the client:
Stripe.card.createToken({
number: number,
cvc: cvc,
exp_month: exp,
exp_year: exp_year,
address_zip: zip,
address_country: country
}, function(status, response){
if(response.error){
console.log("Make sure all fields are filled before submitting order.");
} else{
let token = response.id;
Meteor.call("createCustomer", token, email, plan, function(error, result){
if(result){
console.log("Congratulations, everything worked!");
} else{
if(error.error === "incorrect_cvc"){
console.log("oops, the CSV is incorrect");
}
// More of such errors follow..
}
})
}
});
So, everything works in terms of when there is a real error, it throws fine on server + client. When user uses card, the charges are created and subscription is always created. HOWEVER, when there is a success and everything clicking fine, I still receive an error on client via callback and the result is never true or triggered. No idea why.

Not 100% up on Meteor, but it looks to me like your createCustomer method doesn't actually return anything, so the result from your (err, result) might never have anything in it?
As was mentioned in the comments, you might want to separate out the steps and wrap each in its own try-catch set so you can better isolate the issue.
Also, I feel like you could probably generalize your server-side error code to something like:
throw new Meteor.Error(error.error, error.message);
And I might even be tempted to do something like this, at least during testing/development - that way you can actually console.log() the original error in the browser:
throw new Meteor.Error(error.error, error.message, JSON.stringify(error));

Related

Unhandled Rejection (SyntaxError): Unexpected token < in JSON at position 0?

I am trying to fetch local api made in asp.net api which is running in https://localhost:44388/. When I tried to fetch get request it responds ok but return html not json. The problem might occur by two reasons:
1.typo in url (But I checked in my browser, it worked)
2.Server restart needed
What might be the problem with my code?
componentDidMount(){
var proxyUrl = "http://127.0.0.1:3000/";
var targetUrl = "https://127.0.0.1:44388/api/product/getproducts";
fetch(proxyUrl+targetUrl, {
method:'GET',
headers:{
'Access-Control-Allow-Origin':'*',
'Access-Control-Allow-Mehods': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Credentials':'*',
'Content-type':'application/json'
}
})
.then(data=>{
if(!data.ok){
throw new Error("Error");
}else{
return data.json();
}
})
.then(data=>this.setState({products:data}))
}
when you give parameter as:proxyUrl+targetUrl,
actually the url which you have called is :
http://127.0.0.1:3000/https://127.0.0.1:44388/api/product/getproducts
which does not seems to be correct.
i think the structure of url you'v given to fetch function is wrong.

Meteor: Using iron router and custom authentication issue

I might have this pretty close but I'm lacking the knowledge to fix this last issue.
I wanted to use a custom authentication system instead of using accounts-ui so I could track some additional details about each user.
Everything worked great until I get to the resetPassword part. If a user submits their email address in the forgotPassword form, the email is received. But when you click the reset password link in the email it does not display the resetPassword template.
This is on SO here:
Meteor account email verify fails two ways
And the iron-router github issue tracker here (which has the most fixes though is more focused on the enrollmentemail than resetPassword which I'm assuming should be very similar):
Iron-router swallows Accounts.sendEnrollmentEmail
If I understand correctly from the iron-router issue tracker above, iron-router doesn't (or didn't and maybe still doesn't) support hashbang urls like that being sent in the reset password email. A URL like:
http://localhost:3000/#/reset-password/T4rPxcVNWKwBONHSRajSk7dNZvM_YRxTLyzxZVv5SuU
Meteor was then updated so that meteor accounts-base strips out everything after the # and stores them in variables in the Accounts namespace.
While I think I understand all of that, now the question is why I can't get the suggestions in the issue tracker to work for my reset password code. I'm using everything that is in the custom auth system by Julien Le Coupanec and then I've done the following from the issue tracker:
router.js
Router.map(function() {
this.route('invList', {path: '/'});
this.route('resetPassword', {
controller: 'AccountController',
path: '/reset-password/:token',
action: 'resetPassword'
});
});
AccountController = RouteController.extend({
resetPassword: function () {
Accounts.resetPassword(this.params.token, function () {
Router.go('/reset-password');
});
}
});
overrideaccounts.js in /server
(function () {
"use strict";
Accounts.urls.resetPassword = function (token) {
return Meteor.absoluteUrl('reset-password/' + token);
};
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl('verify-email/' + token);
};
Accounts.urls.enrollAccount = function (token) {
return Meteor.absoluteUrl('enroll-account/' + token);
};
})();
I'm wondering if the issues isn't related to either bad routing on my part (likely since I don't have my head wrapped around it well yet), if I put "server code" as is listed in the issue track in the right place, or if the session related code below is what is causing the resetPassword template to not display. Or something else that I'm missing of course.
main.js
//forgotPassword helper and event handler
Template.main.helpers({
showForgotPassword: function() {
return Session.get('showForgotPassword');
},
resetPassword: function(){
return Session.get('resetPassword');
}
});
After spending many hours on what I thought would be a really simple authentication system, I'm still at a loss. Appreciate any advice!
Don't struggle with hacking the hash and iron router, just back to Meteor original design flow.
When user click the verify link in email, it lead back to "/" (home), so just did this:
Template.home.created = function() {
if (Accounts._verifyEmailToken) {
Accounts.verifyEmail(Accounts._verifyEmailToken, function(err){
if (err != null) {
// handle the error
} else {
// do what you want, maybe redirec to some route show verify successful message
}
});
}
};
I did this and verify email right, same way worked for enroll, reset password...

Apigee 401 Unauthorized with token_expired

I'm trying to create a user account through the apigee JS API. This worked just fine when I was last doing this before the holidays in mid December. Now, however, I get a 401 Unauthorized error reading token_expired.
Is there a way to refresh the token? I don't know why it would have expired.
This is what I'm trying. First I instantiate the data client. No problems here:
var dataClient;
var client_creds = {
orgName: '*******',
appName: '*******'
}
dataClient = new Apigee.Client(client_creds);
Later, when trying to create a new user, I get the token_expired error:
dataClient.request(options, function (error, response) {
if (error) {
console.log(response);
alert("Something went wrong when trying to create the user. " + response.error)
// Error
} else {
// Success - the user has been created, now login.
dataClient.login(user.email, user.password,
function (err) {
if (err) {
//error - could not log user in
console.log("There was an error logging in " + user.name);
} else {
//success - user has been logged in
}
}
);
}
});
I've also tried dataClient.signup, but same error.
There are no refresh tokens within App Services; you'll need to follow the login flow in order to retrieve a new token. Note that you can specify the ttl parameter, like so, so you don't need to do this as frequently:
https://api.usergrid.com/{org}/{app}/token?ttl=604800
By default, this is set to 7 days, but you can change the default app max ttl to 0 (non-expiring) or something else like 31104000000 (365 days).
To do that, you make a PUT request:
https://api.usergrid.com/{org}/{app}/?client_id={app_client_id}&client_secret={app_client_secret}
With JSON payload:
{
"accesstokenttl":0
}
Or for 1 year:
{
"accesstokenttl":31104000000
}
If that doesn't work for you, the authorization tokens for the JavaScript SDK are kept in your browser's local storage. In Chrome, use the Developer Tools. In the Resources tab on the left hand side expand the Local Storage entry. You should see something like "http://usergrid.dev" or something similar. Choose that and on the right hand side you should see an entry for accessToken. Delete that and it should solve your problem.

How to call async method from Meteor own callbacks?

I've just spent a few hours reading SO with answers such as Meteor: Calling an asynchronous function inside a Meteor.method and returning the result
Unfortunately, I still didn't manage to user fibers, or futures for that matter.
I'm trying to do something fairly simple (I think!).
When creating a user, add a variable to the user object, based on the result of an asynchronous method. So imagine if you will my async method is called on a 3rd party db server called BANK, which could take several seconds to return.
Accounts.onCreateUser(function(options,user){
var Fiber = Npm.require("fibers");
Fiber(function() {
BANK.getBalance(function(err, theBalance) {
if (err) return console.log(err);
_.extend(user,{
balance: theBalance;
});
});
}).run();
return user;
});
So what happens in the above is that the BANK method is called, but by the time it returns the code has already moved on and _.extend is never invoked.
I tried placing the return call inside the Fiber, that only made things worse: it never return user. Well it did, but 3 seconds too late so by then everything downstream was bailing out.
Thank you for any help!
Answering my own question which hopefully will help some people in the future. This is based on the excellent advice of Avital Oliver and David Glasser to have a look at Mike Bannister's meteor-async.md. You can read it here: https://gist.github.com/possibilities/3443021
Accounts.onCreateUser(function(options,user){
_.extend(user,{
balance: getBalance(),
});
return user;
});
function getBalance() {
var Future = Npm.require("fibers/future");
var fut = new Future();
BANK.getBalance(function(err, bal) {
if (err) return console.log(err);
fut.return(bal);
});
return fut.wait();
}
I believe there's an even better way to handle this, which is directly by wrapping the BANK API in Futures within the npm package itself, as per this example (from Avital Oliver): https://github.com/avital/meteor-xml2js-npm-demo/blob/master/xml2js-demo.js
I hope it helps!
Use this.unblock() on server side code.
From Meteor 1.0 documentation: "Allow subsequent method from this client to begin running in a new fiber.On the server, methods from a given client run one at a time. The N+1th invocation from a client won't start until the Nth invocation returns. However, you can change this by calling this.unblock. This will allow the N+1th invocation to start running in a new fiber."
Meteor.methods({checkTwitter: function (userId) {
check(userId, String);
this.unblock();
try {
var result = HTTP.call("GET", "http://api.twitter.com/xyz",
{params: {user: userId}});
return true;
} catch (e) {
// Got a network error, time-out or HTTP error in the 400 or 500 range.
return false;
}
}});
method calls use the sync style (see 'sync call' here http://docs.meteor.com/#meteor_call) on the server side, which is where this create user method runs - you should be able to do something like
Accounts.onCreateUser(function(options, user) {
user.balance = Meteor.call('getBankBalance', params);
return user;
});
Thanks yo so much that's work, This solution its better for Meteor projects, because Fibers module installed by default. mrt add npm has a method for this too -> Meteor.sync . For any nodeJS projects there is a other module based on Fibers, its name is Fibrous
Reference:https://github.com/goodeggs/fibrous

Using tinytest to test Meteor client while the server is running

Is it possible to test the Meteor client while the server is running using tinytest? Here's my example testing the client only:
Tinytest.add("Add object to a collection", function(test) {
var people = new Meteor.Collection("people");
people.insert({"name": "Andrew"}, function(error, id) {
test.isNull(error);
});
});
For a fraction of a second this passes, but then it goes into the state of "waiting". I'm also positive that error is not null.
Meteor.Error {error: 404, reason: "Method not found", details: undefined}
I know this is happening because their is no server for the client to communicate with. When I try to run this test on the server and client, I continue to get the same issue with the client. Is there a way to test the client while the server is running?
Thanks, Andrew
Use new Meteor.Collection with no argument to create a stub collection that doesn't require the server. See the docs on Collections:
If you pass null as the name, then you're creating a local collection. It's not synchronized anywhere; it's just a local scratchpad that supports Mongo-style find, insert, update, and remove operations.
This is an async test, so you'll have to use addAsync.
Tinytest.addAsync("Add object to a collection", function(test, next) {
var people = new Meteor.Collection("people");
people.insert({"name": "Andrew"}, function(error, id) {
test.isNull(error);
next();
});
});
Note the next argument which signals that you are done in the callback.

Resources