For some reason I am getting a 404 error but only on when sending a post request to '/webhook' but I cannot figure out why. the rest of my post's are absolutely fine but I keep getting this message [404] POST http://localhost:3001/webhook [evt_1IzeQ1EUG2WFNVaczcOMJWgu]
Port 3001 is my client and my server is running on port 8080 (firebase emulator). Any idea what I could be doing incorrectly?
index.js
require("dotenv").config();
const functions = require("firebase-functions");
const express = require("express");
const cors = require("cors");
const { buffer } = require('micro');
const admin = require('firebase-admin');
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const serviceAccount = require("./permissions.json");
const firebaseAdmin = !admin.apps.length ? admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
}, 'firebaseAdmin') : admin.firebaseAdmin();
// api
// app config
const app = express();
// middlewares
app.use(cors({origin: true}));
app.use(express.json());
// api routes
app.get("/", (request, response) => response.status(200).send("hello world"));
app.post("/create-checkout-session", async (request, response) => {
const { cart, email} = request.body;
const transformedItems = cart.map(item => ({
description: item.description === '' ? 'no description' : item.description,
quantity: 1,
price_data: {
currency: 'usd',
unit_amount: item.price * 100,
product_data: {
name: item.title,
},
}
}));
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
shipping_address_collection: {
allowed_countries: ['US']
},
line_items: transformedItems,
mode: 'payment',
success_url: `${process.env.DOMAIN}/confirmation`,
cancel_url: `${process.env.DOMAIN}/sorry`,
metadata: {
email
}
});
response.status(200).json({ id: session.id});
});
const endpointSecret = process.env.STRIPE_SIGNING_SECRET;
const fulfillOrder = async (session) => {
console.log('Fulfilling Booking', session);
return firebaseAdmin
.firestore()
.collection('customers')
.doc(session.metadata.email)
.collection('orders')
.doc(session.id).set({
amount: session.amount_total / 100,
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
.then(() => {
console.log(`SUCCESS! Order #${session.id} has been added to the database!`);
})
}
app.post("/webhook", async (request, response) => {
const requestBuffer = await buffer(request);
const payload = requestBuffer.toString();
const sig = request.headers("stripe-signature");
let event;
try {
event = stripe.webhooks.constructEvent(payload, sig, endpointSecret);
} catch (err) {
console.log('ERROR!!!', err.message);
return res.status(400).send(`Webhook error: ${err.message}`);
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
return fulfillOrder(session)
.then(() => response.status(200))
.catch((err) => response.status(400)
.send(`Webhook error ${err.message}`));
}
});
// listen command
exports.api = functions.https.onRequest(app, firebaseAdmin);
To test Stripe webhooks locally (i.e. on localhost), you need to forward those webhook events to your localhost port. For that you need to install and configure the Stripe CLI. Once installed you can configure it using these commands:
stripe login
stripe listen --forward-to localhost:5001/webhook
// To manually trigger events using CLI
stripe trigger payment_intent.created
Because if you are triggering events from the dashboard they won't reach your localhost.
The port number should be the same as Cloud Functions emulator and the complete URL should match where your function is running. In you case, "http://localhost:5001/piers-laine/us-central1/api/webhook"
Related
I'm getting a cors error when trying to login into my MERN app
Screenshot.It's working fine locally and in the Network tab it shows a 503 error code.
My Heroku logs show these messages.
const err = new MongooseError(message);
MongooseError: Operation users.findOne() buffering timed out after 10000ms
But, I'm getting the 'User Name verified' console log in the Heroku logs which is present in the validation.js file below. So the backend connection is fine I think.
I used 0.0.0.0/0 in MongoDB network access and used cors package for all origin.
Also tried using mongoose.connect using async/await, and using the older drivers in MongoDB but nothing seems to work. If anyone knows how to fix it please respond.
Here is my code in node.js.
index.js
const express = require('express')
const cors = require('cors')
const DBConnect = require('./mongo')
const Login = require('./routes/login')
const Google = require('./routes/google')
const Register = require('./routes/register')
const SaveData = require('./routes/saveData')
require('dotenv').config()
const port = process.env.PORT || 5000
const app = express()
const corsOptions = {
origin: '*',
credentials: true,
optionSuccessStatus: 200
}
app.use(cors(corsOptions))
app.use(express.json())
DBConnect().then(()=>console.log('DB Connection successfull!'))
app.get('/', (req,res)=>{ res.json('Server running')})
app.use('/api/gauth', Google)
app.use('/api/login', Login)
app.use('/api/register', Register)
app.use('/api/save', SaveData)
app.listen(port,()=>console.log(`Server listening on port ${port}`))
mongo.js
const mongoose = require('mongoose')
module.exports = async () => {
const username = process.env.USER_NAME
const password = process.env.PASSWORD
const cluster = process.env.CLUSTER
const dbname = process.env.DB_NAME
const url = `mongodb+srv://${username}:${password}#${cluster}.mongodb.net/${dbname}?retryWrites=true&w=majority`
const mongooseOptions = {
useNewUrlParser: true,
useUnifiedTopology: true
}
await mongoose.connect(url, mongooseOptions)
.then(x => console.log(`Connected to MongoDB : ${x.connections[0].name}`))
.catch(err => console.error('Error connecting to mongo', err))
}
google.js
const app = require('express')()
const googleAuth = require('../components/validation')
const userModel = require('../components/models')
const bcrypt = require('bcrypt')
const JWT = require('jsonwebtoken')
const dotenv = require('dotenv')
dotenv.config()
app.post('/', async (req,res)=>{
const user = await googleAuth(req.body.token)
const emailExist = await userModel.findOne({ email: user.email })
const token = JWT.sign({_id: user.id}, process.env.TOKEN_KEY)
const data = {
token: token,
email: user.email,
name: user.name,
image: user.image,
data: emailExist && emailExist.data || []
}
if(!emailExist){
const salt = await bcrypt.genSalt(10)
const cipherPass = await bcrypt.hash(user.id, salt)
const newUser = new userModel({
username: user.name,
email: user.email,
password: cipherPass,
image: user.image,
data: []
})
try{
await newUser.save()
res.status(200).json(data)
}catch (err){
res.status(500).json(err)
}
}else{
res.status(200).json(data)
}
})
module.exports = app
validation.js
const { OAuth2Client } = require('google-auth-library')
const client = new OAuth2Client(process.env.OAUTH_ID)
const googleAuth = async token => {
const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.OAUTH_ID
})
const payload = ticket.getPayload()
// console.log('Paylod:', payload)
console.log(`User ${payload.name} verified`)
const { sub, name, email, picture } = payload
const userId = sub
return { id:userId, name:name, email:email, image:picture}
}
module.export = googleAuth
I'm trying to make a payment component to my react app. This is my first time using stripe. I've tried to follow a tutorial, and I have done exactly the same thing as in the tutorial, but still getting an internal server error or invalid value for stripe.confirmCardPayment (see image below) when posting the request with axios. Hopefully someone can guide me onto the right track with this.
CheckoutForm.js - HandleSubmit function
handleSubmit = async event => {
event.preventDefault();
const { stripe, elements } = this.props;
if (!stripe || !elements) {
return;
}
const card = elements.getElement(CardElement);
const { } = await axios.post("/api/stripe/charge", {
amount: 1000,
})
const paymentMethodReq = await stripe.createPaymentMethod({
type: 'card',
card: card,
billing_details: {
name: 'Daniel Olsen',
email: 'olsen.daniel04#gmail.com'
},
payment_method: {
card: card
}
})
console.log(paymentMethodReq)
axios.get('/api/stripe/charge').then(function(response) {
console.log(response)
return response.json();
}).then(function(responseJson) {
var clientSecret = responseJson.client_secret;
const confirm = await stripe.confirmCardPayment(clientSecret, {
payment_method: paymentMethodReq.paymentMethod
})
});
const result = await stripe.createToken(card);
if (result.error) {
console.log(result.error.message);
} else {
console.log(result.token);
}
};
Stripe.js
const stripe = require('stripe')('secret_key')
async function postCharge(req, res) {
try {
const { amount } = req.body
const charge = await stripe.paymentIntent.create({
amount: 2000,
currency: 'nok',
payment_method_types: ['card'],
})
if (!charge) throw new Error('charge unsuccessful')
res.status(200).json({
message: 'charge posted successfully',
charge
})
} catch (error) {
res.status(500).json({
message: error.message
})
}
}
module.exports = postCharge
server.js
const app = express()
const router = express.Router()
const port = 3000
app.post('/stripe/charge', postCharge)
router.all('*', (_, res) =>
res.json({ message: 'please make a POST request to /stripe/charge' })
)
app.use((_, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
)
next()
})
app.use(bodyParser.json())
app.use('/api', router)
app.use(express.static(path.join(__dirname, '../build')))
app.get('*', (_, res) => {
res.sendFile(path.resolve(__dirname, '../build/index.html'))
})
app.listen(port, () => console.log(`server running on port ${port}`))
Error:
After you create the payment intent you're sending back the entire "charge" [sic] object in the json:
res.status(200).json({
message: 'charge posted successfully',
charge
})
While I'd suggest changing this to only send back the client_secret, the way you have it now the client needs to retrieve responseJson.charge.client_secret. You're missing that charge layer.
Whenever I try to run the function refreshStock() in an endpoint in one of the API endpoints /api/seller/deactivate it gives me this error:
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (net.js:1318:16)
at listenInCluster (net.js:1366:12)
at Server.listen (net.js:1452:7)
at C:\Users\***\Documents\GitHub\***\***\.next\server\pages\api\seller\deactivate.js:191:10
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command
It looks like it's trying to restart the server, but it happens after it compiles, is there something I'm doing wrong, I've followed a couple of tutorials on medium, and they give this same type of code, just not ES Modules. I want to use ES Modules because it is what my database functions are written in.
Server.js:
import express from 'express';
import { createServer } from 'http';
import next from 'next';
import models from './server/models';
import { genStock } from './server/lib/functions';
import { Server } from 'socket.io';
const port = parseInt(process.env.PORT || '3000', 10);
const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({ dev });
const nextHandler = nextApp.getRequestHandler();
const app = express();
const server = createServer(app);
const io = new Server(server);
const Users = models.users;
io.use(async (socket, next) => {
const err = new Error('Unauthorized');
err.data = { message: 'Unauthorized, please try again later.' };
try {
if (!socket.handshake.auth.token) return next(err);
let user = await Users.findOne({
where: {
socket_token: socket.handshake.auth.token,
},
});
if (!user) {
console.log('unauthenticated socket');
socket.disconnect();
next(err);
}
await Users.update(
{ socket_id: socket.id },
{
where: {
socket_token: socket.handshake.auth.token,
},
},
);
next();
} catch (e) {
console.log(e);
next(e);
}
});
io.on('connection', async (socket) => {
// Works fine
const stock = await genStock();
socket.emit('updateStock', stock);
});
// Fails with address already in use :::3000
export async function refreshStock() {
const stock = await genStock();
io.emit('updateStock', stock);
}
nextApp.prepare().then(async () => {
app.all('*', (req, res) => nextHandler(req, res));
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
This is meant to refresh the stock after a seller deactivates their account and sends all users the new stock.
/api/seller/deactivate
....
await refreshStock();
....
I figured it out, I just split up the WebSocket server and the next.js one. I have whitelisted local IPs that may appear to only allow server-to-server communication. Although I don't think this is full-proof as there is most likely a better way to have this type of communication but for now it works.
/**
* This server cannot be imported in /api folders, it won't work.
* Although it can import other functions
* */
import express from 'express';
import { createServer } from 'http';
import session from 'express-session';
import { Server } from 'socket.io';
import { genStock } from './server/lib/stockFunctions';
import { sessionStore } from './server/lib/session';
import passport from './server/lib/passport';
import models from './server/models';
const authorizedIPs = ['::1', '127.0.0.1', '::ffff:127.0.0.1'];
const Users = models.users;
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: `http://localhost:3000`,
methods: ['GET', 'POST'],
credentials: true,
},
});
const wrap = (middleware) => (socket, next) => middleware(socket.request, {}, next);
io.use(
wrap(
session({
secret: "---",
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
path: '/',
sameSite: 'lax',
},
store: sessionStore,
}),
),
);
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));
io.use(async (socket, next) => {
const err = new Error('Unauthorized');
err.data = { message: 'Unauthorized, please try again later.' };
try {
const user = socket.request.user;
if (!user) return next(err);
await Users.update(
{ socket_id: socket.id },
{
where: {
id: user.id,
},
},
);
next();
} catch (e) {
console.log(e);
next(e);
}
});
io.on('connection', async (socket) => {
const stock = await genStock();
socket.emit('updateStock', stock);
});
app.post('/refresh-stock', async function (req, res) {
const ip = req.ip;
if (!authorizedIPs.includes(ip)) {
console.log(ip);
return res.status(401).json({ success: false });
}
const newStock = await genStock();
io.emit('updateStock', newStock);
return res.status(200).json({ success: true });
});
httpServer.listen(3001);
console.log(`> Websockets ready on http://localhost:3001`);
so I get this error every time I re-render the site
" GET http://localhost:3000/api/ShopItems 431 (Request Header Fields Too Large)"
I can see that my fetch doesn't go as planed and invokes the error above ⬆️
The chrome developer tool points me to the "ShopItemsActions" fill
import { FETCH_SHOPITEMS } from "../types"
export const fetchShopItems = () => async (dispatch) => {
const res = await fetch("/api/ShopItems");
const data = res.json();
console.log(data);
dispatch({
type: FETCH_SHOPITEMS,
payload: data
});
}
The chrome developer tool mark the third line "const res = await fetch("/api/ShopItems")"
Question: how can i fix this error?
Edit:
the server side code
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const shortid = require('shortid');
const app = express();
app.use(bodyParser.json());
mongoose.connect("mongodb://localhost/bread_project_db", {
useNewUrlParser:true,
useCreateIndex: true,
useUnifiedTopology:true,
});
const ShopItem = mongoose.model(
"ShopItems",
new mongoose.Schema({
id: { type: String, default: shortid.generate },
name: String,
image: String,
price: Number,
info: String,
flourType: [String],
}))
app.get("/api/ShopItems", async (req, res)=>{
const ShopItems = await ShopItem.find({});
res.send(ShopItems);
});
app.post("/api/ShopItems", async (req, res)=>{
const newShopItem = new ShopItem(req.body);
const savedShopItem = await newShopItem.save();
res.send(savedShopItem);
});
app.delete("/api/ShopItems/:id", async (req, res) => {
const deletedShopItem = await ShopItem.findByIdAndDelete(req.params.id);
res.send(deletedShopItem);
});
const port = process.env.PORT || 5000;
app.listen( port, () => console.log(`server at http://localhost:${port}`));
I am using postman and I dont have cURL installed for some reason.
the postman just show this:
431 Request Header Fields Too Large
The server is unwilling to process the request because either an individual header field, or all the header fields collectively, are too large. Proposed in an Internet-Draft.
I am trying to get a response from '/razorpay' route in my index.js file, and use it in displayRazorpay()
. For this, I'm using fetch, but it shows forbidden 403 error. Can someone help out? I have attached my '/razorpay' route, and displayRazorpay() function below. I'm new to node.js, so any help is appreciated!
router.post('/razorpay', async function(req,res,next){
if(!req.session.cart){
return res.redirect('/shopping-cart');
}
var cart = new Cart(req.session.cart);
const payment_capture = 1;
const amount = cart.totalPrice;
const currency = 'INR';
const options = {
amount: amount * 100,
currency,
receipt: shortid.generate(),
payment_capture,
}
try {
const response = await razorpay.orders.create(options);
console.log(response);
res.json({
id: response.id,
currency: response.currency,
amount: response.amount
});
} catch (error) {
console.log(error);
}
});
async function displayRazorpay() {
const data = await fetch('/razorpay', {method:'POST'}).then((t) =>
t.json()
);
console.log(data);
const options = {
key: 'rzp_test_2eJE3rP3gEWqze',
amount: 400,
order_id: data.id,
name: 'Donation',
description: 'Thank you for nothing. Please give us some money',
image: '/images/modiLogo3.png',
handler: function (response) {
alert(response.razorpay_payment_id);
alert(response.razorpay_order_id);
alert(response.razorpay_signature);
},
prefill: {
email: 'sdfdsjfh2#ndsfdf.com',
phone_number: '9899999999'
}
}
const paymentObject = new window.Razorpay(options);
paymentObject.open();
}
This is a problem of CORS. Make sure you enable cors on your server side.
Since you are using express, here's sample code
First install cors: npm install cors
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
app.get('/products/:id', function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})