Node code:
app.post("/create-payment-intent", async (req, res) => {
try {
const { items } = req.body;
console.log(items)
const paymentIntent = await stripe.paymentIntents.create({
currency: "gbp",
amount: items,
automatic_payment_methods: { enabled: true },
});
// Send publishable key and PaymentIntent details to client
res.send({
clientSecret: paymentIntent.client_secret,
});
React code:
useEffect(() => {
fetch("/create-payment-intent", {
method: "POST",
headers: {
"Content-Type" : "application/json"
},
body: JSON.stringify({items: [{ price: totalAmount }]}),
}).then(async (result) => {
var { clientSecret } = await result.json();
setClientSecret(clientSecret);
});
}, [totalAmount]);
Error: error
I do not understand why I cannot destructure items on the back end when it has been passed.
I have used the Stripe docs in order to attempt this: https://stripe.com/docs/payments/quickstart?locale=en-GB&lang=node
Related
I'm trying to set the cookie from my express backend to my nextjs front end using the response.setHeader, but it's not working, I get my json response but no cookies are set, I used postman to make some tests and in postman it did set the cookies as it should be.
NextJs version is 13.0.2, express version is 4.18.2 and cookie version is 0.5.0
My express server:
export const loginUser = async (req: Request, res: Response) => {
const { email, password } = req.body;
//limpa os cookies
res.clearCookie("accessToken", { httpOnly: true });
if (email !== "" && password !== "") {
try {
const foundUser: any = await prisma.users.findFirst({
where: {
email,
password,
},
});
try {
const accessToken = jwt.sign(
{ id: foundUser.id, email },
"dspaojdspoadsaodksa",
{
expiresIn: "10s",
}
);
const refreshToken = jwt.sign(
{ id: foundUser.id, email },
"dsaoindsadmnsaosda",
{
expiresIn: "50s",
}
);
const savedRefresh = await prisma.refresh_token.upsert({
where: {
users_id: foundUser.id,
},
update: {
access_token: accessToken,
refresh_tk: refreshToken,
users_id: foundUser.id,
},
create: {
access_expires_in: "",
refresh_expires_in: "",
access_token: accessToken,
refresh_tk: refreshToken,
users_id: foundUser.id,
},
});
res.setHeader(
"Set-Cookie",
cookie.serialize("accessToken", accessToken, {
maxAge: 1000 * 60 * 15, //15 minutes
httpOnly: true, // The cookie only accessible by the web server
})
);
res.json({ accessToken, user: { email }, ok: true });
} catch (error) {
console.log("refresh cant be created");
res.send({ message: "refresh cant be created" });
}
} catch (error) {
res.send({ message: "user not found" });
}
} else {
res.json({ message: "token creation failed" });
}
};
My nextJs front-end:
const handleLogin = async (event: React.SyntheticEvent) => {
event.preventDefault();
const email = (event?.target as any).email.value;
const password = (event?.target as any).password.value;
if (email === "" || password === "") {
setError(true);
return;
}
const data = {
email,
password,
};
const resp = await fetch("http://localhost:5000/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const userData = await resp.json();
console.log(userData);
};
Is it possible to host stripe webhook with node.js deploy.
I've setup my stripe webhook with stripe checkout and It's ran successfully in localhost using stripe cli.
But I'm tried to take webhooks live.
const express = require("express");
const Stripe = require("stripe");
const { Order } = require("../models/Order");
require("dotenv").config();
const stripe = Stripe(`${process.env.STRIPE_SECRET}`);
const router = express.Router();
router.post("/create-checkout-session", async (req, res) => {
const customer = await stripe.customers.create({
metadata: {
userId: req.body.userId,
cart: JSON.stringify(req.body.products),
},
});
const line_items = req.body.products.map((item) => {
return {
price_data: {
currency: "usd",
product_data: {
name: item.title,
description: item.description,
metadata: {
id: item.id,
},
},
unit_amount: item.price * 100,
},
quantity: item.quantity,
};
});
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
shipping_address_collection: {
allowed_countries: ["BD", "US", "CA"],
},
shipping_options: [
{
shipping_rate_data: {
type: "fixed_amount",
fixed_amount: {
amount: 0,
currency: "usd",
},
display_name: "Free shipping",
// Delivers between 5-7 business days
delivery_estimate: {
minimum: {
unit: "business_day",
value: 5,
},
maximum: {
unit: "business_day",
value: 7,
},
},
},
},
{
shipping_rate_data: {
type: "fixed_amount",
fixed_amount: {
amount: 1500,
currency: "usd",
},
display_name: "Next day air",
// Delivers in exactly 1 business day
delivery_estimate: {
minimum: {
unit: "business_day",
value: 1,
},
maximum: {
unit: "business_day",
value: 1,
},
},
},
},
],
line_items,
mode: "payment",
customer: customer.id,
success_url: `https://nobab-3b3c4.web.app/checkout-success`,
cancel_url: `https://nobab-3b3c4.web.app/`,
});
// res.redirect(303, session.url);
res.send({ url: session.url });
});
// Create order function
const createOrder = async (customer, data) => {
const Items = JSON.parse(customer.metadata.cart);
const products = Items.map((item) => {
return {
productId: item.id,
quantity: item.quantity,
};
});
const newOrder = new Order({
userId: customer.metadata.userId,
customerId: data.customer,
paymentIntentId: data.payment_intent,
products,
subtotal: data.amount_subtotal,
total: data.amount_total,
shipping: data.customer_details,
payment_status: data.payment_status,
});
try {
const savedOrder = await newOrder.save();
console.log("Processed Order:", savedOrder);
} catch (err) {
console.log(err);
}
};
// Stripe webhoook
router.post(
"/webhook",
express.json({ type: "application/json" }),
async (req, res) => {
let data;
let eventType;
// Check if webhook signing is configured.
// let webhookSecret = `${process.env.STRIPE_WEB_HOOK}`;
let webhookSecret;
if (webhookSecret) {
// Retrieve the event by verifying the signature using the raw body and secret.
let event;
let signature = req.headers["stripe-signature"];
try {
event = stripe.webhooks.constructEvent(
req.body,
signature,
webhookSecret
);
} catch (err) {
console.log(`⚠️ Webhook signature verification failed: ${err}`);
return res.sendStatus(400);
}
// Extract the object from the event.
data = event.data.object;
eventType = event.type;
} else {
// Webhook signing is recommended, but if the secret is not configured in `config.js`,
// retrieve the event data directly from the request body.
data = req.body.data.object;
eventType = req.body.type;
}
// Handle the checkout.session.completed event
if (eventType === "checkout.session.completed") {
stripe.customers
.retrieve(data.customer)
.then(async (customer) => {
try {
// CREATE ORDER
createOrder(customer, data);
console.log("Ordered");
res.status(200).json({ message: 'Order created', data: data })
res.status(200).send("Order created")
} catch (err) {
console.log(typeof createOrder);
console.log(err);
}
})
.catch((err) => console.log(err.message));
}
res.status(200).end();
}
);
module.exports = router;
This code working fine and store user order in mongodb database, but i need to run this after my server deployment.
Thanks everyone
for help me
Yes it should be possible to host webhook with Node: Stripe Doc. It's a separated logic with your Checkout Creation endpoint, and you will need to test the webhook endpoint properly.
I'm trying to implement stripe to my small e-commerce website developed in reactjs. To do so I've used the checkout component.
It works perfectly when i'm on localhost, but after i deploy the backend with heroku I receive this error message :
Apparently i didn't provide the API key but I feel like I did... I don't understand ...
Anyone ?
On the front end :
// Linking with the back end
const makePayment = (token) => {
const body = {
token: token,
product: product,
};
const headers = {
"Content-Type": "application/json",
};
return fetch(`https://serotonineneshop.herokuapp.com/payment`, {
method: "POST",
headers: headers,
body: JSON.stringify(body),
})
.then((response) => {
console.log("RESPONSE : ", response);
const { status } = response;
console.log("STATUS : ", status);
})
.catch((err) => console.log(err));
};
on the back-end :
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY)
app.post("/payment", (req, res) => {
const {
product,
token
} = req.body;
const idempotencyKey = uuidv4()
return stripe.customers.create({
email: token.email,
source: token.id
}).then(customer => {
stripe.charges.create({
amount: product.price * 100,
currency: "eur",
customer: customer.id,
receipt_email: token.email,
description: `purchase of ${product.name}`,
shipping: {
name: token.card.name,
phone: token.card.phone,
address: {
country: token.card.address_country,
postAddress: token.card.address,
zipCode: token.card.zipCode
}
}
}, {
idempotencyKey
})
.then(result = res.status(202).json(result))
.catch(err => console.log(err))
})
});
I'm going insane over this one. The api data is returning correctly when using postman, but for some reason in React I cannot get the data. While experimenting I noticed that if I pass only one condition in the api I will get the response back in React, but when passing two I cannot. Keep in mind this is only happening in react, so the API in postman is returning the response perfectly.
React Snippet:
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
const { data: response } = await axios.get('/api/activity/suggested');
setData(response);
} catch (error) {
console.error(error);
}
setLoading(false);
};
fetchData();
}, []);
Mongoose api :
router.get('/suggested', auth, async (req, res) => {
try {
const user = await User.findById(req.user.id);
const activity = await Activity.find({
event: 'suggest_to_user',
user: req.user.id,
})
.sort({ date: -1 })
.populate([
{
path: 'user',
},
{
path: 'ref_following',
},
{
path: 'ref_request',
},
{
path: 'ref_review',
populate: [
{
path: 'suggestion',
},
],
},
{
path: 'ref_collection',
populate: [
{
path: 'user',
},
],
},
{
path: 'ref_suggestion',
},
]);
res.json(activity);
} catch (error) {
console.error(error.message);
res.status(500).send('Server Error');
}
});
Removing a condition here make it work in React:
const activity = await Activity.find({
event: 'suggest_to_user',
user: req.user.id,
})
I think the problem is that when you call the API from your to react app. It doesn't find the id of the user. Try to send authorization user info through the header. Hope you find this helpful.
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.