Problem with validating stripe webhook in NextJS - javascript

I am following the stripe docs about validating webhooks and despite I do everything as they do i keep getting 400 error. BTW in the docs here https://stripe.com/docs/webhooks/signatures they don't return from the catch blok but here https://stripe.com/docs/webhooks/quickstart they do, so I assyme that the correct option is to return from it? And back to my main problem I have no idea what am I missing here this is my code:
import { NextApiHandler } from "next";
import { Stripe } from "stripe";
import { apolloClient } from "../../graphql/apolloClient";
import {
UpdateOrderDocument,
UpdateOrderMutation,
UpdateOrderMutationVariables,
} from "../../generated/graphql";
import type { StripeWebhookEvents } from "../../stripeEvents";
const stripeWebhookHandler: NextApiHandler = (req, res) => {
const webhookSignature = req.headers["stripe-signature"];
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
const stripeSecret = process.env.STRIPE_SECRET_KEY;
if (!stripeSecret || !webhookSignature || !webhookSecret) {
return res.status(500).json({ error: "Stripe credential not provided" });
}
const stripe = new Stripe(stripeSecret, { apiVersion: "2020-08-27" });
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
webhookSignature,
webhookSecret
) as StripeWebhookEvents;
} catch (err: unknown) {
return res
.status(400)
.send(`Webhook Error: ${err instanceof Error && err.message}`);
}
switch (event.type) {
case "charge.succeeded":
apolloClient.mutate<UpdateOrderMutation, UpdateOrderMutationVariables>({
mutation: UpdateOrderDocument,
variables: {
id: {
id: event.data.object.metadata.cartId,
},
data: {
stripeCheckoutId: event.data.object.id,
email: event.data.object.receipt_email,
},
},
});
break;
}
res.status(204).end();
};
export default stripeWebhookHandler;
and I thought that maybe next has maybe different shape of req.headers or req.body and I am not sure abouut req body, headers seem to be in tact however. At least the signature seems to be extracted correctly. Dhose are test data of course:
{
webhookSignature: 't=1658224240,v1=a3f574b3e6c3a02eb86308e5e43f3d0a96664098ee5dd58859fc94e96693fc50,v0=ef29de87716d9d318d6ad960f028fd5960618c853ff686bd44e261aaa2368f3b',
webhookSecret: 'whsec_0d8a54d09bf221f7c5c77ca7a3fca4b988ccd9e49d8a31d7c91f854025503fe4',
stripeSecret: 'sk_test_51Kuvp4KsMpogemXo9vUcgihi1vK4dlof76OL4EcYhmVgN8r81tl7r0rSsqWgOtXxYnZPJlo6S2KA0gFWZmyBQIbS00ABzicwum',
headers: {
host: 'localhost:3000',
'user-agent': 'Stripe/1.0 (+https://stripe.com/docs/webhooks)',
'content-length': '2818',
accept: '*/*; q=0.5, application/xml',
'cache-control': 'no-cache',
'content-type': 'application/json; charset=utf-8',
'stripe-signature': 't=1658224240,v1=a3f574b3e6c3a02eb86308e5e43f3d0a96664098ee5dd58859fc94e96693fc50,v0=ef29de87716d9d318d6ad960f028fd5960618c853ff686bd44e261aaa2368f3b',
'accept-encoding': 'gzip'
}
is there something I do incorrectly here? Thanks a lot

Related

Axios doesn't send headers from request interceptors

On my front-end I have this Axios code:
import axios from "axios";
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
const apiUrl = process.server ? `${process.env.FRONT}api/` : '/api/';
const api = axios.create({
baseURL: apiUrl,
headers: {
'Content-Type': 'application/json'
}
})
api.interceptors.request.use(function (config) {
const token = localStorage.getItem('token')
if (token) {
config.headers.common['Authorization'] = 'Bearer ' + token
}
return config;
}, function (error) {
return Promise.reject(error);
});
export const login = async (payload) => {
const { data } = await api.post(`login`, payload)
return data
}
What this code does is just set some headers with token. Then, this request goes to front-end server:
router.post(`/login`, async (req, res) => {
try {
const data = await api.post('/login', req.body)
res.json(data.data)
} catch (e) {
res.status(e.response.status).json(e.response.data)
}
})
If you do console.log(req.headers) here, it's gonna be okay, headers will look like this:
req.headers {
accept: 'application/json, text/plain, */*',
'content-type': 'application/json',
host: 'localhost:8010',
connection: 'keep-alive',
'content-length': '898',
authorization: 'Bearer ...token...',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36',
...
}
And here is the problem, after that, when request goes to back end, it's captured by middleware auth:
router.post('/login', auth, accountController.login)
This is how this middleware looks like:
import * as jwtService from './../services/jwtService';
import { Request, Response } from 'express';
import { CommonResponse } from "../responses/response";
export default async (req: Request, res: Response, next: any) => {
try {
if (req.headers.authorization) {
const user = await jwtService.getUser(req.headers.authorization.split(' ')[1])
if (user) next();
else return CommonResponse.common.unauthorized({ res })
} else {
return CommonResponse.common.unauthorized({ res })
}
} catch (e) {
return CommonResponse.common.unauthorized({res});
}
}
And here is the problem, if you do console.log(req.headers) here you'll see, this:
{
accept: 'application/json, text/plain, */*',
'content-type': 'application/json',
'user-agent': 'axios/0.26.0',
'content-length': '898',
host: 'localhost:3000',
connection: 'close'
}
What happened to my headers?
By the way, if on front end server you do this:
const data = await api.post('/login', req.body, {headers: req.headers})
This is going to work, but still, what happened? Is this how interceptors should work?
Change your code of interceptors as follows:
api.interceptors.request.use(function (config) {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = 'Bearer ' + token
}
return config;
}, function (error) {
return Promise.reject(error);
});
And in middleware you should use Authorization instead of authorization like:
import * as jwtService from './../services/jwtService';
import { Request, Response } from 'express';
import { CommonResponse } from "../responses/response";
export default async (req: Request, res: Response, next: any) => {
try {
if (req.headers.Authorization) { // correct this line
const user = await jwtService.getUser(req.headers.Authorization.split(' ')[1]) // correct this line
if (user) next();
else return CommonResponse.common.unauthorized({ res })
} else {
return CommonResponse.common.unauthorized({ res })
}
} catch (e) {
return CommonResponse.common.unauthorized({res});
}
}
And make sure that you have token in localStorage too and provide feedback if still facing issue.
There seems to be no issue with code.
Try this demo and it works fine.
https://github.com/indolent-developer/axiosDemo
Most probably you are having issue with localstorage. Generally I like to avoid if without else. Can you can some console logs and see it is working fine.
Maybe the reason behind it is that you're assigning to request.headers.common.
It works for me when I assign to req.headers
api.interceptors.request.use(
(req) => { // I'm using req instead of config just for clarity
req.headers['Authorization'] = `token ${token}`
return req;
},
(err) => {
return Promise.reject(err);
}
);
In your server side, change req.headers.authorization to req.headers.Authorization
import * as jwtService from './../services/jwtService';
import { Request, Response } from 'express';
import { CommonResponse } from "../responses/response";
export default async (req: Request, res: Response, next: any) => {
try {
if (req.headers.Authorization) {
const user = await jwtService.getUser(req.headers.authorization.split(' ')[1])
if (user) next();
else return CommonResponse.common.unauthorized({ res })
} else {
return CommonResponse.common.unauthorized({ res })
}
} catch (e) {
return CommonResponse.common.unauthorized({res});
}
}

