How to use JWT with stripe to retrieve a paymentIntent? - node.js

I have a MEVN stack application that uses JWT for auth and that can take stripe payments.
Upon payment, I need to retrieve the payment intent object for that charge and send it to the front end to validate payment and serve up a PDF. My Question is, how can I make sure that the customer only had access to the charge created by that particular user by using the Json web token.
My current node.js code for stripe (without JWT)
const express = require("express");
const router = express.Router();
const endpointSecret = process.env.WEBHOOK_SECRET;
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
let Intent;
router.post("/", async (req, res) => {
const session = await stripe.checkout.sessions.create(
{
success_url: "http://localhost:8080/#/success",
cancel_url: "http://localhost:8080/#/cancel",
payment_method_types: ["card"],
line_items: [
{
price: "price_1H0up7Kc91wTjOOikyrKImZs",
quantity: 1,
},
],
mode: "payment",
},
function (err, session) {
if (err) {
console.log(err);
res.status(500).send({ success: false, reason: "session didnt work" });
} else {
console.log(session);
Intent = session.payment_intent;
console.log(Intent);
res.json({ session_id: session.id });
// res.status(200).send({ success: true });
}
}
);
});
router.get("/confirm", async (req, res) => {
const intentObject = await stripe.paymentIntents.retrieve(Intent, function (
err,
paymentIntent
) {
if (err) {
console.log(err);
res
.status(500)
.send({ success: false, reason: "cannot retrieve payment" });
} else {
console.log(paymentIntent);
res.status(200).json({ status: paymentIntent.status });
setTimeout(() => (intent = ""), 10);
}
});
});
module.exports = router;

You can't use Intent the way you're using it there; it won't persist between requests.
You might want to consider using something like this: https://github.com/auth0/express-jwt

Related

Node Express server not send messages with whatsapp-web-js

i have a problem with my custom server, i'm trying to setup an api that send whatsapp messages in react as a frontend.
So, i have actually a route that send the QR to the frontend (working fine), a route that handle the authentication event (working fine) and
PROBLEM HERE:
...a route that send a message to a specific number (NOT WORK)
here my server code...what i'm doing wrong? if i launch a POST request to the endpoint on postman, i get an infinite loading (no errors).
const express = require('express')
const router = express.Router()
const fs = require('fs')
const { Client, LocalAuth } = require('whatsapp-web.js')
const authStrategy = new LocalAuth({
clientId: 'adminSession',
})
const worker = `${authStrategy.dataPath}/session-admin/Default/Service Worker`
if (fs.existsSync(worker)) {
fs.rmdirSync(worker, { recursive: true })
}
const client = new Client({
takeoverOnConflict: true,
authStrategy,
})
const sessionData = {
client: 'admin',
session: true,
qrCodeScanned: true,
}
client.on('authenticated', (session) => {
fs.writeFile(
'waSession.json',
JSON.stringify(sessionData),
'utf-8',
(err) => {
if (!err) {
console.log('Session saved on disk...')
}
}
)
})
router.get('/whatsapp/auth', async (req, res) => {
const dir = './waSession.json'
fs.readFile(dir, (err, data) => {
if (data.length === 0) {
return res.status(200).json({
message: 'You need to login first',
})
} else {
return res.status(200).json({
message: 'You are logged in.',
})
}
})
})
router.get('/whatsapp', async (req, res) => {
try {
client.on('qr', (qr) => {
res.status(200).send({
message: 'Connect whatsapp with this qr-code',
qrCode: qr,
})
})
await client.initialize()
res.status(404)
} catch (err) {
res.send(err)
}
})
router.post('/whatsapp/send', async (req, res) => {
const { phoneNumber, message } = req.body
try {
client.on('ready', async () => {
const number = phoneNumber
const text = message
const chatId = number.substring(1) + '#c.us'
await client.sendMessage(chatId, text)
})
await client.initialize()
res.json('Messaggio inviato')
} catch (err) {
res.status(404).send(err)
await client.destroy()
}
})
module.exports = router
A Client represents one authenticated WhatsApp user, and you have only one global variable client. This implies that all incoming requests will represent the same WhatsApp user, even if several different real users send requests to your server in parallel. This is probably not what you intend.
I suggest that you use express-session to associate every client with a session. Then a user needs to create a client and authenticate it only once during a session. All subsequent requests in the same session will re-use that client, and the client.on(...) and client.initialize() commands will not be repeated.

How do I make sure customers don't skip paywall with stripe?

I am building a marketplace with a model similar to fiverr. You pay for a service, and after you pay you can fill out the preferences that you want from the seller. The problem I am facing is that the successlink after payment can be just copy and pasted to progress to the preferences page without payment. How do I ensure this doesn't happen with stripe. Here is my server code:
//checkout stripe session code:
app.post('/create-checkout-session', async (req, res) => {
const {request} = req;
const account_id = req.body.account_id;
const user_id = req.body.user_id;
var successLink = 'http://localhost:3000/dashboard/userreq/'+user_id;
console.log('request: ' + req.body.account_id);
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price_data: {
currency: 'usd',
product_data: {
name: 'Story',
},
unit_amount: 1300,
},
quantity: 1,
},
],
payment_intent_data: {
application_fee_amount: 123,
transfer_data: {
destination: account_id,
},
},
mode: 'payment',
success_url: successLink,
cancel_url: 'http://localhost:3000/cancel.html',
});
res.send({
sessionId: session.id,
});});
//webhook to see if payment was successful
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => {
const sig = request.headers['stripe-signature'];
let event;
// Verify webhook signature and extract the event.
// See https://stripe.com/docs/webhooks/signatures for more information.
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
handleCompletedCheckoutSession(session);
}
response.json({received: true});
});
const handleCompletedCheckoutSession = (session) => {
// Fulfill the purchase.
console.log(JSON.stringify(session));
}
app.listen(process.env.PORT || 8080, () => {
console.log("Server started...");
});
You'd want to encode the Checkout Session ID within your success URL: https://stripe.com/docs/payments/checkout/custom-success-page#modify-success-url
Then when your success page is hit, you'd check to see if the session ID is valid by retrieving the Checkout Session from the backend and checking its payment_status: https://stripe.com/docs/api/checkout/sessions/object#checkout_session_object-payment_status
If there's no session ID or the payment_status is not paid, then you wouldn't grant access to whatever it is that you're selling.

