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:
@@ -7,7 +7,6 @@ import {
|
|||||||
orderConfirmedWebhook,
|
orderConfirmedWebhook,
|
||||||
orderFulfilledWebhook,
|
orderFulfilledWebhook,
|
||||||
orderCancelledWebhook,
|
orderCancelledWebhook,
|
||||||
orderFullyPaidWebhook,
|
|
||||||
} from "./webhooks/order-notifications";
|
} from "./webhooks/order-notifications";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +36,6 @@ export default createManifestHandler({
|
|||||||
orderConfirmedWebhook.getWebhookManifest(apiBaseURL),
|
orderConfirmedWebhook.getWebhookManifest(apiBaseURL),
|
||||||
orderFulfilledWebhook.getWebhookManifest(apiBaseURL),
|
orderFulfilledWebhook.getWebhookManifest(apiBaseURL),
|
||||||
orderCancelledWebhook.getWebhookManifest(apiBaseURL),
|
orderCancelledWebhook.getWebhookManifest(apiBaseURL),
|
||||||
orderFullyPaidWebhook.getWebhookManifest(apiBaseURL),
|
|
||||||
],
|
],
|
||||||
extensions: [],
|
extensions: [],
|
||||||
author: "ManoonOils",
|
author: "ManoonOils",
|
||||||
|
|||||||
@@ -6,24 +6,14 @@ import {
|
|||||||
OrderFulfilledWebhookPayloadFragment,
|
OrderFulfilledWebhookPayloadFragment,
|
||||||
OrderCancelledSubscriptionDocument,
|
OrderCancelledSubscriptionDocument,
|
||||||
OrderCancelledWebhookPayloadFragment,
|
OrderCancelledWebhookPayloadFragment,
|
||||||
OrderFullyPaidSubscriptionDocument,
|
|
||||||
OrderFullyPaidWebhookPayloadFragment,
|
|
||||||
} from "@/generated/graphql";
|
} from "@/generated/graphql";
|
||||||
import { saleorApp } from "@/saleor-app";
|
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";
|
// N8N webhook URLs - these will forward Saleor events to N8N
|
||||||
const FROM_NAME = process.env.FROM_NAME || "ManoonOils";
|
const N8N_BASE_URL = "https://n8n.nodecrew.me/webhook";
|
||||||
const SITE_URL = process.env.SITE_URL || "https://dev.manoonoils.com";
|
|
||||||
|
|
||||||
export const orderConfirmedWebhook = new SaleorAsyncWebhook<OrderConfirmedWebhookPayloadFragment>({
|
export const orderConfirmedWebhook = new SaleorAsyncWebhook<OrderConfirmedWebhookPayloadFragment>({
|
||||||
name: "Order Confirmed",
|
name: "Order Confirmed - N8N",
|
||||||
webhookPath: "api/webhooks/order-confirmed",
|
webhookPath: "api/webhooks/order-confirmed",
|
||||||
event: "ORDER_CONFIRMED",
|
event: "ORDER_CONFIRMED",
|
||||||
apl: saleorApp.apl,
|
apl: saleorApp.apl,
|
||||||
@@ -31,7 +21,7 @@ export const orderConfirmedWebhook = new SaleorAsyncWebhook<OrderConfirmedWebhoo
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhookPayloadFragment>({
|
export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhookPayloadFragment>({
|
||||||
name: "Order Fulfilled",
|
name: "Order Fulfilled - N8N",
|
||||||
webhookPath: "api/webhooks/order-fulfilled",
|
webhookPath: "api/webhooks/order-fulfilled",
|
||||||
event: "ORDER_FULFILLED",
|
event: "ORDER_FULFILLED",
|
||||||
apl: saleorApp.apl,
|
apl: saleorApp.apl,
|
||||||
@@ -39,101 +29,80 @@ export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhoo
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const orderCancelledWebhook = new SaleorAsyncWebhook<OrderCancelledWebhookPayloadFragment>({
|
export const orderCancelledWebhook = new SaleorAsyncWebhook<OrderCancelledWebhookPayloadFragment>({
|
||||||
name: "Order Cancelled",
|
name: "Order Cancelled - N8N",
|
||||||
webhookPath: "api/webhooks/order-cancelled",
|
webhookPath: "api/webhooks/order-cancelled",
|
||||||
event: "ORDER_CANCELLED",
|
event: "ORDER_CANCELLED",
|
||||||
apl: saleorApp.apl,
|
apl: saleorApp.apl,
|
||||||
query: OrderCancelledSubscriptionDocument,
|
query: OrderCancelledSubscriptionDocument,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orderFullyPaidWebhook = new SaleorAsyncWebhook<OrderFullyPaidWebhookPayloadFragment>({
|
// Forward webhook payload to N8N
|
||||||
name: "Order Fully Paid",
|
async function forwardToN8N(eventType: string, payload: any) {
|
||||||
webhookPath: "api/webhooks/order-fully-paid",
|
const n8nUrl = `${N8N_BASE_URL}/saleor-${eventType}`;
|
||||||
event: "ORDER_FULLY_PAID",
|
|
||||||
apl: saleorApp.apl,
|
console.log(`Forwarding ${eventType} to N8N: ${n8nUrl}`);
|
||||||
query: OrderFullyPaidSubscriptionDocument,
|
|
||||||
});
|
try {
|
||||||
|
const response = await fetch(n8nUrl, {
|
||||||
function getTrackingInfo(order: any) {
|
method: "POST",
|
||||||
const trackingMeta = order.metadata?.find((m: any) => m.key === "trackingNumber");
|
headers: {
|
||||||
const trackingUrlMeta = order.metadata?.find((m: any) => m.key === "trackingUrl");
|
"Content-Type": "application/json",
|
||||||
return {
|
"x-saleor-event": eventType,
|
||||||
trackingNumber: trackingMeta?.value,
|
},
|
||||||
trackingUrl: trackingUrlMeta?.value,
|
body: JSON.stringify(payload),
|
||||||
};
|
});
|
||||||
}
|
|
||||||
|
if (!response.ok) {
|
||||||
function getCancellationReason(order: any) {
|
console.error(`N8N returned ${response.status}: ${await response.text()}`);
|
||||||
const reasonMeta = order.metadata?.find((m: any) => m.key === "cancellationReason");
|
throw new Error(`N8N returned ${response.status}`);
|
||||||
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) => {
|
export const orderConfirmedHandler = orderConfirmedWebhook.createHandler(async (req, res, ctx) => {
|
||||||
const { payload } = ctx;
|
const { payload } = ctx;
|
||||||
|
|
||||||
console.log(`Order confirmed: ${payload.order?.id}`);
|
console.log(`Order confirmed received: ${payload.order?.id}`);
|
||||||
|
|
||||||
if (payload.order) {
|
try {
|
||||||
try {
|
await forwardToN8N("order.created", payload);
|
||||||
await sendOrderConfirmationEmail(payload.order);
|
return res.status(200).end();
|
||||||
await sendAdminNotification(payload.order, "ORDER_CONFIRMED");
|
} catch (error) {
|
||||||
} catch (error) {
|
console.error("Failed to forward order confirmed:", error);
|
||||||
console.error("Failed to send confirmation email:", error);
|
return res.status(500).end();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).end();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orderFulfilledHandler = orderFulfilledWebhook.createHandler(async (req, res, ctx) => {
|
export const orderFulfilledHandler = orderFulfilledWebhook.createHandler(async (req, res, ctx) => {
|
||||||
const { payload } = ctx;
|
const { payload } = ctx;
|
||||||
|
|
||||||
console.log(`Order fulfilled: ${payload.order?.id}`);
|
console.log(`Order fulfilled received: ${payload.order?.id}`);
|
||||||
|
|
||||||
if (payload.order) {
|
try {
|
||||||
const { trackingNumber, trackingUrl } = getTrackingInfo(payload.order);
|
await forwardToN8N("order.fulfilled", payload);
|
||||||
try {
|
return res.status(200).end();
|
||||||
await sendOrderShippedEmail(payload.order, trackingNumber, trackingUrl);
|
} catch (error) {
|
||||||
await sendAdminNotification(payload.order, "ORDER_FULFILLED");
|
console.error("Failed to forward order fulfilled:", error);
|
||||||
} catch (error) {
|
return res.status(500).end();
|
||||||
console.error("Failed to send shipping email:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).end();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orderCancelledHandler = orderCancelledWebhook.createHandler(async (req, res, ctx) => {
|
export const orderCancelledHandler = orderCancelledWebhook.createHandler(async (req, res, ctx) => {
|
||||||
const { payload } = ctx;
|
const { payload } = ctx;
|
||||||
|
|
||||||
console.log(`Order cancelled: ${payload.order?.id}`);
|
console.log(`Order cancelled received: ${payload.order?.id}`);
|
||||||
|
|
||||||
if (payload.order) {
|
try {
|
||||||
const reason = getCancellationReason(payload.order);
|
await forwardToN8N("order.cancelled", payload);
|
||||||
try {
|
return res.status(200).end();
|
||||||
await sendOrderCancelledEmail(payload.order, reason);
|
} catch (error) {
|
||||||
await sendAdminNotification(payload.order, "ORDER_CANCELLED");
|
console.error("Failed to forward order cancelled:", error);
|
||||||
} catch (error) {
|
return res.status(500).end();
|
||||||
console.error("Failed to send cancellation email:", error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(200).end();
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user