feat: implement Phase 1 LINE Message Integration (Order Created, Payment Confirmed, Shipped, Cancelled)

This commit is contained in:
Your Name committed 2026-06-13 00:23:21 -04:00
1 parent 852d9c1925
commit 59363616c4
4 files changed
+630 -34

No files matched your search

+2 -2
View File
@@ -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}`
+52 -24
View File
@@ -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" };
+3 -8
View File
@@ -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);
}
}