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:
Generated
+176
-20
File diff suppressed because one or more lines are too long
@@ -2,12 +2,89 @@ fragment OrderCancelledWebhookPayload on OrderCancelled {
|
|||||||
order {
|
order {
|
||||||
id
|
id
|
||||||
number
|
number
|
||||||
|
created
|
||||||
|
status
|
||||||
userEmail
|
userEmail
|
||||||
|
languageCode
|
||||||
|
channel {
|
||||||
|
slug
|
||||||
|
}
|
||||||
user {
|
user {
|
||||||
email
|
email
|
||||||
firstName
|
firstName
|
||||||
lastName
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,5 +82,9 @@ fragment OrderCreatedWebhookPayload on OrderCreated {
|
|||||||
currency
|
currency
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
metadata {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,17 +2,95 @@ fragment OrderFulfilledWebhookPayload on OrderFulfilled {
|
|||||||
order {
|
order {
|
||||||
id
|
id
|
||||||
number
|
number
|
||||||
|
created
|
||||||
|
status
|
||||||
userEmail
|
userEmail
|
||||||
|
languageCode
|
||||||
|
channel {
|
||||||
|
slug
|
||||||
|
}
|
||||||
user {
|
user {
|
||||||
email
|
email
|
||||||
firstName
|
firstName
|
||||||
lastName
|
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 {
|
fulfillments {
|
||||||
id
|
id
|
||||||
status
|
status
|
||||||
created
|
created
|
||||||
|
trackingNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Generated
+697
-25
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,9 @@ import {
|
|||||||
OrderCancelledWebhookPayloadFragment,
|
OrderCancelledWebhookPayloadFragment,
|
||||||
} from "@/generated/graphql";
|
} from "@/generated/graphql";
|
||||||
import { saleorApp } from "@/saleor-app";
|
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>({
|
export const orderCancelledWebhook = new SaleorAsyncWebhook<OrderCancelledWebhookPayloadFragment>({
|
||||||
name: "Order Cancelled in Saleor",
|
name: "Order Cancelled in Saleor",
|
||||||
@@ -15,43 +17,29 @@ export const orderCancelledWebhook = new SaleorAsyncWebhook<OrderCancelledWebhoo
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default orderCancelledWebhook.createHandler(async (req, res, ctx) => {
|
export default orderCancelledWebhook.createHandler(async (req, res, ctx) => {
|
||||||
const { payload, event, baseUrl, authData } = ctx;
|
const { payload, event, authData } = ctx;
|
||||||
const order = payload.order;
|
|
||||||
|
|
||||||
if (!order) {
|
console.log(`Order cancelled: ${payload.order?.number} for ${payload.order?.userEmail}`);
|
||||||
console.error("No order data in webhook payload");
|
console.log(`Forwarding to N8N: ${N8N_WEBHOOK_URL}`);
|
||||||
return res.status(200).end();
|
|
||||||
}
|
|
||||||
|
|
||||||
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"),
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (order.userEmail) {
|
// Forward to N8N
|
||||||
await sendOrderCancelledEmail({
|
const response = await fetch(N8N_WEBHOOK_URL, {
|
||||||
to: order.userEmail,
|
method: "POST",
|
||||||
orderData,
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"x-saleor-event": "order.cancelled",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
console.log(`Customer notification sent for cancelled order ${order.number}`);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`N8N returned ${response.status}: ${await response.text()}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Successfully forwarded to N8N: ${response.status}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to send email:", error);
|
console.error(`Failed to forward to N8N:`, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).end();
|
return res.status(200).end();
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import {
|
|||||||
OrderFulfilledWebhookPayloadFragment,
|
OrderFulfilledWebhookPayloadFragment,
|
||||||
} from "@/generated/graphql";
|
} from "@/generated/graphql";
|
||||||
import { saleorApp } from "@/saleor-app";
|
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>({
|
export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhookPayloadFragment>({
|
||||||
name: "Order Fulfilled in Saleor",
|
name: "Order Fulfilled in Saleor",
|
||||||
@@ -15,42 +17,29 @@ export const orderFulfilledWebhook = new SaleorAsyncWebhook<OrderFulfilledWebhoo
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default orderFulfilledWebhook.createHandler(async (req, res, ctx) => {
|
export default orderFulfilledWebhook.createHandler(async (req, res, ctx) => {
|
||||||
const { payload, event, baseUrl, authData } = ctx;
|
const { payload, event, authData } = ctx;
|
||||||
const order = payload.order;
|
|
||||||
|
|
||||||
if (!order) {
|
console.log(`Order fulfilled: ${payload.order?.number} for ${payload.order?.userEmail}`);
|
||||||
console.error("No order data in webhook payload");
|
console.log(`Forwarding to N8N: ${N8N_WEBHOOK_URL}`);
|
||||||
return res.status(200).end();
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (order.userEmail) {
|
// Forward to N8N
|
||||||
await sendOrderShippedEmail({
|
const response = await fetch(N8N_WEBHOOK_URL, {
|
||||||
to: order.userEmail,
|
method: "POST",
|
||||||
orderData,
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"x-saleor-event": "order.fulfilled",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
console.log(`Customer notification sent for fulfilled order ${order.number}`);
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(`N8N returned ${response.status}: ${await response.text()}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Successfully forwarded to N8N: ${response.status}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to send email:", error);
|
console.error(`Failed to forward to N8N:`, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).end();
|
return res.status(200).end();
|
||||||
|
|||||||
Reference in New Issue
Block a user