Can`t find JSON Web Token from headers

I have set up JWT to be set in localstorage whenever someone logins or registers. And it works, I can see the token in localstorage. But when I set the token in the headers with axios, node.js in the backend can`t find the token. Like it does not exists. I have checked it in the front end, I get logs of the token in the headers. And also when I request from postman it works. Here is the code.
setAuthToken function = {
const instance = axios.create({
baseURL: "https://localhost:5000",
});
if (token) {
instance.defaults.headers.common["x-auth-token"] = `${token}`;
console.log(instance.defaults.headers.common["x-auth-token"]);
} else {
delete instance.defaults.headers.common["x-auth-token"];
}
}
const loadUser = async () => {
if (localStorage.token) setAuthToken(localStorage.token);
console.log(localStorage.token);
try {
const res = await axios.get("/api/users");
console.log(res);
dispatch({ type: USER_LOADED, payload: res.data });
} catch (err) {
console.log(err.response.data.msg);
dispatch({ type: AUTH_ERROR });
}
The request comes to the await axios statement and goes to catch so error is in the request.
Here is the backend code
// Get current user
router.get("/", auth, async (req, res) => {
try {
const user = await User.findById(req.user.id);
res.status(200).json({ user });
} catch (err) {
console.log(err);
res.status(500).json({ msg: `Server Error` });
}
});
auth middleware function here = {
const token = req.headers["x-auth-token"];
console.log(token, "token in auth.js");
console.log(req.headers, "req.header");
if (!token) {
return res.status(401).json({ msg: `Access denied.` });
}
try {
const decoded = jwt.verify(token, config.get("jwtSecret"));
req.user = decoded.user;
next();
} catch (err) {
res.status(401).json({ msg: `Token is not valid` });
}
}
I`m new to backend develoment and axios. Can someone help me please. Thank you
Here are the console.logs
Logs
Logs
Little update, it looks like there is a problem with proxy, I am using my own backend api, and also movie data base api. So maybe thats why I cant set headers? Here are new logs:
config: Object { url: "/api/users", method: "get", timeout: 0, … }
​
data: "Proxy error: Could not proxy request /api/users from localhost:3000 to http://localhost:5000/ (ECONNREFUSED)."
​
headers: Object { connection: "keep-alive", date: "Wed, 05 May 2021 13:18:05 GMT", "keep-alive": "timeout=5", … }
​
request: XMLHttpRequest { readyState: 4, timeout: 0, withCredentials: false, … }
​
status: 500
​
statusText: "Internal Server Error
I think the issue is because you are setting you are setting up your instance wrongly
set up your instance in a new file config.js -
import Axios from 'axios';
const baseURL = "http://localhost:5000";
const axiosInstance = Axios.create({
baseURL: baseURL,
});
axiosInstance.interceptors.request.use(
function (config) {
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = 'Bearer ' + token;
}
return config;
},
function (error) {
return Promise.reject(error);
}
);
export default axiosInstance;
now when making any api request instead of using axios use axiosInstance eg-
axiosInstance.get('/something').then(res => console.log(res)).catch(err => console.log(err))

How to resolve Empty error with status code 500 axios?

this is my code :
Express Routes:
router.route('/block')
.post(controller.ticketBlocking);
Express Controller:
const axios = require('axios');
const OAuth = require('oauth-1.0a');
const crypto = require('crypto');
const ticketBlocking = (req, res) => {
const data = JSON.stringify({
source = req.body.source
});
const oauth = OAuth({
consumer: {
key: '....', //Hided the key
secret: '....', //Hided the secret
},
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return crypto.createHmac('sha1', key).update(base_string).digest('base64');
}
});
const request_data = {
url: 'http://link.vvv/blockTicket',
method: 'post',
};
axios({
method: request_data.method,
url: request_data.url,
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
...oauth.oauth.toHeader(oauth.oauth.authorize(request_data)),
},
data : data
})
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
if (error.response) {
console.log(error.response.data);
console.log(error.response.status);
} else if (error.request) {
console.log(error.request);
} else {
console.log('Error', error.message);
}
console.log(error.config);
});
};
the npm package which am using is - "oauth-1.0a"
The problem am facing is, when i use GET method with different end point, i get an output but when ever i use POST method am getting an empty error with status code 500
I dont know where is the mistake, am using oauth1.0a for authorization, please help !

How to correctly send an image to the backend (nodejs) using multer in React Native

I'm having trouble sending a photo, in a PUT route, be it with Axios or Fetch, anyway, my Nodejs backend is configured and the whole upload process works normal testing by Insonmia, but in React Native It does not work properly.
BACKEND - CONTROLLER FOR UPDATING USER DATA
async updateUserData(req: Request, res: Response, next: NextFunction) {
const { id } = req.params;
const { username, status } = req.body as IBodyData;
try {
const userExists = await knex('tb_user')
.where('id', Number(id))
.first();
if (!userExists) {
return res.status(400).json({ error: 'User does not exist.' });
}
const serializedUserInfo = {
photo: `${process.env.BASE_URL}/uploads/${req.file.filename}`,
username,
status,
};
const updateInfo = await knex('tb_user')
.update(serializedUserInfo)
.where('id', Number(id));
if (!updateInfo) {
return res.status(400).json({ error: 'Error updating data.' });
}
return res.json(updateInfo);
} catch(err) {
return res.status(500).json({ error: 'Error on updating user.' });
}
}
BACKEND - ROUTE
routes.put(
'/updateuser/:id',
multer(multerConfig).single('userphoto'),
UserController.updateUserData
);
CONFIG MULTER
export default {
fileFilter: (req: Request, file, cb) => {
const allowedMimes = [
'image/jpeg',
'image/jpg',
'image/pjpeg',
'image/png',
];
if (!allowedMimes.includes(file.mimetype)) {
return cb(new Error('Invalid file type.'));
}
return cb(null, true);
},
limits: {
fileSize: 2 * 1024 * 1024,
},
storage: multer.diskStorage({
destination: resolve(__dirname, '..', '..', 'uploads'),
filename: (req: Request, file, cb) => {
const filename = `${randomBytes(6).toString('hex')}-${file.originalname}`;
return cb(null, filename);
}
})
} as Options;
REACT NATIVE - FUNCTION FOR GET DATA OF PHOTO AND SET ON STATE
function handleUploadImage(image: IImagePickerResponse) {
if (image.error) {
return;
}
if (image.didCancel) {
return;
}
if (!image.uri) {
return;
}
setSelectedImage({
fileName: image.fileName,
fileSize: image.fileSize,
type: image.type,
uri: image.uri,
});
}
REACT NATIVE - FUNCTION FOR SEND DATA IN API
async function handleSaveChange() {
try {
const formData = new FormData();
formData.append('userphoto', {
uri:
Platform.OS === 'android'
? selectedImage?.uri
: selectedImage?.uri?.replace('file://', ''),
type: selectedImage.type,
name: selectedImage.fileName,
});
formData.append('username', usernameInput);
formData.append('status', statusInput);
console.log(formData);
await api.put(`/updateuser/${userData?.id}`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
// const updateUser = await fetch('http://192.168.2.8:3333/updateuser/1', {
// method: 'POST',
// headers: {
// 'Content-Type': 'multipart/form-data',
// },
// body: formData,
// })
// .then((response) => {
// response.json();
// })
// .then((response) => {
// console.log(response);
// })
// .catch((err) => console.log(err));
return Toast.success('Informações alteradas com sucesso.');
} catch (err) {
const {error} = err.response.data;
return Toast.error(error);
}
}
obs: A note to take into account is that in the formData.append ('userphoto', value) part if the value has sent a normal json object, the request is neither made nor the Network Error error even IP address being correct the port too, anyway, and if I use formData.append ('userphoto', JSON.stringfy(value)) the request is made normally but in the backend the photo arrives as undefined and the rest of fields are sent and received normal.
I really tried several things and I couldn't, I changed the type of the PUT method to POST without success, add 'Accept': 'application/json' without success, as I said, the normal sending by Insomnia is working.
Finally, here is the lib configuration I used to get the photo:
REACT NATIVE IMAGE PICKER
ImagePicker.showImagePicker(
{
title: 'Selecione uma imagem',
takePhotoButtonTitle: 'Tirar foto',
chooseFromLibraryButtonTitle: 'Escolher da galeria',
cancelButtonTitle: 'Cancelar',
noData: true,
// storageOptions: {
// skipBackup: true,
// path: 'images',
// cameraRoll: true,
// waitUntilSaved: true,
// },
},
handleUploadImage,
)
SOLVED
Apparently it was a problem with the version of React Native, I am currently using version 0.62.XX
To resolve, just comment line number 43 in the file:
android/app/src/debug/java/com/mobile/ReactNativeFlipper.java
The code is this:
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor (networkFlipperPlugin));

Fetch() api in react native failed - "Unhandled promise rejection"

Im trying to use the Fetch api in react native but im having some issues. Whenever I try to make a POST, i get a "Unhandled promise rejection" error saying the body not allowed for GET or HEAD reequests.
The GET method works perfectly fine, it's just with POST.
Any ideas?
submit_task() {
this.setModalVisible(!this.state.modalVisible);
const task = {
text: this.state.content,
date: this.state.date
}
console.log(task);
const API_URL = 'http://localhost:5000/tasks';
fetch(API_URL, {
methood: 'POST',
body: JSON.stringify(task),
headers: {
'Content-Type': 'application-json',
}
})
}
There's a typo in your fetch options methood should be method. Separately to that issue it's recommended to catch errors from Promises, something like:
submit_task() {
this.setModalVisible(!this.state.modalVisible);
const task = {
text: this.state.content,
date: this.state.date
}
const API_URL = 'http://localhost:5000/tasks';
fetch(API_URL, {
method: 'POST',
body: JSON.stringify(task),
headers: {
'Content-Type': 'application-json',
}
}).catch(error => {
// Maybe present some error/failure UI to the user here
});
}
This error because of you didn't handle your exception in your API call use .catch like this
submit_task() {
this.setModalVisible(!this.state.modalVisible);
const task = {
text: this.state.content,
date: this.state.date
}
console.log(task);
const API_URL = 'http://localhost:5000/tasks';
fetch(API_URL, {
methood: 'POST',
body: JSON.stringify(task),
headers: {
'Content-Type': 'application-json',
}
})
.catch(error => {
console.log('found error', error)
});
}
Or make your api call inside try and use catch function for handling the exception or error.
try {
submit_task() {
this.setModalVisible(!this.state.modalVisible);
const task = {
text: this.state.content,
date: this.state.date
}
console.log(task);
const API_URL = 'http://localhost:5000/tasks';
fetch(API_URL, {
methood: 'POST',
body: JSON.stringify(task),
headers: {
'Content-Type': 'application-json',
}
})
}
}
catch(e){
console.log('found error', error)
}

Categories