Stripe Connect Express Webhook - How to get Stripe webhook to fire once user has completed Stripe Connect Express form and is redirected - node.js

so I've spent a fair amount of time (weeks) with this issue however still have not been able to figure out how to resolve it. I believe I've narrowed it down to the issue of trying to get a Stripe Connect webhook to fire once a user has completed the signup form through the Stripe Connect Express process. Here's the example Stripe gives with their RocketRides.io demo. I've submitted an issue to stripe but am waiting to hear back. I was hoping to get there quicker with the help of the SO community.
I've previously created other questions as I chase the proverbial rabbits down their various holes as I go through the trial and error process of trying to narrow down the issue so bear with me. You can find these other links at the bottom of this question post.
The snippet below displays my Stripe CLI Log when I am "listening" with stripe listen.
This snippet displays the stripe logs tail
The customer.created is fired when a user signs up for my app, which is supposed to happen using the code below. I am not sure if this is supposed to be used when trying to use a Stripe Connect Account which is my intention. I'm still figuring out Stripe. The bottom line is I need to have payout and subscription capability, and from the research I've done, Stripe Connect is the direction to go.
exports.createStripeCustomerHTTPSCall = functions.firestore.document('Users/{userName}').onCreate((change, context) =>{
console.log("Stripe Customer profile for " + context.params.userName + " created.");
return stripeToken.customers.create(
{
description: 'My First Test Customer (created for API docs)',
email: context.params.userName,
},
(err, customer) => {
// asynchronously called
console.log("Creating Stripe customer failed because: " + err.toString())
}
)
Once a user signs up for the App and is logged in the FirebaseDatabase as a user, they will be directed to the Stripe Connect Express form.
The issue now I'm running into is getting stripe to fire a Connect Webhook which would trigger Step 4, a Cloud Functions version of a cURL request to the redirected URL with the Authentication code, and potentially a state code. The account.application.authorized event only occurs in my listener only after I have sent in a cURL request (I think is called a GET Request? I'm not a formally trained programmer)
I believe the issue lies in setting up a webhook to fire once a user has completed the form and is redirected which I think should be account.updated?
Here is my webhook setup:
Here is my Node.js code. I'm not sure if its relevant since I can't even get the webhook to fire but just in case:
exports.stripeCreateOathResponseToken = functions.https.onRequest((req, res) => {
console.log("rawbody: " + rawbody); // <-- usually comes back with req.rawbody
console.log("request.body: " + req.body); // <-- returns undefined
console.log("request.query.code: " + req.query.code); // <-- returns undefined
console.log("request.query.body: " + req.query.body); // <-- returns undefined
console.log("request.query.state: " + req.query.state); // <-- returns undefined
return stripeToken.oauth.token({
grant_type: 'authorization_code',
code: req.query.code,
}).then(function(response) {
// asynchronously called
return res.send(response)
// var connected_account_id = response.stripe_user_id;
})
.catch(error=> {
res.send(error)
})
});
In case its relevant, here is android kotlin code for when user decides to sign up for payouts capabilities. AKA Stripe Connect Express
openURL.data = Uri.parse(
"https://connect.stripe.com/express/oauth/authorize?" +
"redirect_uri=http://XXXXXXXXXXXXXX.com&" +
"client_id=ca_XXXXXXXXXXXXXXXXXXXX" + // how to hide client id? maybe look at how to access from stripe CLI
"state=" + mAuth.currentUser!!.uid +
"&stripe_user[email]=" + userEmail +
"&stripe_user[business_type]=individual" +
"&stripe_user[phone_number]=" + userPhoneNumberStripped +
"#/")
startActivity(openURL)
Previous Questions
Stripe Firebase Cloud Functions - res.send() is not a function
Cloud Firebase Stripe Connect - Retrieve Authorization Code / Access Token, State, etc
Firebase Cloud Functions - Stripe Connect Webhook not firing
Android Stripe Connect WebView - Create Account Form NOT loading

In this case a webhook won't work; you'll instead want to redirect the user - after they complete the OAuth process - to a Cloud Functions URL that runs the Node.js code you've supplied there (and then redirects them to whatever your next step is from there).
You'll also want to make sure that the URL you're redirecting to is listed as a redirect URL in your dashboard's Platform Settings, per here: https://stripe.com/docs/connect/express-accounts#integrating-oauth
In terms of redirecting in GCF, this might help: https://stackoverflow.com/a/45897350/379538

Related

How can I authorize to send a GET request from one GCLOUD function to another

Trying to hit a gcloud function's endpoint via GET to trigger the http function. All I need to do is hit the endpoint with some param values to trigger the function.
We are not allowing unauthenticated on these functions so I need to authenticate in order to send it but cannot for the life of my find a working example on how to do this.
I have read this and quite literally gone in circles following the links in their documentation trying to find what I need to do to set this up.
The function I am hitting sends a message when everything has updated for the day
https.get(`${endPoint}`, (resp) => {
// The whole response has been received. Print out the result.
resp.on('end', () => {
console.log(JSON.parse(data).explanation); // Should return the text set int he function, current, "It's Done"
res.status(200).send(`SQL INSERTs have all been run for client(${clientId}) and they have been notified`);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
res.status(200).send(`There was an error running SQL INSERTs for client(${clientId}) and they have not been notified, error ${err.message}`);
});
In the logs for the function I am trying to hit it returns
The request was not authenticated. Either allow unauthenticated invocations or set the proper Authorization header. Read more at https://cloud.google.com/run/docs/securing/authenticating Additional troubleshooting documentation can be found at: https://cloud.google.com/run/docs/troubleshooting#unauthorized-client
So I am specifically trying to figure out what I need to do, exactly, in order to authenticate and hit the endpoint via a GET request.
Since posting this question I have also created a service account and downloaded the credentials, which is set to GOOGLE_APPLICATION_CREDENTIALS, so if there is a solution using that JSON file I can try that as well.

how does users.watch (in gmail google api) listen for notifications?

I am confused as to how should the watch feature in the gmail API be implemented to recieve the push notificatons inside a node.js script. Should I call the method inside an infinite loop or something so that it doesn't stop listening for notifications for email once after the call is made?
Here's the sample code that I've written in node.js:
const getEmailNotification = () => {
return new Promise(async (resolve, reject) => {
try{
let auth = await authenticate();
const gmail = google.gmail({version: 'v1', auth});
await gmail.users.stop({
userId: '<email id>'
});
let watchResponse = await gmail.users.watch({
userId: '<email id>',
labelIds: ['INBOX'],
topicName: 'projects/<projectName>/topics/<topicName>'
})
return resolve(watchResponse);
} catch(err){
return reject(`Some error occurred`);
}
})
Thank you!
Summary
To receive push notifications through PUB/SUB you need to create a web-hook to receive them. What does this mean? You need a WEB application or any kind of service that exposes a URL where notifications can be received.
As stated in the Push subscription documentation:
The Pub/Sub server sends each message as an HTTPS request to the subscriber application at a pre-configured endpoint.
The endpoint acknowledges the message by returning an HTTP success status code. A non-success response indicates that the message should be resent.
Setup a channel for watch the notifications could be summarized in the following steps (the documentation you refer to indicates them):
Select/Create a project within the Google Cloud Console.
Create a new PUB/SUB topic
Create a subscription (PUSH) for that topic.
Add the necessary permissions, in this case add gmail-api-push#system.gserviceaccount.com as Pub/Sub Publisher.
Indicate what types of mail you want it to listen for via Users.watch() method (which is what you are doing in your script).
Example
I give you an example using Apps Script (it is an easy way to visualize it, but this could be achieved from any kind of WEB application, as you are using Node.js I suppose that you are familiarized with Express.js or related frameworks).
First I created a new Google Apps Script project, this will be my web-hook. Basically I want it to make a log of all HTTP/POST requests inside a Google Doc that I have previously created. For it I use the doPost() equal to app.post() in Express. If you want to know more about how Apps Script works, you can visit this link), but this is not the main topic.
Code.gs
const doPost = (e) => {
const doc = DocumentApp.openById(<DOC_ID>)
doc.getBody().appendParagraph(JSON.stringify(e, null, 2))
}
Later I made a new implementation as a Web App where I say that it is accessible by anyone, I write down the URL for later. This will be similar to deploying your Node.js application to the internet.
I select a project in the Cloud Console, as indicated in the Prerequisites of Cloud Pub/Sub.
Inside this project, I create a new topic that I call GmailAPIPush. After, click in Add Main (in the right bar of the Topics section ) and add gmail-api-push#system.gserviceaccount.com with the Pub/Sub Publisher role. This is a requirement that grants Gmail privileges to publish notification.
In the same project, I create a Subscription. I tell it to be of the Push type and add the URL of the Web App that I have previously created.
This is the most critical part and makes the difference of how you want your application to work. If you want to know which type of subscription best suits your needs (PUSH or PULL), you have a detailed documentation that will help you choose between these two types.
Finally we are left with the simplest part, configuring the Gmail account to send updates on the mailbox. I am going to do this from Apps Script, but it is exactly the same as with Node.
const watchUserGmail = () => {
const request = {
'labelIds': ['INBOX'],
'topicName': 'projects/my_project_name/topics/GmailAPIPush'
}
Gmail.Users.watch(request, 'me')
}
Once the function is executed, I send a test message, and voila, the notification appears in my document.
Returning to the case that you expose, I am going to try to explain it with a metaphor. Imagine you have a mailbox, and you are waiting for a very important letter. As you are nervous, you go every 5 minutes to check if the letter has arrived (similar to what you propose with setInterval), that makes that most of the times that you go to check your mailbox, there is nothing new. However, you train your dog to bark (push notification) every time the mailman comes, so you only go to check your mailbox when you know you have new letters.

Can I access twitter auth data via firebase cloud functions Admin SDK? If so, how?

I'm currently using firebase for the backend of a project I'm working on. In this project, the client authenticates using the firebase-twitter sign in method. For the purpose of security, I'm trying to minimise the amount of communication between the client and backend when it comes to auth data. In jest of this, I'm wondering if there is a way to access the auth data i.e. the user's twitter key/secret (as well as things like the user's twitter handle) from the server-side after the user authenticates ? I figured there might be a way as the authentication happens through twitter + firebase, but I'm struggling to find the exact solution I need in the documentation (been stuck on this for a week now) so was hoping someone else already knows if this is possible and how :) cheers
Maybe not the best way, but you can try: on client side use RealTime database and add a new entry every time the user log in. They call this 'realtime triggers'.
You don't mention what front are you using, but on ionic is something like:
firebase.auth().onAuthStateChanged(function(user) {
if (user)
this.db.addLogin(user.uid)
});
On database class function:
addLogin(uid){
let path = "/logins/"
let ref = this.db.list(path)
let body = {uid: uid}
return ref.push(body)
}
On the server side, listen the path using child_added
var ref = db.ref("logins");
ref.on("child_added", function(snapshot, prevChildKey) {
var newPost = snapshot.val();
console.log("Uid: " + newPost.uid);
console.log("Previous Post ID: " + prevChildKey);
});
More information about triggers

How to catch Facebook messaging_optins with Azure Bot Channels Registration + Botbuilder SDK?

I've got a chatbot up and running, built using Node.JS Microsoft Bot Framework, and deployed to an Azure server as a Web App, with a Bot Channels Registration resource as the frontend endpoint.
This Bot Channels Registration is connected to Facebook Messenger (via a FB App) - meaning, the webhook for the Facebook App points to https://facebook.botframework.com/api/v1/bots/<BOT_CHANNELS_REGISTRATION_RESOURCE_NAME>.
This all works well for normal chat functionality.
However, I'd now like to add an opt-in checkbox to a separate web page I have. This checkbox works by pinging FB, which then sends a very specific payload to the already configured bot webhook.
{
"recipient":{
"id":"<PAGE_ID>"
},
"timestamp":<UNIX_TIMESTAMP>,
"optin":{
"ref":"<PASS_THROUGH_PARAM>",
"user_ref":"<UNIQUE_REF_PARAM>"
}
}
My question is this:
How does the Bot Channels Registration receive and handle the above payload? Will it just automatically forward it to the Messaging Endpoint I have configured in the Bot Channels Registration settings? Or will it get stuck, and never reach my actual bot Web App?
Finally, if it does reach my normal messages endpoint, how can I handle the specific payload with my botbuilder.ChatConnector() listener? Given that my web app code looks like (in essence)
var restify = require('restify');
var builder = require('botbuilder');
var dialogues = require('./dialogues');
var chatbot = function (config) {
var bot = {};
chatbot.listen = function () {
var stateStorage = new builder.MemoryBotStorage();
var connector = new builder.ChatConnector({
appId: process.env.APP_ID,
appPassword: process.env.APP_PASSWORD
});
bot = new builder.UniversalBot(connector, function (session) {
session.beginDialog(dialogues.base(bot).name);
}).set('storage', stateStorage);
return connector.listen();
};
return chatbot;
}
var server = restify.createServer();
// Listen for messages from users
server.post('/api/messages', chatbot.listen());
server.listen(process.env.port, function () {
console.log('%s listening to %s', server.name, server.url);
});
Thanks!
EDIT: I've figured out how to handle the above payload within my messaging endpoint - by adding a server.pre() handler to my server, e.g.
server.pre(function (req, res, next) {
if (req.body && req.body.optin_payload_specific_field){
// handle opt-in payload
} else {
return next();
}
});
However, via extra logging lines, it seems the opt-in payload isn't even making it to this endpoint. It seems to be stopped within the Bot Channels Registration. Currently looking for a way to resolve that major roadblock.
So, per #JJ_Wailes investigation, it seems like this is not a supported feature (in fact, it's a current feature request). See his comments on the original post for more details.
However, I did find a half-workaround to capture the user_ref identifier generated by the checkbox_plugin, for those interested:
1) From your external site, follow the steps from the documentation here for sending the initial user_ref to FB. FB will then make a callout to your bot, but per the above, that gets blocked by the Bot Channels Registration piece, so it's ignored.
2) From that same external site, use the user_ref to send a message to the user (just using the normal requests library). A successful send means that the user_ref was properly registered in FB by that step #1 call - a failure means you'll need to repeat step #1 (or use some other error handling flow).
3) After that, the next time the user responds to your bot in FB (as long as you don't send any other messages to them), the message your bot will receive will contain this as part of the payload:
{ ...
channelData:
{ ...
prior_message:
{ source: 'checkbox_plugin',
identifier: <user_ref> }
}
}
So I've currently added a check within my bot.use(), where if that section is present in the incoming message payload (session.message.sourceEvent.prior_message) and the source is "checkbox_plugin", I store the corresponding user_ref in the session.userData, and can work from there.
I would love to see this feature added into the supported Azure bot stack, but in the meantime hopefully this helps anyone else encountering this (admittedly niche) hurdle.

Using PayPal REST api in Node.js

After reading their docs and github API page I understand that in order to complete a transaction I need
i) Create the payment using the paypal.payment.create(create_payment_json, function (error, payment) { .. }); function from their API and to provide a payment object. Get the redirect url and in some way provide it to the user in order to pay.
ii) Execute the payment paypal.payment.execute(paymentId, execute_payment_json, function (error, payment) { .. }); (thats where my issue is, more on that later)
iii) And lastly, authorize the payment.
I understand the first part and I have no issue either with the PayPal response or using it. My issue is the second function. Since I'd like to send the URL to the user and I don't have a button there, I cant understand how to wait for the execute response. Suppose I send the URL to the user and the user pays, how can I wait for the response to run the execute function afterward.
Should I create a server (with no actual website) listening to a random port?

Resources