fix: update order-fulfilled and order-cancelled webhooks to forward to N8N

- Update GraphQL fragments to include languageCode and all necessary fields
- Modify order-fulfilled.ts to forward to N8N instead of sending directly
- Modify order-cancelled.ts to forward to N8N instead of sending directly
- Regenerate GraphQL types with full order data
- Enable multi-language emails for shipped and cancelled orders
This commit is contained in:
Unchained
2026-03-28 13:58:29 +02:00
parent 2065b24d7a
commit 1089f03ee3
7 changed files with 1080 additions and 116 deletions
+176 -20
View File
File diff suppressed because one or more lines are too long
+79 -2
View File
@@ -2,12 +2,89 @@ fragment OrderCancelledWebhookPayload on OrderCancelled {
order {
id
number
created
status
userEmail
languageCode
channel {
slug
}
user {
email
firstName
lastName
}
status
shippingAddress {
firstName
lastName
streetAddress1
streetAddress2
city
postalCode
country {
country
}
phone
}
billingAddress {
firstName
lastName
streetAddress1
streetAddress2
city
postalCode
country {
country
}
phone
}
lines {
id
quantity
unitPrice {
gross {
amount
currency
}
}
totalPrice {
gross {
amount
currency
}
}
variant {
name
sku
product {
name
media {
url
}
}
}
}
subtotal {
gross {
amount
currency
}
}
shippingPrice {
gross {
amount
currency
}
}
total {
gross {
amount
currency
}
}
metadata {
key
value
}
}
}
}
+4
View File
@@ -82,5 +82,9 @@ fragment OrderCreatedWebhookPayload on OrderCreated {
currency
}
}
metadata {
key
value
}
}
}
+80 -2
View File
@@ -2,17 +2,95 @@ fragment OrderFulfilledWebhookPayload on OrderFulfilled {
order {
id
number
created
status
userEmail
languageCode
channel {
slug
}
user {
email
firstName
lastName
}
status
shippingAddress {
firstName
lastName
streetAddress1
streetAddress2
city
postalCode
country {
country
}
phone
}
billingAddress {
firstName
lastName
streetAddress1
streetAddress2
city
postalCode
country {
country
}
phone
}
lines {
id
quantity
unitPrice {
gross {
amount
currency
}
}
totalPrice {
gross {
amount
currency
}
}
variant {
name
sku
product {
name
media {
url
}
}
}
}
subtotal {
gross {
amount
currency
}
}
shippingPrice {
gross {
amount
currency
}
}
total {
gross {
amount
currency
}
}
metadata {
key
value
}
fulfillments {
id
status
created
trackingNumber
}
}
}
}
+697 -25
View File
File diff suppressed because it is too large Load Diff
+22 -34
View File
@@ -4,7 +4,9 @@ import {
OrderCancelledWebhookPayloadFragment,
} from "@/generated/graphql";
import { saleorApp } from "@/saleor-app";
import { sendOrderCancelledEmail, formatPrice } from "@/lib/resend";
// N8N webhook URL for Order Cancelled
const N8N_WEBHOOK_URL = "https://n8n.nodecrew.me/webhook/saleor-order-cancelled";
export const orderCancelledWebhook = new SaleorAsyncWebhook<OrderCancelledWebhookPayloadFragment>({
name: "Order Cancelled in Saleor",
@@ -15,43 +17,29 @@ export const orderCancelledWebhook = new SaleorAsyncWebhook<OrderCancelledWebhoo
});
export default orderCancelledWebhook.createHandler(async (req, res, ctx) => {
const { payload, event, baseUrl, authData } = ctx;
const order = payload.order;
if (!order) {
console.error("No order data in webhook payload");
return res.status(200).end();
}
const { payload, event, authData } = ctx;
console.log(`Order ${order.number} cancelled for customer: ${order.userEmail}`);
const items = ((order as any).lines || []).map((line: any) => ({
id: line.id,
name: line.variant?.product?.name || "Unknown Product",
quantity: line.quantity,
price: formatPrice(line.totalPrice?.gross?.amount || 0, line.totalPrice?.gross?.currency || "USD"),
}));
const orderData = {
orderId: order.id,
orderNumber: order.number || "Unknown",
customerName: (order as any).shippingAddress?.firstName
? `${(order as any).shippingAddress.firstName} ${(order as any).shippingAddress.lastName || ""}`.trim()
: order.userEmail?.split("@")[0] || "Customer",
items,
total: formatPrice((order as any).total?.gross?.amount || 0, (order as any).total?.gross?.currency || "USD"),
};
console.log(`Order cancelled: ${payload.order?.number} for ${payload.order?.userEmail}`);
console.log(`Forwarding to N8N: ${N8N_WEBHOOK_URL}`);
try {
if (order.userEmail) {
await sendOrderCancelledEmail({
to: order.userEmail,
orderData,
});
console.log(`Customer notification sent for cancelled order ${order.number}`);
// Forward to N8N
const response = await fetch(N8N_WEBHOOK_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-saleor-event": "order.cancelled",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
console.error(`N8N returned ${response.status}: ${await response.text()}`);
} else {
console.log(`Successfully forwarded to N8N: ${response.status}`);
}
} catch (error) {
console.error("Failed to send email:", error);
console.error(`Failed to forward to N8N:`, error);
}
return res.status(200).end();
@@ -61,4 +49,4 @@ export const config = {
api: {
bodyParser: false,
},
};
};
+22 -33
View File
@@ -4,7 +4,9 @@ import {
OrderFulfilledWebhookPayloadFragment,
} from "@/generated/graphql";
import { saleorApp } from "@/saleor-app";
import { sendOrderShippedEmail, formatPrice } from "@/lib/resend";
// N8N webhook URL for Order Fulfilled/Shipped
const N8N_WEBHOOK_URL = "https://n8n.nodecrew.me/webhook/saleor-order-shipped";
export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhookPayloadFragment>({
name: "Order Fulfilled in Saleor",
@@ -15,42 +17,29 @@ export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhoo
});
export default orderFulfilledWebhook.createHandler(async (req, res, ctx) => {
const { payload, event, baseUrl, authData } = ctx;
const order = payload.order;
if (!order) {
console.error("No order data in webhook payload");
return res.status(200).end();
}
const { payload, event, authData } = ctx;
console.log(`Order ${order.number} fulfilled for customer: ${order.userEmail}`);
const items = ((order as any).lines || []).map((line: any) => ({
id: line.id,
name: line.variant?.product?.name || "Unknown Product",
quantity: line.quantity,
price: formatPrice(line.totalPrice?.gross?.amount || 0, line.totalPrice?.gross?.currency || "USD"),
}));
const orderData = {
orderId: order.id,
orderNumber: order.number || "Unknown",
customerName: (order as any).shippingAddress?.firstName
? `${(order as any).shippingAddress.firstName} ${(order as any).shippingAddress.lastName || ""}`.trim()
: order.userEmail?.split("@")[0] || "Customer",
items,
};
console.log(`Order fulfilled: ${payload.order?.number} for ${payload.order?.userEmail}`);
console.log(`Forwarding to N8N: ${N8N_WEBHOOK_URL}`);
try {
if (order.userEmail) {
await sendOrderShippedEmail({
to: order.userEmail,
orderData,
});
console.log(`Customer notification sent for fulfilled order ${order.number}`);
// Forward to N8N
const response = await fetch(N8N_WEBHOOK_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-saleor-event": "order.fulfilled",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
console.error(`N8N returned ${response.status}: ${await response.text()}`);
} else {
console.log(`Successfully forwarded to N8N: ${response.status}`);
}
} catch (error) {
console.error("Failed to send email:", error);
console.error(`Failed to forward to N8N:`, error);
}
return res.status(200).end();
@@ -60,4 +49,4 @@ export const config = {
api: {
bodyParser: false,
},
};
};