created checkout route and connected with Stripe to checkout order
This commit is contained in:
parent
0b6516e19d
commit
47f9e9d371
5 changed files with 161 additions and 27 deletions
45
donutshop_ecommerce/package-lock.json
generated
45
donutshop_ecommerce/package-lock.json
generated
|
@ -23,6 +23,7 @@
|
|||
"react-scroll-parallax": "^3.4.5",
|
||||
"react-slick": "^0.30.2",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"stripe": "^15.8.0",
|
||||
"uniqid": "^5.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -2722,7 +2723,6 @@
|
|||
"version": "20.12.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
|
||||
"integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
|
@ -3313,7 +3313,6 @@
|
|||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
|
@ -3602,7 +3601,6 @@
|
|||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
|
@ -3784,7 +3782,6 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.4"
|
||||
},
|
||||
|
@ -3796,7 +3793,6 @@
|
|||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
|
@ -4532,7 +4528,6 @@
|
|||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
|
@ -4610,7 +4605,6 @@
|
|||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
|
@ -4775,7 +4769,6 @@
|
|||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.3"
|
||||
},
|
||||
|
@ -4816,7 +4809,6 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0"
|
||||
},
|
||||
|
@ -4828,7 +4820,6 @@
|
|||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
|
||||
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
|
@ -4840,7 +4831,6 @@
|
|||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
|
@ -4872,7 +4862,6 @@
|
|||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
|
@ -6074,7 +6063,6 @@
|
|||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
|
@ -6608,6 +6596,20 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.12.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
|
||||
"integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
|
@ -6991,7 +6993,6 @@
|
|||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.4",
|
||||
"es-errors": "^1.3.0",
|
||||
|
@ -7044,7 +7045,6 @@
|
|||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
||||
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.7",
|
||||
"es-errors": "^1.3.0",
|
||||
|
@ -7314,6 +7314,18 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/stripe": {
|
||||
"version": "15.8.0",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-15.8.0.tgz",
|
||||
"integrity": "sha512-7eEPMgehd1I16cXeP7Rcn/JKkPWIadB9vGIeE+vbCzQXaY5R95AoNmkZx0vmlu1H4QIDs7j1pYIKPRm9Dr4LKg==",
|
||||
"dependencies": {
|
||||
"@types/node": ">=8.1.0",
|
||||
"qs": "^6.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.*"
|
||||
}
|
||||
},
|
||||
"node_modules/strnum": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
|
||||
|
@ -7671,8 +7683,7 @@
|
|||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
|
||||
},
|
||||
"node_modules/uniqid": {
|
||||
"version": "5.4.0",
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
"react-scroll-parallax": "^3.4.5",
|
||||
"react-slick": "^0.30.2",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"stripe": "^15.8.0",
|
||||
"uniqid": "^5.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
77
donutshop_ecommerce/src/app/api/checkout/route.js
Normal file
77
donutshop_ecommerce/src/app/api/checkout/route.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
import mongoose from "mongoose";
|
||||
import { getServerSession } from "next-auth"
|
||||
import { authOptions } from "../auth/[...nextauth]/route"
|
||||
import { Order } from "../models/Order";
|
||||
import { MenuItem } from "../models/MenuItem"
|
||||
const stripe = require('stripe')(process.env.STRIPE_SK);
|
||||
|
||||
|
||||
export async function POST(req){
|
||||
mongoose.connect(process.env.MONGO_URL);
|
||||
|
||||
const {address ,cartProducts} = await req.json();
|
||||
const session = await getServerSession(authOptions);
|
||||
const userEmail = session?.user?.email;
|
||||
|
||||
const orderDoc = await Order.create({
|
||||
userEmail,
|
||||
...address,
|
||||
cartProducts,
|
||||
paid: false,
|
||||
})
|
||||
|
||||
const stripeLineItems = [];
|
||||
for(const cartProduct of cartProducts){
|
||||
|
||||
const productInfo = await MenuItem.findById(cartProduct._id);
|
||||
let productPrice = productInfo.basePrice;
|
||||
if(cartProduct.size){
|
||||
const size = productInfo.sizes.find(size => size._id.toString() === cartProduct.size._id.toString());
|
||||
productPrice += size.price;
|
||||
}
|
||||
if(cartProduct.extra?.length > 0){
|
||||
for(const cartProductExtra of cartProduct.extra){
|
||||
const productExtra = productInfo.extraItems;
|
||||
const extraToppingInfo = productExtra.find(extras => extras._id.toString() === cartProductExtra._id.toString())
|
||||
productPrice += extraToppingInfo.price
|
||||
}
|
||||
}
|
||||
|
||||
const productName = cartProduct.itemName
|
||||
|
||||
stripeLineItems.push({
|
||||
quantity: 1,
|
||||
price_data: {
|
||||
currency: 'USD',
|
||||
product_data: {
|
||||
name: productName,
|
||||
},
|
||||
unit_amount: productPrice * 100,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const stripeSession = await stripe.checkout.sessions.create({
|
||||
line_items: stripeLineItems,
|
||||
mode: 'payment',
|
||||
customer_email: userEmail,
|
||||
success_url: process.env.NEXTAUTH_URL + 'orders/' + orderDoc._id.toString() + '?clear-cart=1',
|
||||
cancel_url: process.env.NEXTAUTH_URL + 'cart?canceled=1',
|
||||
metadata: {orderId: orderDoc._id.toString()},
|
||||
payment_intent_data: {
|
||||
metadata:{orderId:orderDoc._id.toString()},
|
||||
},
|
||||
shipping_options: [
|
||||
{
|
||||
shipping_rate_data: {
|
||||
display_name: 'Delivery fee',
|
||||
type: 'fixed_amount',
|
||||
fixed_amount: {amount: 500, currency: 'USD'},
|
||||
},
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
return Response.json(stripeSession.url);
|
||||
}
|
35
donutshop_ecommerce/src/app/api/models/Order.js
Normal file
35
donutshop_ecommerce/src/app/api/models/Order.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { Schema, model, models } from "mongoose";
|
||||
|
||||
const OrderSchema = new Schema({
|
||||
userEmail: {
|
||||
type: String
|
||||
},
|
||||
phoneNumber:{
|
||||
type: String,
|
||||
},
|
||||
streetAddress:{
|
||||
type: String,
|
||||
},
|
||||
city:{
|
||||
type: String,
|
||||
},
|
||||
stateProvince:{
|
||||
type: String,
|
||||
},
|
||||
zipCode:{
|
||||
type: String,
|
||||
},
|
||||
country:{
|
||||
type: String,
|
||||
},
|
||||
cartProducts: {
|
||||
type: Object,
|
||||
},
|
||||
paid: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
}, {timestamps: true})
|
||||
|
||||
export const Order = models?.Order || model('Order', OrderSchema);
|
|
@ -18,23 +18,33 @@ const CartPage = () => {
|
|||
setAddress(addressFormProfile)
|
||||
}
|
||||
}, [profileCheckoutData])
|
||||
|
||||
//cart total
|
||||
let subtotal = 0
|
||||
for(const p of cartProducts){
|
||||
subtotal += productTotal(p)
|
||||
}
|
||||
|
||||
|
||||
|
||||
function handleAddresschange(propName, value){
|
||||
setAddress(prevAddress => ({...prevAddress, [propName]:value}));
|
||||
}
|
||||
|
||||
//checkout to Stripe
|
||||
function handleCheckout(ev){
|
||||
fetch('/api/checkout', {
|
||||
|
||||
})
|
||||
async function handleCheckout(ev){
|
||||
ev.preventDefault();
|
||||
//info and cart products
|
||||
const response = await fetch('/api/checkout', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type':'application/json'},
|
||||
body: JSON.stringify({address, cartProducts,}),
|
||||
});
|
||||
window.location = await response.json()
|
||||
}
|
||||
console.log({cartProducts})
|
||||
|
||||
//cart total
|
||||
let subtotal = 0
|
||||
for(const p of cartProducts){
|
||||
subtotal += productTotal(p)
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
|
@ -96,7 +106,7 @@ const CartPage = () => {
|
|||
<p className='uppercase font-semibold mb-5 text-center'>Shipping Address</p>
|
||||
<form onSubmit={handleCheckout} className='border-t py-5'>
|
||||
<AddressInfo addressProps={address} setAddressProps={handleAddresschange}/>
|
||||
<button type='button' className='w-full rounded-md bg-pink-500 text-white hover:opacity-80 duration-300 p-2 block'>Process to pay</button>
|
||||
<button type='submit' className='w-full rounded-md bg-pink-500 text-white hover:opacity-80 duration-300 p-2 block'>Process to pay</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Reference in a new issue