I have an App using Parse.com as a backend and an external site that acts as my payment gateway. Upon receiving the customer/subscription webhook data from Stripe I wish to lookup the users email so I can then run a Cloud Code function and change their user status to 'paid'
My webhook receiver is:
Parse.Cloud.define("update_user", function(request, response) {
var data = request.params["data"]
var customer = data.object.customer;
response.success'Working' + request);
});
And I am able to get an email back from stripe from the customer ID using:
Parse.Cloud.define("pay", function(request, response) {
Stripe.initialize(STRIPE_SECRET_KEY);
console.log(JSON.stringify(request.params));
Stripe.Customers.retrieve(
customerId, {
success:function(results) {
console.log(results["email"]);
// alert(results["email"]);
response.success(results);
},
error:function(error) {
response.error("Error:" +error);
}
}
);
});
I need help turning this into a complete function that is run on receipt of every webhook from Stripe. I am also struggling with options for fallback if this does not work for whatever reason.
EDIT
Taking parts of the first answer and I now have:
Parse.Cloud.define("update_user", function(request, response) {
Stripe.initialize(STRIPE_SECRET_KEY);
var data = request.params["data"]
var customerId = data.object.customer;
get_stripe_customer(customerId, 100).then(function(stripeResponse) {
response.success(stripeResponse);
}, function(error) {
response.error(error);
});
});
function get_stripe_customer (customerId) {
Stripe.initialize(STRIPE_SECRET_KEY);
return Stripe.Customers.retrieve(
customerId, {
success:function(results) {
console.log(results["email"]);
},
error:function(error) {
}
}
);
};
My knowledge is really falling down on the Promise side of things and also the callback (success:, error, request response) etc further reading would be appreciated.
This is now working
Out of interest I did this:
Parse.Cloud.define("update_user", function(request, response) {
var data = request.params["data"]
var customerId = data.object.customer;
get_stripe_customer(customerId, 100).then(function(stripeResponse) {
return set_user_status(username, stripeResponse);
}).then(function(username) {
response.success(username);
}, function(error) {
response.error(error);
});
});
function get_stripe_customer (customerId) {
Stripe.initialize(STRIPE_SECRET_KEY);
return Stripe.Customers.retrieve(
customerId, {
success:function(results) {
// console.log(results["email"]);
},
error:function(error) {
}
}
);
};
function set_user_status(stripeResponse) {
Parse.Cloud.useMasterKey();
var emailquery = new Parse.Query(Parse.User);
emailquery.equalTo("username", stripeResponse['email']); // find all the women
return emailquery.first({
success: function(results) {
alert('running set_user_status success');
var user = results;
user.set("tier", "paid");
user.save();
},
error:function(error) {
console.log('error finding user');
}
});
};
open to improvements...
EDIT - I (#danh) cleaned it up a bit. A few notes:
used promises throughout. much easier to read and handle errors
get_stripe_customer requires only one param (that 100 was my idea to charge $100)
set_user_status appears to need only user email as param, which apparently is in the stripeResponse
set_user_status returns a promise to save the user. that will be fulfilled with the user object, not the username
be sure you're clear on how to identify the user. stripe apparently provides email address, but in your user query (in set_user_status) you compare email to "username". some systems set username == email. make sure yours does or change that query.
Parse.Cloud.define("update_user", function(request, response) {
var data = request.params["data"]
var customerId = data.object.customer;
get_stripe_customer(customerId).then(function(stripeResponse) {
var email = stripeResponse.email;
return set_user_status(email);
}).then(function(user) {
response.success(user);
}, function(error) {
response.error(error);
});
});
function get_stripe_customer(customerId) {
Stripe.initialize(STRIPE_SECRET_KEY);
return Stripe.Customers.retrieve(customerId).then(function(results) {
// console.log(results["email"]);
return results;
});
};
function set_user_status(email) {
Parse.Cloud.useMasterKey();
var emailquery = new Parse.Query(Parse.User);
emailquery.equalTo("username", email); // find all the women
return emailquery.first().then(function(user) {
user.set("tier", "paid");
return user.save();
}, function(error) {
console.log('error finding user ' + error.message);
return error;
});
}
Did a quick skim of the docs pertaining to stripe, and it looks like the steps are: (1) make a stripe REST-api call from your client side to get a token, (2) pass that token to a cloud function, (3) call stripe from the parse cloud to finish paying. I understand that you'd like to include a (4) fourth step wherein the transaction is recorded in the data for the paying user.
From the client (assuming a JS client):
var token = // we've retrieved this from Stripe's REST api
Parse.Cloud.run("pay", { stripeToken: token }).then(function(result) {
// success
}, function(error) {
// error
});
On the server:
Parse.Cloud.define("pay", function(request, response) {
var user = request.user;
var stripeToken = request.params.stripeToken;
payStripeWithToken(stripeToken, 100).then(function(stripeResponse) {
return updateUserWithStripeResult(user, stripeResponse);
}).then(function(user) {
response.success(user);
}, function(error) {
response.error(error);
});
});
Now we need only to build promise-returning functions called payStripeWithToken and updateUserWithStripeResult.
// return a promise to pay stripe per their api
function payStripeWithToken(stripeToken, dollarAmt) {
Stripe.initialize(STRIPE_SECRET_KEY); // didn't see this in the docs, borrowed from your code
return Stripe.Charges.create({
amount: dollarAmt * 10, // expressed in cents
currency: "usd",
card: stripeToken //the token id should be sent from the client
});
// caller does the success/error handling
}
// return a promise to update user with stripeResponse
function updateUserWithStripeResult(user, stripeResponse) {
var transactionId = // dig this out of the stripeResponse if you need it
user.set("paid", true);
user.set("transactionId", transactionId);
return user.save();
}
Related
So currently the tweet bot is;
var fs = require('fs'),
path = require('path'),
Twit = require('twit'),
config = require(path.join(__dirname, 'config.js'));
var T = new Twit(config);
function pick_random_countermagic(){
var countermagic = [
'Force-of-Will.jpg',
'Cryptic-Commad.jpg',
'Counterspell.jpg',
];
return countermagic[Math.floor(Math.random() * countermagic.length)];
}
function upload_random_image(){
console.log('Opening an image...');
var image_path = path.join(__dirname, '/countermagic/' + pick_random_countermagic()),
b64content = fs.readFileSync(image_path, { encoding: 'base64' });
console.log('Uploading an image...');
T.post('media/upload', { media_data: b64content }, function (err, data, response) {
if (err){
console.log('ERROR');
console.log(err);
}
else{
console.log('Uploaded an image!');
T.post('statuses/update', {
media_ids: new Array(data.media_id_string)
},
function(err, data, response) {
if (err){
console.log('Error!');
console.log(err);
}
else{
console.log('Posted an image!');
}
}
);
}
});
}
setInterval(
upload_random_image,
10000
);
All it does currently posts, at random an image, which is what I want, but instead of just posting it I would like it to post in reply to a targeted tweet from another twitter when that bot tweets, and or when the bot is active reply to all tweets that bot has tweeted that my bot has not already replied to... if you know what I mean.
This is my first time making a twitter bot, and technically my first time really using javascript and node.js. So yeah, I am just lost. So yeah any help would be insurmountably helpful.
You can retrieve mentions using statuses/mentions_timeline API and reply them using statuses/update API by passing mention's id in in_reply_to_status_id. Here is a simple example:
function replyMentions() {
// check for mentions
// you can add parameter 'since_id' to limit returns
T.get('statuses/mentions_timeline', { count: 100 }, function(err, data, response) {
if (data) {
data.forEach(function(mention) {
// reply if mention.id_str is not yet replied
reply = '#' + mention.user.screen_name + ' thanks for reaching out!'
T.post('statuses/update', { status: reply, in_reply_to_status_id: mention.id_str }, function(err, data, response) {
console.log(data)
// mark data.id_str as replied
})
})
}
})
}
You can add some additional logic to store replied id_str in database and use them to avoid redundant replies and limiting mentions retrieval (using since_id parameter).
New to meteor and stripes API I am trying to apply this coupon code using Meteor and stripe.This is for a one time payment with a coupon. However the handleCharge method fires before the process payment method. And I want the Stripe.coupons.retrieve to return a result first before the payment is processed.
Server Method
Meteor.methods({
processPayment( charge, coupon ) {
Stripe.coupons.retrieve(
coupon,
function(err, result) {
if( result ) {
charge.amount = parseInt(charge.amount) - parseInt( charge.amount * coupon.percent_off );
}
}
);
let handleCharge = Meteor.wrapAsync( Stripe.charges.create, Stripe.charges ),
payment = handleCharge( charge );
return payment;
}
});
I've also tried to return a result before the coupon is passed into the processPayment. Then when i try to console.log the result it is always undefined.
checkForCoupon( couponCode ) {
let result = false;
Stripe.coupons.retrieve(
couponCode,
function(err, coupon) {
if( err ) {
result = false;
} else {
result = true;
}
}
);
return result;
}
Meteor.call( 'checkForCoupon', coupon, ( error, response ) => {
if ( error ) {
console.log( error );
} else {
console.log( "Success");
}
});
Any help would be greatly appreciated.
Ok here's a thing, the argument for coupon key in stripe api takes a string which looks like you already have because you are passing that in coupons.retrieve api, so what you'll get from this api is coupon object which is no use to you. So usually in Stripe we already have coupon id before creating subscription which gets passed in Stripe API for discount.
But as you said you are having problem in getting response before running another method, so here I can suggest you to use Async.runSync of meteor.
And another thing is that you can't use coupon in charge.create api, its for subscriptions. So here's how my approach would be with subscription:
Here I'm retreiving coupons object from coupon_id and and then hitting subscriptions API.
On Client:
var data = {
source: "source",
plan: "plan"
}
Meteor.call('processPayment', data, function(err, res) {
if(err)
return;
console.log("Subscription added with coupon");
});
On Server:
Meteor.methods({
var coupon;
'processPayment': function(data) {
check( data, {
source: String,
plan: String
});
Async.runSync(function(done) {
var coupon = Stripe.coupons.retrieve("coupon_id", function(err, coupon) {
if(err) {
return done(null); //done is callback
}
return done(null, coupon);
});
});
//this code will only run when coupon async block is completed
if(coupon !== null) {
data.coupon = coupon.id;
}
var subscription = Async.runSync(function(done) {
Stripe.subscriptions.create(data, function(err, subscription) {
if(err) {
console.log(err);
return done(null, err);
}
return done(null, subscription);
}
);
});
return subscription;
}
});
Hope this helps you and feel free to ask anything in comments, I'll be more than happy to answer.
I've written up a Parse Cloud script in JS which gets called in my Objective-C app. When the script gets called I get an error with the code: 141.
Here is the code, it gets called when a user accepts a friend request from another user (I'm also not entirely familiar with JS either):
Parse.Cloud.define("addFriendToFriendsRelation", function(request, response) {
Parse.Cloud.useMasterKey();
var friendRequestId = request.params.friendRequest;
var query = new Parse.Query("FriendRequest");
//get the friend request object
query.get(friendRequestId, {
success: function(friendRequest) {
//get the user the request was from
var fromUser = friendRequest.get("from");
//get the user the request is to
var toUser = friendRequest.get("to");
var relation = fromUser.relation("friends");
//add the user the request was to (the accepting user) to the fromUsers friends
relation.add(toUser);
//save the fromUser
fromUser.save(null, {
success: function() {
//saved the user, now edit the request status and save it
friendRequest.set("status", "accepted");
friendRequest.save(null, {
success: function() {
response.success("saved relation and updated friendRequest");
},
error: function(error) {
response.error(error);
}
});
},
error: function(error) {
response.error(error);
}
});
},
error: function(error) {
response.error(error);
}
});
});
All help and suggestions are greatly appreciated. Thank you.
EDIT: In my code I am calling:
[PFCloud callFunctionInBackground:#"addFriendToFriendsRelation" withParameters:#{#"friendRequest" : friendRequest.objectId} block:^(id object, NSError *error) {
EDIT2: The error I am getting in the JS script is the last
error: function(error) {
response.error(error);
}
to be called.
This will solve your problem , all you have to do is
add
useMasterKey: true
in the find method.
change your code to this
Parse.Cloud.define("addFriendToFriendsRelation", function(request, response) {
Parse.Cloud.useMasterKey();
var friendRequestId = request.params.friendRequest;
var query = new Parse.Query("FriendRequest");
//get the friend request object
query.get(friendRequestId, {
useMasterKey: true, success: function(friendRequest) {
//get the user the request was from
var fromUser = friendRequest.get("from");
//get the user the request is to
var toUser = friendRequest.get("to");
var relation = fromUser.relation("friends");
//add the user the request was to (the accepting user) to the fromUsers friends
relation.add(toUser);
//save the fromUser
fromUser.save(null, {
success: function() {
//saved the user, now edit the request status and save it
friendRequest.set("status", "accepted");
friendRequest.save(null, {
success: function() {
response.success("saved relation and updated friendRequest");
},
error: function(error) {
response.error(error);
}
});
},
error: function(error) {
response.error(error);
}
});
},
error: function(error) {
response.error(error);
}
});
});
Any ideas why this isn't working? It comes back with the success message but doesn't actually update anything.
I'm adding an FB user using Parse but since it doesn't have a func to add the username and email trying to do it this way. Any help much appreciated.
JS
Parse.Cloud.run("test", { objectId: "Q8XRUcL22N", name: "Rich", email: "rich#gmail.com"}, {
success: function(results) {
console.log(results);
},
error: function(error) {
console.log(error);
}
});
CLOUD CODE:
Parse.Cloud.define("test", function (request, response) {
Parse.Cloud.useMasterKey();
var uid;
var query = new Parse.Query("_User");
query.count({
success: function (results) {
uid = parseInt(results);
},
error: function () {
response.error("UID lookup failed");
}
});
var query = new Parse.Query(Parse.User);
query.equalTo("objectId", request.params.objectId);
query.first({
success: function (object) {
object.set("username", request.params.name);
object.set("email", request.params.email);
object.set("uid", uid);
var acl = new Parse.ACL();
acl.setPublicWriteAccess(false);
acl.setPublicReadAccess(false);
object.setACL(acl);
object.save();
response.success("Success Message");
},
error: function (error) {
response.error("Error Message");
}
});
});
Thanks
Calling success() or error() on the response halts whatever is underway at the time, including the save() on the user.
Also, it looks like you want to record in the user a count of users at the time the ACL is set. Getting the count must also be serialized with the other operations. (Also, please note that count is only good as an id "uid" to the extent the user count never goes down. What's wrong with the parse objects's id as an id?).
Most of the parse functions return promises, and using them is the only way to not go nuts trying to nest callbacks. So...
Parse.Cloud.define("test", function(request, response) {
Parse.Cloud.useMasterKey();
var uid;
var query = new Parse.Query(Parse.User);
query.count().then(function(count) {
uid = count;
return query.get(request.params.objectId);
}).then(function(object) {
object.set("username", request.params.name);
object.set("email", request.params.email);
object.set("uid", uid);
var acl = new Parse.ACL();
acl.setPublicWriteAccess(false);
acl.setPublicReadAccess(false);
object.setACL(acl);
return object.save();
}).then(function (object) {
response.success(object);
},function(error) {
response.error("Error Message");
});
});
I have an Angular service that takes in a roleId and userId and assigns the user to that role and make a pointer in User to that role.
app.service('CRUD', function () {
this.addUserToRole = function (roleId, userId) {
// first we have to find the role we're adding to
var query = new Parse.Query(Parse.Role);
return query.get(roleId, {
success: function (role) {
// then add the user to it
var Database = Parse.Object.extend("User");
var query = new Parse.Query(Database);
console.log(role);
return query.get(userId, {
success: function (user) {
console.log(user);
role.getUsers().add(user);
role.save();
// now we need to tell the user that he has this role
console.log(user);
user.attributes.role.add(role);
user.save();
return user;
},
error: function (err) {
return err;
}
});
},
error: function (err) {
console.log(err);
}
});
}
});
I'm getting {"code":206,"error":"Parse::UserCannotBeAlteredWithoutSessionError"} on user.save();
After some research, I arrived at this website. He uses this code snippet as a JS SDK example:
Parse.Cloud.run('modifyUser', { username: 'userA' }, {
success: function(status) {
// the user was updated successfully
},
error: function(error) {
// error
}
});
and mentions something about a useMasterKey() function.
I'm still unsure how to fix this error.
Add
Parse.Cloud.useMasterKey();
at the beginning of your function.
Set it up as a background job. That is the code snip you found I think and a simpler far more secure means of fondling users and roles
https://parse.com/docs/cloud_code_guide#jobs