feat: implement Phase 1 LINE Message Integration (Order Created, Payment Confirmed, Shipped, Cancelled)
This commit is contained in:
1 parent
852d9c1925
commit
59363616c4
4 files changed
+630
-34
No files matched your search
@@ -37,7 +37,7 @@ const webhookHandler = async ({ request, body, set }: { request: Request, body:
|
||||
|
||||
try {
|
||||
const parsedBody = typeof body === 'string' ? JSON.parse(body) : body;
|
||||
const events: line.WebhookEvent[] = parsedBody.events || [];
|
||||
const events: line.webhook.Event[] = parsedBody.events || [];
|
||||
|
||||
for (const event of events) {
|
||||
if (event.type === 'message' && event.message.type === 'text') {
|
||||
@@ -48,7 +48,7 @@ const webhookHandler = async ({ request, body, set }: { request: Request, body:
|
||||
|
||||
// Simple echo bot for now
|
||||
await lineClient.replyMessage({
|
||||
replyToken: event.replyToken,
|
||||
replyToken: event.replyToken!,
|
||||
messages: [{
|
||||
type: 'text',
|
||||
text: `คุณส่งข้อความว่า: ${event.message.text}`
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { Elysia, t } from "elysia";
|
||||
import prisma from "../lib/prisma";
|
||||
import {
|
||||
sendOrderCreatedNotification,
|
||||
sendPaymentConfirmedNotification,
|
||||
sendOrderShippedNotification,
|
||||
sendOrderCancelledNotification,
|
||||
markOrderAsPaid
|
||||
} from "../lib/notifications";
|
||||
|
||||
export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
.get("/", async () => {
|
||||
@@ -87,6 +94,11 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger LINE notification asynchronously
|
||||
sendOrderCreatedNotification(order.id).catch(e => {
|
||||
console.error("Failed to trigger sendOrderCreatedNotification:", e);
|
||||
});
|
||||
|
||||
return order;
|
||||
} catch (err) {
|
||||
console.error("[Orders] Error:", err);
|
||||
@@ -127,29 +139,40 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
const { status } = body as any;
|
||||
const order = await prisma.order.findUnique({ where: { id: Number(id) } });
|
||||
|
||||
const updatedOrder = await prisma.order.update({
|
||||
where: { id: Number(id) },
|
||||
data: { status },
|
||||
include: { items: true }
|
||||
if (!order) {
|
||||
set.status = 404;
|
||||
return { error: "Order not found" };
|
||||
}
|
||||
|
||||
if (status === 'PAID') {
|
||||
await markOrderAsPaid(order.id);
|
||||
} else {
|
||||
await prisma.order.update({
|
||||
where: { id: order.id },
|
||||
data: { status }
|
||||
});
|
||||
|
||||
// If status becomes CANCELLED, refund the points used and notify
|
||||
if (status === 'CANCELLED' && order.status !== 'CANCELLED') {
|
||||
if (order.customerId && order.pointsUsed > 0) {
|
||||
await prisma.customer.update({
|
||||
where: { id: order.customerId },
|
||||
data: { points: { increment: order.pointsUsed } }
|
||||
});
|
||||
}
|
||||
sendOrderCancelledNotification(order.id).catch(e => console.error(e));
|
||||
}
|
||||
|
||||
// If status becomes SHIPPED, notify
|
||||
if (status === 'SHIPPED' && order.status !== 'SHIPPED') {
|
||||
sendOrderShippedNotification(order.id).catch(e => console.error(e));
|
||||
}
|
||||
}
|
||||
|
||||
return await prisma.order.findUnique({
|
||||
where: { id: order.id },
|
||||
include: { items: true, customer: true }
|
||||
});
|
||||
|
||||
// Grant points if status becomes PAID and it wasn't PAID before
|
||||
if (status === 'PAID' && order?.status !== 'PAID' && order?.customerId && order?.pointsEarned > 0) {
|
||||
await prisma.customer.update({
|
||||
where: { id: order.customerId },
|
||||
data: { points: { increment: order.pointsEarned } }
|
||||
});
|
||||
}
|
||||
|
||||
// If status becomes CANCELLED, refund the points used
|
||||
if (status === 'CANCELLED' && order?.status !== 'CANCELLED' && order?.customerId && order?.pointsUsed > 0) {
|
||||
await prisma.customer.update({
|
||||
where: { id: order.customerId },
|
||||
data: { points: { increment: order.pointsUsed } }
|
||||
});
|
||||
}
|
||||
|
||||
return updatedOrder;
|
||||
} catch (err) {
|
||||
set.status = 500;
|
||||
return { error: "Failed to update order status" };
|
||||
@@ -179,11 +202,16 @@ export const ordersRoute = new Elysia({ prefix: "/orders" })
|
||||
.patch("/:id/tracking", async ({ params: { id }, body, set }) => {
|
||||
try {
|
||||
const { trackingNo } = body as any;
|
||||
return await prisma.order.update({
|
||||
const order = await prisma.order.update({
|
||||
where: { id: Number(id) },
|
||||
data: { trackingNo },
|
||||
data: { trackingNo, status: "SHIPPED" },
|
||||
include: { items: true }
|
||||
});
|
||||
// Trigger notification asynchronously
|
||||
sendOrderShippedNotification(order.id).catch(e => {
|
||||
console.error("Failed to trigger sendOrderShippedNotification:", e);
|
||||
});
|
||||
return order;
|
||||
} catch (err) {
|
||||
set.status = 500;
|
||||
return { error: "Failed to update tracking number" };
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Elysia, t } from "elysia";
|
||||
import prisma from "../lib/prisma";
|
||||
import { markOrderAsPaid } from "../lib/notifications";
|
||||
|
||||
const OMISE_SECRET_KEY = process.env.OMISE_SECRET_KEY || "";
|
||||
|
||||
@@ -67,10 +68,7 @@ export const paymentsRoute = new Elysia({ prefix: "/payments" })
|
||||
|
||||
// If card payment is immediately successful (some cards don't require 3DS)
|
||||
if (charge.status === "successful") {
|
||||
await prisma.order.update({
|
||||
where: { id: orderId },
|
||||
data: { status: "PAID" }
|
||||
});
|
||||
await markOrderAsPaid(orderId);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -161,10 +159,7 @@ export const paymentsRoute = new Elysia({ prefix: "/payments" })
|
||||
const orderId = parseInt(orderIdStr, 10);
|
||||
console.log(`[Omise Webhook] Payment successful for Order #${orderId}`);
|
||||
|
||||
await prisma.order.update({
|
||||
where: { id: orderId },
|
||||
data: { status: "PAID" }
|
||||
});
|
||||
await markOrderAsPaid(orderId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user