File size: 5,514 Bytes
1067b6f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | import { addresses, products } from "./../../../../db/schema";
import { db } from "@/db/db";
import { carts, orders, payments } from "@/db/schema";
import { CheckoutItem } from "@/lib/types";
import { SQL, eq, inArray, sql } from "drizzle-orm";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { Readable } from "stream";
import Stripe from "stripe";
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
async function getRawBody(readable: Readable): Promise<Buffer> {
const chunks = [];
for await (const chunk of readable) {
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
}
return Buffer.concat(chunks);
}
export async function POST(request: Request) {
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2022-11-15",
});
const rawBody = await getRawBody(request.body as unknown as Readable);
const headersList = headers();
const sig = headersList.get("stripe-signature");
let event;
try {
event = stripe.webhooks.constructEvent(
rawBody,
sig as string,
endpointSecret as string
);
} catch (err: any) {
return NextResponse.json(
{ error: `Webhook Error: ${err.message}` },
{ status: 400 }
);
}
let dbUpdateCartResponse;
// Handle the event
switch (event.type) {
case "payment_intent.payment_failed":
const paymentIntentPaymentFailed = event.data.object;
// Then define and call a function to handle the event payment_intent.payment_failed
break;
case "payment_intent.processing":
const paymentIntentProcessing = event.data.object;
// Then define and call a function to handle the event payment_intent.processing
break;
case "payment_intent.succeeded":
const paymentIntentSucceeded = event.data.object;
// Then define and call a function to handle the event payment_intent.succeeded
// Mark cart as closed in DB
const stripeObject = event?.data?.object as {
id: string;
amount: string;
metadata: {
cartId: string;
items: string;
};
shipping: {
name: string;
address: {
line1: string;
line2: string;
city: string;
state: string;
postal_code: string;
country: string;
};
};
receipt_email: string;
status: string;
};
const paymentIntentId = stripeObject?.id;
const orderTotal = stripeObject?.amount;
try {
if (!event.account) throw new Error("No account on event");
const store = await db
.select({
storeId: payments.storeId,
})
.from(payments)
.where(eq(payments.stripeAccountId, event.account));
const storeId = store[0].storeId as number;
// create new address in DB
const stripeAddress = stripeObject?.shipping?.address;
const newAddress = await db.insert(addresses).values({
line1: stripeAddress?.line1,
line2: stripeAddress?.line2,
city: stripeAddress?.city,
state: stripeAddress?.state,
postal_code: stripeAddress?.postal_code,
country: stripeAddress?.country,
});
if (!newAddress[0].insertId) throw new Error("No address created");
// get current order count in DB
const storeOrderCount = await db
.select({ count: sql<number>`count(*)` })
.from(orders)
.where(eq(orders.storeId, storeId));
// create new order in DB
const newOrder = await db.insert(orders).values({
prettyOrderId: Number(storeOrderCount[0].count) + 1,
storeId: storeId,
items: stripeObject.metadata?.items,
total: String(Number(orderTotal) / 100),
stripePaymentIntentId: paymentIntentId,
stripePaymentIntentStatus: stripeObject?.status,
name: stripeObject?.shipping?.name,
email: stripeObject?.receipt_email,
createdAt: event.created,
addressId: Number(newAddress[0].insertId),
});
console.log("ORDER CREATED", newOrder);
} catch (err) {
console.log("ORDER CREATION WEBHOOK ERROR", err);
}
// update inventory from DB
// try {
// const orderedItems = JSON.parse(
// stripeObject.metadata?.items
// ) as CheckoutItem[];
// for (let index in orderedItems) {
// orderedItems[index].id;
// }
// orderedItems.forEach((item) => item.id);
// // UPDATE products SET inventory = inventory - 1 WHERE id = $1 or id = $2 or id = $3 or id = $4 or id = $5; --> [0, 1, 2, 3, 4]
// } catch (err) {
// console.log("INVENTORY UPDATE WEBHOOK ERROR", err);
// }
try {
// Close cart and clear items
dbUpdateCartResponse = await db
.update(carts)
.set({
isClosed: true,
items: JSON.stringify([]),
})
.where(eq(carts.paymentIntentId, paymentIntentId));
} catch (err) {
console.log("WEBHOOK ERROR", err);
return NextResponse.json(
{ response: dbUpdateCartResponse, error: err },
{ status: 500 }
);
}
break;
// ... handle other event types
default:
console.log(`Unhandled event type ${event.type}`);
}
return NextResponse.json({ status: 200 });
}
|