testing authenticated express endpoint with superagent does not work?

I am testing my application however I could not figure out how to test the authenticated endpoints with supertest, based on documentation i wrote this, but it is not working. Can someone help me how to test authenticated endpoints: this is my code:
const request = require("supertest");
const app = require("../../server/app");
let testSession = null;
beforeEach(function () {
testSession = request.agent(app);
});
test("Should add new blogs", async () => {
await testSession
.post("/api/v1/blogs")
.send({
title: "first test",
location: "ny",
position: "developer",
})
.expect(200);
});
here is the result:
expected 200 "OK", got 401 "Unauthorized"
endpoints are working but here are the controller and route:
exports.createBlog = (req, res) => {
//key comes from client
//because if other user send the request I dont wanna lock the request for other one
//we send the key with the save button in the controller menu
const lockId = req.query.lockId;
let blog;
if (!lock.isBusy(lockId)) {
lock
.acquire(lockId, async () => {
const blogData = req.body;
blog = await new Blog(blogData);
if (req.user) {
blog.userId = req.user.sub;
blog.author = req.user.name;
}
await blog.save();
})
.then(() =>
res.status(200).json({ message: "Blog is saved!", blog: blog })
)
.catch((e) => res.status(422).json({ message: e.message }));
} else {
return res.status(422).json({ message: "Blog is saving" });
}
router.post(
"/",
authService.checkJwt,
authService.checkRole("siteOwner"),
blogCtrl.createBlog
);

access the info of the user that created the post

I am making a react-native mobile app and I am having trouble passing the users info that created the post to the home page in the post detail. I can pass the userID but for some reason when I add the rest of the info to the payload I can't create a post. Please help.
BACKEND
This is the requireAuth file that requires authentication before performing a tast. My code for the user is here as well at the bottom---
const mongoose = require("mongoose");
const User = mongoose.model("User");
module.exports = (req, res, next) => {
const { authorization } = req.headers;
if (!authorization) {
return res.status(401).send({ error: "You must be logged in." });
}
const token = authorization.replace("Bearer ", "");
jwt.verify(token, "mySecretKey", async (err, payload) => {
if (err) {
return res.status(401).send({ error: "You must be logged in." });
}
const { userId, name, phone, email } = payload;
const user = await User.findById(userId);
req.user = user;
console.log(req.user);
next();
});
};
This is the POST route for the Item---
router.post("/items", requireAuth, async (req, res) => {
const { title, category, detail, condition, price } = req.body;
if (!title || !category || !detail || !condition || !price) {
return res.status(422).send({
error: "You must provide a title, category, detail, condition, and price"
});
}
try {
const item = new Item({
title,
category,
detail,
condition,
price,
userId: req.user._id
});
await item.save();
res.send(item);
} catch (err) {
res.status(422).send({ error: err.message });
}
});
FRONT-END
This is my createItem function in the itemContext file---
const createItem = dispatch => async ({
title,
category,
detail,
condition,
price
}) => {
try {
const response = await sellerApi.post("/items", {
title,
category,
detail,
condition,
price
});
//this is the other place the error might be happening i need this to save in the phone local storage
dispatch({ type: "create_item", payload: response.data });
navigate("Home");
} catch (err) {
console.log(err);
}
};
All I am trying to do it is when the post is being displayed so is the info of the post creator
For existing post in the database: If you are referencing your user in post model like this
const Post = mongoose.model('Post', {
// other fields
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
}
})
then you can use populate to fetch user of that post.
const post= await Post.findById('5c2e505a3253e18a43e612e6')
await post.populate('userId').execPopulate()
console.log(post.userId)

how to make another post request after one is done to stripe api from express?

I am trying to make a subscription to stripe, and in order for that I need a user created first - done.
After I create the user how can i proceed automatically to make the subscription for given user?
my code so far
const stripe = require('./../constants/stripe');
const postStripeCharge = res => (stripeErr, stripeRes) => {
if (stripeErr) {
res.status(500).send({
error: stripeErr
});
}
else {
res.status(200).send({
success: stripeRes
});
}
}
const paymentApi = app => {
app.get('/', (req, res) => {
res.send({
message: 'Hello Stripe checkout server!',
timestamp: new Date().toISOString()
})
});
app.post('/', (req, res) => {
console.log('request in server', req);
// stripe.charges.create(req.body, postStripeCharge(res));
stripe.customers.create({
description: 'customer for ' + req.body.email,
source: req.body.token
}, postStripeCharge(res))
});
return app;
};
module.exports = paymentApi;
this is the code i want to call after the user is created :
stripe.subscriptions.create({
customer: req.id,
items: [{
plan: 'plan_CyQRCVcrEztYI7'
}],
}
Fixed it: it is not req.body.token but req.body.source, and then it works as it should.

Resources