feat: forward order webhooks to N8N instead of sending emails directly

- Modified order-notifications.ts to forward webhooks to N8N
- Updated webhook handlers for ORDER_CONFIRMED, ORDER_FULFILLED, ORDER_CANCELLED
- Removed direct email sending via Resend
- Webhooks now forward to https://n8n.nodecrew.me/webhook/saleor-{event}
- Each event type gets forwarded with appropriate x-saleor-event header
This commit is contained in:
Unchained
2026-03-27 15:13:52 +02:00
parent bd8d941b1c
commit a2601a3d3c
2 changed files with 53 additions and 86 deletions
-2
View File
@@ -7,7 +7,6 @@ import {
orderConfirmedWebhook,
orderFulfilledWebhook,
orderCancelledWebhook,
orderFullyPaidWebhook,
} from "./webhooks/order-notifications";
/**
@@ -37,7 +36,6 @@ export default createManifestHandler({
orderConfirmedWebhook.getWebhookManifest(apiBaseURL),
orderFulfilledWebhook.getWebhookManifest(apiBaseURL),
orderCancelledWebhook.getWebhookManifest(apiBaseURL),
orderFullyPaidWebhook.getWebhookManifest(apiBaseURL),
],
extensions: [],
author: "ManoonOils",
+44 -75
View File
@@ -6,24 +6,14 @@ import {
OrderFulfilledWebhookPayloadFragment,
OrderCancelledSubscriptionDocument,
OrderCancelledWebhookPayloadFragment,
OrderFullyPaidSubscriptionDocument,
OrderFullyPaidWebhookPayloadFragment,
} from "@/generated/graphql";
import { saleorApp } from "@/saleor-app";
import {
sendOrderConfirmationEmail,
sendOrderShippedEmail,
sendOrderCancelledEmail,
sendOrderPaidEmail,
sendAdminNotification,
} from "@/lib/resend";
const FROM_EMAIL = process.env.FROM_EMAIL || "support@mail.manoonoils.com";
const FROM_NAME = process.env.FROM_NAME || "ManoonOils";
const SITE_URL = process.env.SITE_URL || "https://dev.manoonoils.com";
// N8N webhook URLs - these will forward Saleor events to N8N
const N8N_BASE_URL = "https://n8n.nodecrew.me/webhook";
export const orderConfirmedWebhook = new SaleorAsyncWebhook<OrderConfirmedWebhookPayloadFragment>({
name: "Order Confirmed",
name: "Order Confirmed - N8N",
webhookPath: "api/webhooks/order-confirmed",
event: "ORDER_CONFIRMED",
apl: saleorApp.apl,
@@ -31,7 +21,7 @@ export const orderConfirmedWebhook = new SaleorAsyncWebhook<OrderConfirmedWebhoo
});
export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhookPayloadFragment>({
name: "Order Fulfilled",
name: "Order Fulfilled - N8N",
webhookPath: "api/webhooks/order-fulfilled",
event: "ORDER_FULFILLED",
apl: saleorApp.apl,
@@ -39,101 +29,80 @@ export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhoo
});
export const orderCancelledWebhook = new SaleorAsyncWebhook<OrderCancelledWebhookPayloadFragment>({
name: "Order Cancelled",
name: "Order Cancelled - N8N",
webhookPath: "api/webhooks/order-cancelled",
event: "ORDER_CANCELLED",
apl: saleorApp.apl,
query: OrderCancelledSubscriptionDocument,
});
export const orderFullyPaidWebhook = new SaleorAsyncWebhook<OrderFullyPaidWebhookPayloadFragment>({
name: "Order Fully Paid",
webhookPath: "api/webhooks/order-fully-paid",
event: "ORDER_FULLY_PAID",
apl: saleorApp.apl,
query: OrderFullyPaidSubscriptionDocument,
// Forward webhook payload to N8N
async function forwardToN8N(eventType: string, payload: any) {
const n8nUrl = `${N8N_BASE_URL}/saleor-${eventType}`;
console.log(`Forwarding ${eventType} to N8N: ${n8nUrl}`);
try {
const response = await fetch(n8nUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-saleor-event": eventType,
},
body: JSON.stringify(payload),
});
function getTrackingInfo(order: any) {
const trackingMeta = order.metadata?.find((m: any) => m.key === "trackingNumber");
const trackingUrlMeta = order.metadata?.find((m: any) => m.key === "trackingUrl");
return {
trackingNumber: trackingMeta?.value,
trackingUrl: trackingUrlMeta?.value,
};
if (!response.ok) {
console.error(`N8N returned ${response.status}: ${await response.text()}`);
throw new Error(`N8N returned ${response.status}`);
}
function getCancellationReason(order: any) {
const reasonMeta = order.metadata?.find((m: any) => m.key === "cancellationReason");
return reasonMeta?.value;
console.log(`Successfully forwarded ${eventType} to N8N`);
return true;
} catch (error) {
console.error(`Failed to forward ${eventType} to N8N:`, error);
throw error;
}
}
export const orderConfirmedHandler = orderConfirmedWebhook.createHandler(async (req, res, ctx) => {
const { payload } = ctx;
console.log(`Order confirmed: ${payload.order?.id}`);
console.log(`Order confirmed received: ${payload.order?.id}`);
if (payload.order) {
try {
await sendOrderConfirmationEmail(payload.order);
await sendAdminNotification(payload.order, "ORDER_CONFIRMED");
} catch (error) {
console.error("Failed to send confirmation email:", error);
}
}
await forwardToN8N("order.created", payload);
return res.status(200).end();
} catch (error) {
console.error("Failed to forward order confirmed:", error);
return res.status(500).end();
}
});
export const orderFulfilledHandler = orderFulfilledWebhook.createHandler(async (req, res, ctx) => {
const { payload } = ctx;
console.log(`Order fulfilled: ${payload.order?.id}`);
console.log(`Order fulfilled received: ${payload.order?.id}`);
if (payload.order) {
const { trackingNumber, trackingUrl } = getTrackingInfo(payload.order);
try {
await sendOrderShippedEmail(payload.order, trackingNumber, trackingUrl);
await sendAdminNotification(payload.order, "ORDER_FULFILLED");
} catch (error) {
console.error("Failed to send shipping email:", error);
}
}
await forwardToN8N("order.fulfilled", payload);
return res.status(200).end();
} catch (error) {
console.error("Failed to forward order fulfilled:", error);
return res.status(500).end();
}
});
export const orderCancelledHandler = orderCancelledWebhook.createHandler(async (req, res, ctx) => {
const { payload } = ctx;
console.log(`Order cancelled: ${payload.order?.id}`);
console.log(`Order cancelled received: ${payload.order?.id}`);
if (payload.order) {
const reason = getCancellationReason(payload.order);
try {
await sendOrderCancelledEmail(payload.order, reason);
await sendAdminNotification(payload.order, "ORDER_CANCELLED");
} catch (error) {
console.error("Failed to send cancellation email:", error);
}
}
await forwardToN8N("order.cancelled", payload);
return res.status(200).end();
});
export const orderFullyPaidHandler = orderFullyPaidWebhook.createHandler(async (req, res, ctx) => {
const { payload } = ctx;
console.log(`Order fully paid: ${payload.order?.id}`);
if (payload.order) {
try {
await sendOrderPaidEmail(payload.order);
await sendAdminNotification(payload.order, "ORDER_FULLY_PAID");
} catch (error) {
console.error("Failed to send payment email:", error);
console.error("Failed to forward order cancelled:", error);
return res.status(500).end();
}
}
return res.status(200).end();
});