Stripe webook signature verification failed using express - stripe-payments

Hey stack overflow so I am using webooks with stripe. For some reason I am getting the error "Stripe webook signature verification failed". Here is my source code below. Any ideas on how I can get it working? I know it has something to do with the bodyparser. But I am not able to figure out a workaround to get the stripe webhooks working.
Error Message:
Webhook signature verification failed. No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing
require("dotenv").config();
const express = require("express");
var cors = require('cors')
const axios = require("axios");
const bodyParser = require("body-parser");
const Stripe = require('stripe');
const stripe = Stripe('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
const invoiceDescription = require('./constants');
const endpointSecret = "whsec_w5Qipi3Wz0fm8sSCHcJIHwWfobS0kfYe";
const { TOKEN, SERVER_URL, BOTTOKEN } = process.env;
const TELEGRAM_API = `https://api.telegram.org/bot${TOKEN}`;
const URI = `/webhook/${TOKEN}`;
const WEBHOOK_URL = SERVER_URL + URI;
const app = express();
app.use(cors());
app.use(bodyParser.json());
const init = async () => {
const res = await axios.get(`${TELEGRAM_API}/setWebhook?url=${WEBHOOK_URL}`);
console.log(res.data);
};
app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => {
let event = request.body;
// Only verify the event if you have an endpoint secret defined.
// Otherwise use the basic event deserialized with JSON.parse
if (endpointSecret) {
// Get the signature sent by Stripe
console.log(request.headers)
const signature = request.headers['stripe-signature'];
try {
event = stripe.webhooks.constructEvent(
request.body,
signature,
endpointSecret
);
} catch (err) {
console.log(`⚠️ Webhook signature verification failed.`, err.message);
return response.sendStatus(400);
}
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`);
// Then define and call a method to handle the successful payment intent.
// handlePaymentIntentSucceeded(paymentIntent);
console.log('payment intent is', paymentIntent)
break;
case 'payment_method.attached':
const paymentMethod = event.data.object;
// Then define and call a method to handle the successful attachment of a PaymentMethod.
// handlePaymentMethodAttached(paymentMethod);
console.log('payment method is', paymentMethod)
break;
default:
// Unexpected event type
console.log(`Unhandled event type ${event.type}.`);
}
// Return a 200 response to acknowledge receipt of the event
response.send();
});
app.post(URI, async (req, res) => {
let text = "", chatId = "", userObjectForTable = {};
if(req.body.message?.chat?.id && req.body.message?.text && req.body.message?.text === "Start"){
chatId = req.body.message.chat.id;
text = invoiceDescription;
const message = await axios.post(`${TELEGRAM_API}/sendMessage`, {
chat_id: chatId,
text: text,
reply_markup: {
inline_keyboard: [[{
text: 'Pay $65.00',
web_app: {url: 'https://buy.stripe.com/test_14kbKj3Gd0AGeRi7ss' }
}]]
}
});
}
return res.send();
});
app.listen(process.env.PORT || 5050, async () => {
console.log("🚀 app running on port", process.env.PORT || 5050);
await init();
});

Related

Force MicroService to await until RabbitMQ publishes a message , using Cote

I have a an API-GATEWAY MicroService that manages the entire show , and one of the services is called QueueService.
Here is the API-Gateway
const express = require('express')
const bodyParser = require('body-parser')
const cote = require('cote')
const cors = require('cors');
const app = express()
app.use(bodyParser.json())
app.use(cors());
app.post('/queue-msg', async (req, res, next) => {
let { from, to } = req.body;
const payload = {
from, to
}
const queue = await queueRequester.send({ type: 'create-queue-message', payload })
res.status(200).send({ "message-sent": true, queue });
});
This is thq QueueService :
const cote = require('cote')
const queueProducer = require('./utils/queueProducer');
const queueName = 'pathfinderqueue';
const queueResponder = new cote.Responder({ name: 'queue responder', key: 'deliveries' })
queueResponder.on('*', req => req.type && console.log(req))
const deliveries = []
let idCounter = 0
queueResponder.on('create-queue-message', req => {
async function makeRequest() {
console.log('Got a message from API-GATEWAY!');
let { payload } = req; // get From & To
queueProducer.publishToQueue(queueName, payload); // Publish to Rabbit
const queue = { id: idCounter++, status: 'pending' } // Set the ID
deliveries.push(queue)
return Promise.resolve(queue)
}
const response = makeRequest();
return response;
})
However every time that API-Gateway publishes a message from the React client to the QueueService , the QueueService doesn't awaits in the on event , and sends back a
"message-sent": true
to the client.
How can we force QueueService to await ?

How to validate the shopify webhook api using nodejs

I cannot able to validate the webhook response from the shopify by using the "shopify-node-api". and i am using the following code to validate the signature.
Below code is on app.js
app.use(bodyParser.json({
type:'application/json',
limit: '50mb',
verify: function(req, res, buf, encoding) {
if (req.url.startsWith('/webhook')){
req.rawbody = buf;
}
}
})
);
app.use("/webhook", webhookRouter);
Below on webhook.router.js
router.post('/orders/create', verifyWebhook, async (req, res) => {
console.log('🎉 We got an order')
res.sendStatus(200)
});
Below for the verification function
function verifyWebhook(req, res, next) {
let hmac;
let data;
try {
hmac = req.get("X-Shopify-Hmac-SHA256");
data = req.rawbody;
} catch (e) {
console.log(`Webhook request failed from: ${req.get("X-Shopify-Shop-Domain")}`);
res.sendStatus(200);
}
if (verifyHmac(JSON.stringify(data), hmac)) { // Problem Starting from Here
req.topic = req.get("X-Shopify-Topic");
req.shop = req.get("X-Shopify-Shop-Domain");
return next();
}
return res.sendStatus(200);
}
Verify signature function
function verifyHmac(data, hmac) {
if (!hmac) {
return false;
} else if (!data || typeof data.data !== "object") {
// I am Getting Error HERE
console.log('Error in data', data);
return false;
}
const sharedSecret = config.shopify_shared_secret;
const calculatedSignature = crypto
.createHmac("sha256", sharedSecret)
.update(Buffer.from(data), "utf8")
.digest("base64");
console.log('calculatedsecret', calculatedSignature);
return calculatedSignature === hmac;
};
and the body I am getting it as undefined. suggest me how to fix this problem in shopify webhook API
Instead of using the bodyparser.json() use bodyparser.raw to fetch the all the payload to process the shopify webhook verification.
router.use(bodyparser.raw({ type: "application/json" }));
// Webhooks
router.post("/", async (req, res) => {
console.log("Webhook heard!");
// Verify
const hmac = req.header("X-Shopify-Hmac-Sha256");
const topic = req.header("X-Shopify-Topic");
const shop = req.header("X-Shopify-Shop-Domain");
const verified = verifyWebhook(req.body, hmac);
if (!verified) {
console.log("Failed to verify the incoming request.");
res.status(401).send("Could not verify request.");
return;
}
const data = req.body.toString();
const payload = JSON.parse(data);
console.log(
`Verified webhook request. Shop: ${shop} Topic: ${topic} \n Payload: \n ${data}`
);
res.status(200).send("OK");
});
// Verify incoming webhook.
function verifyWebhook(payload, hmac) {
const message = payload.toString();
const genHash = crypto
.createHmac("sha256", process.env.API_SECRET)
.update(message)
.digest("base64");
console.log(genHash);
return genHash === hmac;
}

asynchronous code not working with event listener

*Edited for clarity and to reflect changes:
Hello. I'm struggling to understand why my async functions are working just fine individually, and when chained together, but not when chained together and fired off in an event listener....
worth noting: when i try to run this without an even listener, data passes from my app, through my post route, and to my endpoint the way it should, it is then returned to my app through the correct route, etc. However, i do get an error in the console that says :
error SyntaxError: Unexpected token < in JSON at position 0
however, when i try to run my chain of async functions on the click of a dom element i don't get the above error, data is passed to my post route, i can log the object that is posted:
Object: null prototype] { zipcode: '97206', feel: 'confused' }
but then my server doesn't save any data, and my get route is never triggered, and nothing gets sent back to my app.......
i'm fairly lost.....
full server and app code below:
server:
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
app.use(express.static('public'));
//test values
let projectData = {
date: "today",
temp: "38",
feelings: "confused"
};
app.get("/", (req, res) => {
});
app.post("/postData", (req, res) => {
console.log(req.body)
projectData = {
date: req.body.date,
temp: req.body.temp,
feelings: req.body.feelings
}
console.log("post route was hit, saved:", projectData);
res.redirect("/")
});
app.get("/getData", (req, res) => {
console.log("getData route was hit, sent: ", projectData)
res.send(projectData);
})
app.listen(3000, (req, res) => {
console.log("Listening on port 3000");
});
app
let newDate = getDate();
const apiKey = "5fd7b3a253e67a551e88ff34a92b9e02";
const baseURL = "http://api.openweathermap.org/data/2.5/weather?";
// zip=${}&APPID=${}&units=metric;
const userData = {};
// returns the date //
function getDate() {
let d = new Date();
let newDate = d.getMonth() + "." + d.getDate() + "." + d.getFullYear();
return newDate;
}
//constructs string for api call, adds temp to userData object
const getData = async (apiUrl, zip, key) => {
const url = `${apiUrl}zip=${zip}&APPID=${key}&units=metric`;
const result = await fetch(url);
try {
let newData = await result.json()
// console.log(newData.main.temp)
userData.temp = newData.main.temp
}catch(error) {
console.log("error", error);
}
}
// getData(baseURL, 97206, apiKey)
// .then(result => {
// console.log(userData)
// })
//saves contents of userData Object to server
const postData = async (url, data) => {
const result = await fetch(url, {
method: "POST",
credentials: "same-origin",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(data)
});
try {
const newData = await result.json();
// console.log(newData);
return newData;
} catch (error) {
console.log("error", error);
}
};
// postData('/postData', userData);
//updates interface with projectData values from server
const updateUI = async url => {
const result = await fetch(url);
try {
const newData = await result.json();
document.getElementById("date").innerHTML = newData.date;
document.getElementById("temp").innerHTML = newData.temp;
document.getElementById("feelings").innerHTML = newData.feelings;
} catch (error) {
console.log("error", error);
}
};
// updateUI("/getData")
// THIS WORKS
userData.date = newDate;
userData.feelings = document.getElementById("feel").value;
const zipCode = document.getElementById("zipcode").value;
getData(baseURL, 97206, apiKey).then(result => {
postData("/postData", userData).then(result => {
updateUI("/getData");
});
});
// THIS DOESNT WORK
// document.getElementById("btn").addEventListener("click", e => {
// userData.date = newDate;
// userData.feelings = document.getElementById("feel").value;
// const zipCode = document.getElementById("zipcode").value;
// getData(baseURL, zipCode, apiKey).then(result => {
// postData("/postData", userData).then(result => {
// updateUI("/getData");
// });
// });
// });
EDIT:
I realized that the information that was being passed through the post route when my async functions are fired off by an event listener was actually just the form input element values, rather than the contents of the fetch/post request. after i removed the name attributes from the form inputs, i'm getting no data at all hitting my post route......yet the corosponding function works fine with not in the event listener.
I'm stumped.
well, the syntax error was solved by using .text() instead of .json() on the results of my post request.
adding e.preventDefault() as the first line of my event listener callback fixed the event listener, and now everything is working as it should.

How to deal with the different type of errors that returns from the cloud function?

I have written the cloud functions which sends the response either with the statuscode 200 or 400 but sometimes I am getting this error
Function execution took 219 ms, finished with status: 'connection error'
Error: function crashed out of request scope Function invocation was interrupted.
So the problem is I need to know send this error message with some statuscode as the response of the cloud function.
const functions = require('firebase-functions');
const dialogflow = require('dialogflow');
const admin = require('firebase-admin');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
admin.initializeApp();
var db = admin.firestore();
const {WebhookClient} = require('dialogflow-fulfillment');
var UID = new Object();
exports.fulfillmenttext = functions.https.onRequest((req,res) =>{
const answer1 = req.body.Text;
const uid = answer1.substring(0,28);
const answer = answer1.substring(28);
const sessionId = uid;
var count,questvalue;
const promise = db.collection("***").doc('***').collection("**").doc("uid").get();
promise.then(doc => {
snapshot.forEach(function(doc) {
if (doc.exists) {
count = doc.data().count;
if(count == 1){
var updatequest = title[questvalue];
res.status(200).send({"question":updatequest,"question_number":questvalue});
return;
}
else{
runSample();
}
}
else {
console.log("No such document!");
}
});
}).catch(function(error) {
console.log("Error getting document:", error);
});
async function runSample() {
const languageCode = 'en-US';
const projectId = 'xxxxxxx';
const credentials = {
client_email: 'xxxxxxx',
private_key:
'xxxxxxx',
};
//Instantiate a DialogFlow client.
const dialogflow = require('dialogflow');
const sessionClient = new dialogflow.SessionsClient({
projectId,
credentials,
});
// Define session path
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
// The text query request.
const request = {
//session: context1,
session: sessionPath,
queryInput: {
text: {
text: answer,
languageCode,
},
},
};
const responses = await sessionClient.detectIntent(request);
const result = responses[0].queryResult;
let action = result.action;
if (result.intent) {
const question = result.fulfillmentText;
console.log("question is",question);
const actionHandlers = {
'early': () => {
console.log('earlyaction1', action);
let name1 = JSON.stringify(result.parameters.fields.Name.stringValue);
name1 = name1.toString().replace(/"/g,"");
var data1 = {
Name: name1
};
var setDoc1 = admin.firestore().collection('**').doc('uid').collection("***").doc('uid').collection('**').doc('**').update(data1);
},
};
if (action === 'early') {
console.log('1');
actionHandlers[action]();
}
res.status(200).send({"question":result.fulfillmentText,"action":action,"question_number":title.indexOf(question)});
} else {
console.log(` No intent matched.`);
res.status(400).send({"action":"empty"});
}
}
});
That message is telling you that you have some async code that was still running after the function terminated normally by sending a response to the client. That bit of async code crashed. Since the function already terminated by sending a response, there's nothing you can do to send another response. There can only be one response per function invocation.
What you'll have to do is review your code and make sure you're 1) handling promises correctly and 2) not intentionally trying to leave any work going after sending the response, since Cloud Functions does not support that.

Cloud Functions for Firebase Notification

I am trying to fire a notification using Cloud Functions for Firebase. I can get it to console log stating a message has been fired, but can't actually get the notification to work in the browser. Can anyone see a flaw?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.newMessageAlert = functions.database.ref('/messages/{message}').onWrite((event) => {
const message = event.data.val();
const getTokens = admin.database().ref('users').once('value').then((snapshot) => {
const tokens = [];
snapshot.forEach((user) => {
const token = user.child('token').val();
if (token) tokens.push(token);
});
return tokens;
});
const getAuthor = admin.auth().getUser(message.uid);
Promise.all([getTokens, getAuthor]).then(([tokens, author]) => {
const payload = {
notification: {
title: `Hot Take from ${author.displayName}`,
body: message.content,
icon: author.photoURL
}
};
admin.messaging().sendToDevice(tokens, payload).then((resp) =>{
console.log("IT WORKED", resp);
});
});
});

Resources