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.
Related
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
I want to update data in the server with a Axios Patch call and there seems to be some error in the code which i cant seem to figure out. Help me out please.
Node express API call in the backend:
router.patch('/up',async (req, res) => {
try{
const id = req.query.userid; // User objectid here.
console.log(req.body)
console.log(req.query.userid)
const result= User.findById(id, (error, foundUser) => {
if (foundUser) {
const { products } = foundUser;
const filteredproducts = products.filter((item) => {
return item._id!= req.body.body._id; //product array object id
});
while (foundUser.products.length > 0) {
foundUser.products.pop();
}
foundUser.products = filteredproducts;
foundUser.products.push({
brandname: req.body.brandname,
productname: req.body.productname,
quantity: req.body.quantity,
price: req.body.price,
description: req.body.description
});
foundUser.save();
}
else
{
console.log('User not found')
}
})
res.status(200).send("update successfull");
}
catch(err){
res.status(403).send('update unsuccessfull')
console.log(err)
}
})
Userschema in db:
const userschema = new mongoose.Schema({
username: {
type: String,
},
email: {
type: String,
required: true
},
place: {
type: String,
required: true
},
password: {
type: String,
required: true
},
products:[
{
brandname: {
type:String,
required:true
},
productname:{
type:String,
required:true
},
quantity:{
type:Number,
required:true
},
price:{
type:Number,
required:true
},
description:{
type:String,
required:true
}
}
]
,
tokens:
[{
token: {
type: String,
required: true
}
}
]
})
const User = mongoose.model('USER', userschema)
Front end API code
const updateproduct = async(req,res) => {
console.warn(values.brandname, values.productname,values.quantity, values.price,
values.description)
console.warn("userlogin is:,",userid) // User object id here.
const _id=id; // Product object id here
await axios.patch(`http://localhost:5000/up`,{
headers: {
'Content-type': 'application/json'
},
data: {brandname,productname,quantity,price,description,_id}, //getting the values of all the fields here. checked by console logging them.
params:{
'userid':userid
}
},
{ withCredentials: true })
.then((res) => {
console.log(res)
navigate('/listproduct')
})
.catch((err) => {
console.log(err)
})
}
Sorry if my code look really bad. I am new to React and node.
All i wanna do is edit the product details of a product by the product objectid and store it in the db.
Any other method other than this is also fine. I am stuck with this problem for more than 2 days coudn't find a solution till now. Any help is much Appreciated.
EDIT:
I have tried to update the code but still the same errors.
backend API
router.patch('/up',async (req, res) => {
try{
const id = req.query.userid;
console.log("user id is",id)
console.log("req.body",req.body.body.brandname)
const result= User.findById(id, (error, foundUser) => {
if (foundUser) {
const { products } = foundUser;
const filteredexpenses = products.filter((item) => {
return item._id!= req.body.body._id;
});
while (foundUser.products.length > 0) {
foundUser.products.pop();
}
foundUser.products = filteredexpenses;
foundUser.products.push({
brandname: req.body.body.brandname,
productname: req.body.body.productname,
quantity: req.body.body.quantity,
price: req.body.body.price,
description: req.body.body.description
});
foundUser.save();
}
else
{
console.log('User not found')
}
})
res.status(200).send("update successfull");
}
catch(err){
res.status(403).send('update unsuccessfull')
console.log(err)
}
})
Front end API
const updateproduct = async(req,res) => {
console.warn(values.brandname, values.productname,values.quantity, values.price, values.description)
console.warn("userlogin is:,",userid)
const _id=id;
await axios.patch(`http://localhost:5000/up`,{
headers: {
'Content-type': 'application/json'
},
body:
{'brandname':brandname,'productname':productname,'quantity':quantity,'price':price,'description':description,'_id':_id},
params:{
'userid':userid
}
},
{ withCredentials: true })
.then((res) => {
console.log(res)
navigate('/listproduct')
})
.catch((err) => {
console.log(err)
})
}
Result on console log is
User id is undefined
req.body adidas //brandname
User not found
Try to change your axios request passing all parameters in the body like this:
await axios
.patch(
`http://localhost:5000/up`,
{ brandname, productname, quantity, price, description, _id, userid },
{
headers: {
'Content-type': 'application/json',
},
},
{ withCredentials: true }
)
.then((res) => {
console.log(res);
navigate('/listproduct');
})
.catch((err) => {
console.log(err);
});
And handle the end point like this:
router.patch('/up', async (req, res) => {
try {
const id = req.body.userid;
User.findById(id, async (error, foundUser) => {
if (foundUser) {
const { products } = foundUser;
const { brandname, productname, quantity, price, description, _id } =
req.body;
const filteredexpenses = products.filter((item) => {
return item._id != _id;
});
while (foundUser.products.length > 0) {
foundUser.products.pop();
}
foundUser.products = filteredexpenses;
foundUser.products.push({
brandname,
productname,
quantity,
price,
description,
});
await foundUser.save();
res.status(200).send('update successfull');
} else {
console.log('User not found');
res.status(400).send('User not found');
}
});
} catch (err) {
res.status(403).send('update unsuccessfull');
console.log(err);
}
});
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 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.
I am trying to create an Inventory update-route in a MERN stack. My "Add new item" route works perfectly, but the "update route" as refused to update, can someone please tell me what I am doing wrong
//Model
const mongoose= require('mongoose');
const Schema = mongoose.Schema;
//create Schema
const ItemSchema = new Schema({
name : String,
description : String,
price : Number,
quantity : Number,
supplier : String,
taxable : Boolean,
});
module.exports=Inventory=mongoose.model('item',ItemSchema,'inventory');
Routes.js
router.post('/update', (req, res) => {
// inserting a new inventory
var _id = req.body._id;
var inventory = {
name:req.body.name,
description:req.body.description,
price:req.body.price,
quantity:req.body.quantity,
supplier:req.body.supplier,
taxable:req.body.taxable,
};
Inventory.findByIdAndUpdate(_id, { $set: inventory }, { new: true }, function (err, inventory) {
if (err) {
res.status(500);
res.send(err);
} else {
res.status(200);
res.send();
}
});
});
UpdateForm.js
class InventoryUpdateForm extends Component {
constructor(props){
super(props);
this.state = {
_id: this.props.match.params._id,
name: "",
description: "",
price: "",
quantity: "",
supplier: "",
taxable: "",
loading: true,
date: moment(new Date()).format("YYYY-MM-DD"),
};
//some code, basically binding change function with "this"
.........................
//Firstly, I get the entire data for the particular id here
componentWillMount(){
axios.get("/api/inventory/"+this.state._id)
.then(
(res) => {
var newState = {
name: res.data.name,
description: res.data.department,
price: res.data.price,
quantity:res.data.quantity,
supplier:res.data.supplier,
taxable:res.data.taxable,
};
this.setState( newState );
this.setState( { loading: false } );
},
(err) => {
alert('An error occured! Try refreshing the page.', err);
}
);
}
Here i handle the change event cases
handleDatePickerChange(date){
this.setState({ date: moment(date).format("YYYY-MM-DD") });
}
handleNameChange(event){
this.setState({ name: event.target.value });
}
handleDescriptionChange(event){
this.setState({ description: event.target.value });
}
handlePriceChange(event){
this.setState({ price: event.target.value });
handleQuantityChange(event){
this.setState({ quantity: event.target.value });
}
handleSupplierChange(event){
this.setState({ supplier: event.target.value });
}
handleTaxableChange(event){
this.setState({ taxable: event.target.value });
}
And i finally submit
submitForm(){
const { _id, name, description, price, quantity,supplier,taxable} = this.state;
var inventory = {
_id, name, description, price, quantity,supplier,taxable
};
axios.post('/update', inventory)
.then(
(res) => {
alert('Updated successfully!');
},
(err) => {
alert('An error occurred! Try submitting the form again.', err);
}
);
}
The data is retrieved, rendered and I actually update, but when I try to save it I get my error message
back
An error occurred! Try submitting the form again
How do I resolve this?
In axios to catch errors, we add a catch block.
Your submitForm must be like this, can you try and comment what happens?
submitForm() {
const { _id, name, description, price, quantity,supplier,taxable} = this.state;
var inventory = {
_id, name, description, price, quantity,supplier,taxable
};
axios.post('/update', inventory)
.then( res => {
alert('Updated successfully!');
}
)
.catch(err => {
console.log(err.response);
alert('An error occurred! Try submitting the form again.');
});
}
Also in findByIdAndUpdate, there is no need to use $set, you can simply do like this:
router.post("/update", (req, res) => {
console.log("req.body", req.body);
// inserting a new inventory
var _id = req.body._id;
var inventory = {
name: req.body.name,
description: req.body.description,
price: req.body.price,
quantity: req.body.quantity,
supplier: req.body.supplier,
taxable: req.body.taxable
};
Inventory.findByIdAndUpdate(_id, inventory, { new: true }, function(
err,
inventory
) {
if (err) {
console.log("err", err);
res.status(500).send(err);
} else {
console.log("success");
res.send(inventory);
}
});
});
Also can you set the state like this in constructor to match data types in mongoose schema.
this.state = {
_id: this.props.match.params._id,
name: "",
description: "",
price: 0,
quantity: 0,
supplier: "",
taxable: false,
loading: true,
date: moment(new Date()).format("YYYY-MM-DD"),
};
And lastly lets convert our price and quantity number by adding a plus.
handlePriceChange(event){
this.setState({ price: +event.target.value });
handleQuantityChange(event){
this.setState({ quantity: +event.target.value });
}
An alternative is to use the "PUT" method in express.
router.put('/update', (req, res) => {
//Code to update your data
})