I'm going through this tutorial to collect digit input: https://www.twilio.com/docs/voice/tutorials/how-to-gather-user-input-via-keypad-node-js
The request.body is undefined so I can't seem to find the input. When I try request.params it is empty though in my Twilio dash board I can see the params are in the Post request from my gather action.
const fs = require('fs');
const botSpeak = require('./bot_speak/scripts.json');
const VoiceResponse = require('twilio').twiml.VoiceResponse;
const app = express();
// Returns TwiML which prompts the caller to record a message
app.post('/welcome', (request, response) => {
// Use the Twilio Node.js SDK to build an XML response
const twiml = new VoiceResponse();
//read scipts from .json
const gather = twiml.gather({
numDigits: 1,
action: '/gather'
});
gather.say(botSpeak.hello + botSpeak.continue);
//if no response
twiml.say(botSpeak.bye);
// Render the response as XML in reply to the webhook request
response.type('text/xml');
response.send(twiml.toString());
});
app.post('/gather', (request, response) => {
// Use the Twilio Node.js SDK to build an XML response
const twiml = new VoiceResponse();
// If the user entered digits, process their request
console.log(request.body);
if (request.body.Digits) {
switch (request.body.Digits) {
case '1':
twiml.say('You selected sales. Good for you!');
break;
case '2':
twiml.say('You need support. We will help!');
break;
default:
twiml.say("Sorry, I don't understand that choice.").pause();
twiml.redirect('/welcome');
break;
}
} else {
// If no input was sent, redirect to the /voice route
twiml.redirect('/welcome');
}
// Render the response as XML in reply to the webhook request
response.type('text/xml');
response.send(twiml.toString());
});
// Create an HTTP server and listen for requests on port 3000
app.listen(3000);
console.log('Server serving on port 3000');
You need body-parser middleware. Try adding this.
// Body Parser Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
https://expressjs.com/en/4x/api.html#express-json-middleware
Related
I want to have a Node web server serving pages and also set as an endpoint listening to webhooks. The example for the first comes from Rocket Rides, with the relevant code being:
const express = require('express');
// ...
const app = express();
// ...
// CRUD routes for the pilot signup and dashboard
app.use('/pilots', require('./routes/pilots/pilots'));
app.use('/pilots/stripe', require('./routes/pilots/stripe'));
// ...
// Index page for Rocket Rides
app.get('/', (req, res) => {
res.render('index');
});
// ...
// Start the server on the correct port
const server = app.listen(process.env.PORT || config.port, () => {
console.log('🚀 Rocket Rides server started:', config.publicDomain);
});
For the second, I use this tutorial with the following relevant code:
// Match the raw body to content type application/json
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => {
console.log("called!");
let event;
try {
event = JSON.parse(request.body);
} catch (err) {
response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntentSucceeded = event.data.object;
break;
case 'payment_method.attached':
const paymentMethod = event.data.object;
break;
// ... handle other event types
default:
// Unexpected event type
return response.status(400).end();
}
// Return a response to acknowledge receipt of the event
response.json({received: true});
});
app.listen(8000, () => console.log('Webhooks running on port 8000'));
With both parts, the server does not handle the webhook request:
Webhooks running on port 8000
POST /webhook 404 590.525 ms - 1415
and the sender receives a 404.
When I comment out most of the code in the first part, the webhook request is handled properly:
Webhooks running on port 8000
called!
and the sender receives a 200.
I believe one of the routes from the web server is masking the route for the endpoint. I tried looking for one with this thread:
app._router.stack.forEach(function(middleware){
if(middleware.route){ // routes registered directly on the app
routes.push(middleware.route);
} else if(middleware.name === 'router'){ // router middleware
middleware.handle.stack.forEach(function(handler){
route = handler.route;
route && routes.push(route);
});
}
});
console.log(routes);
and the only relevant one was GET /.
If I include the code for the endpoint before the code for the router, the webhook is handled properly.
How can I find which route is masking the webhook endpoint?
Put the more specific route definitions first like this:
app.use('/pilots/stripe', require('./routes/pilots/stripe'));
app.use('/pilots', require('./routes/pilots/pilots'));
And, the more general route definitions later. That makes sure the more specific routes aren't gobbled up by the more general handlers.
Keep in mind that with app.use(), something like app.use('/pilots') will match any route that starts with /pilots which would include all your /pilots/stripe routes. So, you want to make sure and put the app.use('/pilots/stripe', ...) before the app.use('/pilots', ...).
Another observation. In your /webhook handler, you need to return after you send an error status so the rest of your request handler doesn't continue to run.
// Match the raw body to content type application/json
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => {
console.log("called!");
let event;
try {
event = JSON.parse(request.body);
} catch (err) {
response.status(400).send(`Webhook Error: ${err.message}`);
return; // <===== Add this
}
....
}
This appears to be a bug in the actual stripe documentation.
If I include the code for the endpoint before the code for the router, the webhook is handled properly.
I would guess that you have bodyparser middleware elsewhere in your server. If that middleware is BEFORE this route, then this route won't get to use its bodyParser.raw() and get the data the way it wants and it will not work properly. This is because whichever bodyParser middleware runs first reads the body and parses it and puts it wherever that middleware is configured to put it. Once the body is read, it's gone from the stream so any other middleware that comes along and also tries to read the body data from the stream will find the stream empty.
So, this route just has to be before any other body parsing middleware that might handle JSON.
If you provided a link to your full code, we could take a look and see where this is happening.
I am using the code provide by Stripe to test a webhook. The Stripe secret and the endpoint secret have been triple checked.
Stripe version: 6.19
Body-Parser: 1.19
When I test webhook on the Stripe dashboard I get the result: (Test webhook error: 400) No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?
Any help would be appreciated.
var bodyParser - require('body-parser');
// Using Express
const app = require('express')();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
const stripe = require('stripe')('sk_test_VPw...');
// Find your endpoint's secret in your Dashboard's webhook settings
const endpointSecret = 'whsec_...';
// Use body-parser to retrieve the raw body as a buffer
const bodyParser = require('body-parser');
// Match the raw body to content type application/json
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret); //NOT WORKING!
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the checkout.session.completed event
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
// Fulfill the purchase...
handleCheckoutSession(session);
}
// Return a response to acknowledge receipt of the event
response.json({received: true});
});
Usually this is due to something on your side parsing or modifying the raw request string before the signature is checked(so the signature is computed against a modified string, not the exact one Stripe sent). In this case it looks like the JSON express middleware is doing that:
app.use(express.json());.
Stripe has an example of using a raw bodyParser middleware on the webhook endpoint instead so that your code gets the raw string that's required :
// Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json()(req, res, next);
}
});
// Stripe requires the raw body to construct the event
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
} catch (err) {
// On error, log and return the error message
console.log(`❌ Error message: ${err.message}`);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Successfully constructed event
console.log('✅ Success:', event.id);
// Return a response to acknowledge receipt of the event
res.json({received: true});
});
One liner plus no deprecated bodyParser. Make sure to define your endpoint's parser before the generic one, aka express.json().
app.use('/stripe/webhook', express.raw({type: "*/*"}))
app.use(express.json())
In addition to everything, check whsec_
How to get both parsed body and raw body in Express:
app.use(bodyParser.json({
verify: (req, res, buf) => {
req.rawBody = buf
}
}))
Thanks to:
https://flaviocopes.com/express-get-raw-body/
For those working with NextJS. Here is a solution I bumped on Reddit by one #
u/SiMFiCysed
https://www.reddit.com/user/SiMFiCysed/
One other thing that could be going wrong (which was giving me the error) is that the production webhook key is being used with a test request, or vice versa.
I am a beginner to Twilio. I understand that I can get the status of the SMS send by having a statusCallback, so that the POST will happen to the callbackurl with the status of the message. But I am having troubles in creating that particular POST endpoint.
Here is what I have :
// Twilio API CALL
client.sendMessage({
to:userId,
from: metadata.myTwilioNumber,
body: message,
StatusCallback:'POST URL'
}, function(err, responseData) {
if (!err) {
} else {
logger.info(err);
}
My POST endpoint is a simple node js (request, response) endpoint.
var server = http.createServer ( function(request,response){
response.writeHead(200,{"Content-Type":"text\plain"});
if(request.method == "GET")
{
response.end("received GET request.")
}
else if(request.method == "POST")
{
console.log(request.CallStatus);
console.log(response);
console.log('receivedRequest');
response.end("received POST request.");
}
else
{
response.end("Undefined request .");
}
});
server.listen(4445);
Can someone help me in getting the status and messageID of the response? Currently, the POST is getting invoked but i am unable to get the message details and status.
Twilio developer evangelist here.
You are using the basic Node.js standard library http module which is going to make it a lot of work to extract the information from the POST request. Can I recommend you try something like express with body-parser instead.
You can do so by installing the two modules with npm:
npm install express body-parser
Then you can rewrite your incoming message application like this:
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.post('/', (req, res) => {
console.log(req.body.CallStatus);
res.sendStatus(200);
});
app.listen(4445, () => {
console.log('Application running on localhost:4445');
});
All the parameters that Twilio sends will be available on the req.body object.
Check out this tutorial on receiving SMS messages with Twilio in Node for a bit more detail.
I'm currently trying to add chatbot to my website.
I'm integrating web demo on the agent used for our lIne bot.
which some responses are handled by lambda webhook.
what I'm asking is can we send responses to web demo using lambda?
if can, then how do I send them?
there suppose to be some id right? and do we use HTTP post request to send them?
I couldn't find an example.
and for some intent which has more than one response handled by dialogflow it can only send one of them.
why is that? and what should I do so that I can send all of them via dialogflow?
Yes, it can be achieved and you can refer to give NodeJs code for that,
const express = require("express");
const bodyParser = require("body-parser");
const apiai = require("apiai");
const request = require("request");
const app = express();
app.use(bodyParser.json());
app.set("port", process.env.PORT || 5000);
app.post("/", (req, res) => {
//console.log(req.body)
const action = req.body.result.action;
if (!req.body || !req.body.result || !req.body.result.parameters) {
return res.status(400).send("Bad Request");
}
console.log("--------------------------------");
console.log("Action =>", action);
console.log("--------------------------------");
switch (action) {
case "price.search":
const webhookReply = `Sorry NO book found in store.`;
res.status(200).json({
source: "webhook",
speech: webhookReply,
displayText: webhookReply
});
break;
default:
break;
}
});
app.listen(app.get("port"), function() {
console.log("* Webhook service is listening on port:" + app.get("port"));
});
For every intent, there will be an action of that we have to define in dialogFlow.
So when the user enters any query your webhook will get triggered it will
go in the switch case to find the particular action and form that case you can send back the replay to your bot.
I'm trying to run the sillyNameMaker example from actions-on-google with api.ai, on my computer.
I set up a nodejs server with express, and a ngrok tunneling. When I try to send a request with my agent on api.ai, my server receives the POST request, but the body appears to be empty. Is there anything i didn't set up properly?
Here is my index.js file:
'use strict';
var express = require('express')
var app = express()
const ApiAiAssistant = require('actions-on-google').ApiAiAssistant;
function sillyNameMaker(req, res) {
const assistant = new ApiAiAssistant({request: req, response: res});
// Create functions to handle requests here
const WELCOME_INTENT = 'input.welcome'; // the action name from the API.AI intent
const NUMBER_INTENT = 'input.number'; // the action name from the API.AI intent
const NUMBER_ARGUMENT = 'input.mynum'; // the action name from the API.AI intent
function welcomeIntent (assistant) {
assistant.ask('Welcome to action snippets! Say a number.');
}
function numberIntent (assistant) {
let number = assistant.getArgument(NUMBER_ARGUMENT);
assistant.tell('You said ' + number);
}
let actionMap = new Map();
actionMap.set(WELCOME_INTENT, welcomeIntent);
actionMap.set(NUMBER_INTENT, numberIntent);
assistant.handleRequest(actionMap);
function responseHandler (assistant) {
console.log("okok")
// intent contains the name of the intent you defined in the Actions area of API.AI
let intent = assistant.getIntent();
switch (intent) {
case WELCOME_INTENT:
assistant.ask('Welcome! Say a number.');
break;
case NUMBER_INTENT:
let number = assistant.getArgument(NUMBER_ARGUMENT);
assistant.tell('You said ' + number);
break;
}
}
// you can add the function name instead of an action map
assistant.handleRequest(responseHandler);
}
app.post('/google', function (req, res) {
console.log(req.body);
sillyNameMaker(req, res);
})
app.get('/', function (req, res) {
res.send("Server is up and running.")
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
And the error I got:
TypeError: Cannot read property 'originalRequest' of undefined
at new ApiAiAssistant (/Users/clementjoudet/Desktop/Dev/google-home/node_modules/actions-on-google/api-ai-assistant.js:67:19)
at sillyNameMaker (/Users/clementjoudet/Desktop/Dev/google-home/main.js:8:21)
I'm trying to print req.body but it is undefined... Thanks in advance for your help.
Both you and the actions-on-google package are making an assumption about how you're using Express. By default, Express does not populate the req.body attribute (see the reference for req.body). Instead it relies on additional middleware such as body-parser to do so.
You should be able to add body parser to your project with
npm install body-parser
and then use it to parse the request body into JSON (which API.AI sends and actions-on-google uses) with some additional lines right after you define app to attach it to Express:
var bodyParser = require('body-parser');
app.use(bodyParser.json());