Add order refunded webhook (#751)
* Update the app sdk package * Add order refunded webhook * Add changeset
This commit is contained in:
parent
3c6cd4ccec
commit
790a47ee08
15 changed files with 1571 additions and 173 deletions
5
.changeset/cyan-pianos-hide.md
Normal file
5
.changeset/cyan-pianos-hide.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"saleor-app-emails-and-messages": patch
|
||||
---
|
||||
|
||||
Added support for new event: order refunded.
|
File diff suppressed because it is too large
Load diff
|
@ -73,6 +73,6 @@
|
|||
},
|
||||
"private": true,
|
||||
"saleor": {
|
||||
"schemaVersion": "3.13"
|
||||
"schemaVersion": "3.14"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { vi, expect, describe, it } from "vitest";
|
||||
import { expect, describe, it } from "vitest";
|
||||
import { getEventFormStatus } from "./get-event-form-status";
|
||||
import { PermissionEnum } from "../../generated/graphql";
|
||||
|
||||
|
@ -10,6 +10,7 @@ describe("getEventFormStatus", function () {
|
|||
appPermissions: [PermissionEnum.ManageGiftCard],
|
||||
featureFlags: {
|
||||
giftCardSentEvent: true,
|
||||
orderRefundedEvent: true,
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
|
@ -22,6 +23,7 @@ describe("getEventFormStatus", function () {
|
|||
appPermissions: [],
|
||||
featureFlags: {
|
||||
giftCardSentEvent: false,
|
||||
orderRefundedEvent: true,
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
|
@ -38,6 +40,7 @@ describe("getEventFormStatus", function () {
|
|||
appPermissions: [],
|
||||
featureFlags: {
|
||||
giftCardSentEvent: true,
|
||||
orderRefundedEvent: true,
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
|
@ -54,6 +57,7 @@ describe("getEventFormStatus", function () {
|
|||
appPermissions: [PermissionEnum.ManageGiftCard],
|
||||
featureFlags: {
|
||||
giftCardSentEvent: false,
|
||||
orderRefundedEvent: true,
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
|
|
|
@ -17,24 +17,38 @@ export const getEventFormStatus = ({
|
|||
isDisabled: boolean;
|
||||
requiredSaleorVersion: string | undefined;
|
||||
} => {
|
||||
// Since GIFT_CARD_SENT is the only event with such validation, we can exit early
|
||||
if (eventType !== "GIFT_CARD_SENT") {
|
||||
switch (eventType) {
|
||||
case "ORDER_REFUNDED": {
|
||||
const isUnsupported = !featureFlags?.orderRefundedEvent;
|
||||
|
||||
const hasPermission = (appPermissions || []).includes(PermissionEnum.ManageOrders);
|
||||
|
||||
const isDisabled = isUnsupported || !hasPermission;
|
||||
|
||||
return {
|
||||
isDisabled,
|
||||
missingPermission: hasPermission ? undefined : PermissionEnum.ManageOrders,
|
||||
requiredSaleorVersion: isUnsupported ? ">=3.14" : undefined,
|
||||
};
|
||||
}
|
||||
case "GIFT_CARD_SENT": {
|
||||
const isUnsupported = !featureFlags?.giftCardSentEvent;
|
||||
|
||||
const hasPermission = (appPermissions || []).includes(PermissionEnum.ManageGiftCard);
|
||||
|
||||
const isDisabled = isUnsupported || !hasPermission;
|
||||
|
||||
return {
|
||||
isDisabled,
|
||||
missingPermission: hasPermission ? undefined : PermissionEnum.ManageGiftCard,
|
||||
requiredSaleorVersion: isUnsupported ? ">=3.13" : undefined,
|
||||
};
|
||||
}
|
||||
default:
|
||||
return {
|
||||
isDisabled: false,
|
||||
missingPermission: undefined,
|
||||
requiredSaleorVersion: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const isUnsupported = !featureFlags?.giftCardSentEvent;
|
||||
|
||||
const hasGiftCardPermission = (appPermissions || []).includes(PermissionEnum.ManageGiftCard);
|
||||
|
||||
const isDisabled = isUnsupported || !hasGiftCardPermission;
|
||||
|
||||
return {
|
||||
isDisabled,
|
||||
missingPermission: hasGiftCardPermission ? undefined : PermissionEnum.ManageGiftCard,
|
||||
requiredSaleorVersion: isUnsupported ? ">=3.13" : undefined,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
OrderFullyPaidWebhookPayloadFragment,
|
||||
InvoiceSentWebhookPayloadFragment,
|
||||
GiftCardSentWebhookPayloadFragment,
|
||||
OrderRefundedWebhookPayloadFragment,
|
||||
} from "../../../generated/graphql";
|
||||
import { NotifyEventPayload } from "../../pages/api/webhooks/notify";
|
||||
|
||||
|
@ -137,6 +138,10 @@ const orderFullyPaidPayload: OrderFullyPaidWebhookPayloadFragment = {
|
|||
order: exampleOrderPayload,
|
||||
};
|
||||
|
||||
const orderRefundedPayload: OrderRefundedWebhookPayloadFragment = {
|
||||
order: exampleOrderPayload,
|
||||
};
|
||||
|
||||
const invoiceSentPayload: InvoiceSentWebhookPayloadFragment = {
|
||||
invoice: {
|
||||
id: "SW52b2ljZToxMDE=",
|
||||
|
@ -312,16 +317,17 @@ const giftCardSentPayload: GiftCardSentWebhookPayloadFragment = {
|
|||
};
|
||||
|
||||
export const examplePayloads: Record<MessageEventTypes, any> = {
|
||||
ORDER_CREATED: orderCreatedPayload,
|
||||
ORDER_CONFIRMED: orderConfirmedPayload,
|
||||
ACCOUNT_CHANGE_EMAIL_CONFIRM: accountChangeEmailConfirmPayload,
|
||||
ACCOUNT_CHANGE_EMAIL_REQUEST: accountChangeEmailRequestPayload,
|
||||
ACCOUNT_CONFIRMATION: accountConfirmationPayload,
|
||||
ACCOUNT_DELETE: accountDeletePayload,
|
||||
ACCOUNT_PASSWORD_RESET: accountPasswordResetPayload,
|
||||
GIFT_CARD_SENT: giftCardSentPayload,
|
||||
INVOICE_SENT: invoiceSentPayload,
|
||||
ORDER_CANCELLED: orderCancelledPayload,
|
||||
ORDER_CONFIRMED: orderConfirmedPayload,
|
||||
ORDER_CREATED: orderCreatedPayload,
|
||||
ORDER_FULFILLED: orderFulfilledPayload,
|
||||
ORDER_FULLY_PAID: orderFullyPaidPayload,
|
||||
INVOICE_SENT: invoiceSentPayload,
|
||||
GIFT_CARD_SENT: giftCardSentPayload,
|
||||
ACCOUNT_CONFIRMATION: accountConfirmationPayload,
|
||||
ACCOUNT_PASSWORD_RESET: accountPasswordResetPayload,
|
||||
ACCOUNT_CHANGE_EMAIL_REQUEST: accountChangeEmailRequestPayload,
|
||||
ACCOUNT_CHANGE_EMAIL_CONFIRM: accountChangeEmailConfirmPayload,
|
||||
ACCOUNT_DELETE: accountDeletePayload,
|
||||
ORDER_REFUNDED: orderRefundedPayload,
|
||||
};
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
export const messageEventTypes = [
|
||||
"ACCOUNT_CHANGE_EMAIL_CONFIRM",
|
||||
"ACCOUNT_CHANGE_EMAIL_REQUEST",
|
||||
"ACCOUNT_CONFIRMATION",
|
||||
"ACCOUNT_DELETE",
|
||||
"ACCOUNT_PASSWORD_RESET",
|
||||
"GIFT_CARD_SENT",
|
||||
"INVOICE_SENT",
|
||||
"ORDER_CANCELLED",
|
||||
"ORDER_CONFIRMED",
|
||||
"ORDER_CREATED",
|
||||
"ORDER_FULFILLED",
|
||||
"ORDER_CONFIRMED",
|
||||
"ORDER_CANCELLED",
|
||||
"ORDER_FULLY_PAID",
|
||||
"INVOICE_SENT",
|
||||
"ACCOUNT_CONFIRMATION",
|
||||
"ACCOUNT_PASSWORD_RESET",
|
||||
"ACCOUNT_CHANGE_EMAIL_REQUEST",
|
||||
"ACCOUNT_CHANGE_EMAIL_CONFIRM",
|
||||
"ACCOUNT_DELETE",
|
||||
"GIFT_CARD_SENT",
|
||||
"ORDER_REFUNDED",
|
||||
] as const;
|
||||
|
||||
export type MessageEventTypes = (typeof messageEventTypes)[number];
|
||||
|
||||
export const messageEventTypesLabels: Record<MessageEventTypes, string> = {
|
||||
ACCOUNT_CHANGE_EMAIL_CONFIRM: "Customer account change email confirmation",
|
||||
ACCOUNT_CHANGE_EMAIL_REQUEST: "Customer account change email request",
|
||||
ACCOUNT_CONFIRMATION: "Customer account confirmation",
|
||||
ACCOUNT_DELETE: "Customer account delete request",
|
||||
ACCOUNT_PASSWORD_RESET: "Customer account password reset request",
|
||||
GIFT_CARD_SENT: "Gift card sent",
|
||||
INVOICE_SENT: "Invoice sent",
|
||||
ORDER_CANCELLED: "Order cancelled",
|
||||
ORDER_CONFIRMED: "Order confirmed",
|
||||
ORDER_CREATED: "Order created",
|
||||
ORDER_FULFILLED: "Order fulfilled",
|
||||
ORDER_CONFIRMED: "Order confirmed",
|
||||
ORDER_CANCELLED: "Order cancelled",
|
||||
ORDER_FULLY_PAID: "Order fully paid",
|
||||
INVOICE_SENT: "Invoice sent",
|
||||
GIFT_CARD_SENT: "Gift card sent",
|
||||
ACCOUNT_CONFIRMATION: "Customer account confirmation",
|
||||
ACCOUNT_PASSWORD_RESET: "Customer account password reset request",
|
||||
ACCOUNT_CHANGE_EMAIL_REQUEST: "Customer account change email request",
|
||||
ACCOUNT_CHANGE_EMAIL_CONFIRM: "Customer account change email confirmation",
|
||||
ACCOUNT_DELETE: "Customer account delete request",
|
||||
ORDER_REFUNDED: "Order refunded",
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { SaleorVersionCompatibilityValidator } from "@saleor/apps-shared";
|
||||
|
||||
export const featureFlags = ["giftCardSentEvent"] as const;
|
||||
export const featureFlags = ["giftCardSentEvent", "orderRefundedEvent"] as const;
|
||||
|
||||
export type FeatureFlag = (typeof featureFlags)[number];
|
||||
|
||||
|
@ -17,5 +17,6 @@ interface GetFeatureFlagsArgs {
|
|||
export const getFeatureFlags = ({ saleorVersion }: GetFeatureFlagsArgs): FeatureFlagsState => {
|
||||
return {
|
||||
giftCardSentEvent: new SaleorVersionCompatibilityValidator(">=3.13").isValid(saleorVersion),
|
||||
orderRefundedEvent: new SaleorVersionCompatibilityValidator(">=3.14").isValid(saleorVersion),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -138,6 +138,23 @@ const defaultOrderFullyPaidMjmlTemplate = `<mjml>
|
|||
</mj-body>
|
||||
</mjml>`;
|
||||
|
||||
const defaultOrderRefundedMjmlTemplate = `<mjml>
|
||||
<mj-body>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text font-size="16px">
|
||||
Hello!
|
||||
</mj-text>
|
||||
<mj-text>
|
||||
Order {{ order.number}} has been refunded.
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
${addressSection}
|
||||
${orderLinesSection}
|
||||
</mj-body>
|
||||
</mjml>`;
|
||||
|
||||
const defaultOrderCancelledMjmlTemplate = `<mjml>
|
||||
<mj-body>
|
||||
<mj-section>
|
||||
|
@ -286,6 +303,7 @@ export const defaultMjmlTemplates: Record<MessageEventTypes, string> = {
|
|||
ORDER_CREATED: defaultOrderCreatedMjmlTemplate,
|
||||
ORDER_FULFILLED: defaultOrderFulfilledMjmlTemplate,
|
||||
ORDER_FULLY_PAID: defaultOrderFullyPaidMjmlTemplate,
|
||||
ORDER_REFUNDED: defaultOrderRefundedMjmlTemplate,
|
||||
};
|
||||
|
||||
export const defaultMjmlSubjectTemplates: Record<MessageEventTypes, string> = {
|
||||
|
@ -301,4 +319,5 @@ export const defaultMjmlSubjectTemplates: Record<MessageEventTypes, string> = {
|
|||
ORDER_CREATED: "Order {{ order.number }} has been created",
|
||||
ORDER_FULFILLED: "Order {{ order.number }} has been fulfilled",
|
||||
ORDER_FULLY_PAID: "Order {{ order.number }} has been fully paid",
|
||||
ORDER_REFUNDED: "Order {{ order.number }} has been refunded",
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ import { expect, describe, it } from "vitest";
|
|||
import { SmtpConfiguration } from "../smtp/configuration/smtp-config-schema";
|
||||
import { getWebhookStatusesFromConfigurations } from "./get-webhook-statuses-from-configurations";
|
||||
import { SendgridConfiguration } from "../sendgrid/configuration/sendgrid-config-schema";
|
||||
import { webhookStatusesFactory } from "./webhook-status-dict";
|
||||
|
||||
export const nonActiveSmtpConfiguration: SmtpConfiguration = {
|
||||
id: "1685343953413npk9p",
|
||||
|
@ -169,16 +170,7 @@ describe("getWebhookStatusesFromConfigurations", function () {
|
|||
smtpConfigurations: [],
|
||||
sendgridConfigurations: [],
|
||||
})
|
||||
).toStrictEqual({
|
||||
invoiceSentWebhook: false,
|
||||
notifyWebhook: false,
|
||||
orderCancelledWebhook: false,
|
||||
orderConfirmedWebhook: false,
|
||||
orderFulfilledWebhook: false,
|
||||
orderCreatedWebhook: false,
|
||||
orderFullyPaidWebhook: false,
|
||||
giftCardSentWebhook: false,
|
||||
});
|
||||
).toStrictEqual(webhookStatusesFactory({}));
|
||||
});
|
||||
|
||||
it("Statuses should be set to false, when no active configurations passed", async () => {
|
||||
|
@ -187,16 +179,7 @@ describe("getWebhookStatusesFromConfigurations", function () {
|
|||
smtpConfigurations: [nonActiveSmtpConfiguration],
|
||||
sendgridConfigurations: [nonActiveSendgridConfiguration],
|
||||
})
|
||||
).toStrictEqual({
|
||||
invoiceSentWebhook: false,
|
||||
notifyWebhook: false,
|
||||
orderCancelledWebhook: false,
|
||||
orderConfirmedWebhook: false,
|
||||
orderFulfilledWebhook: false,
|
||||
orderCreatedWebhook: false,
|
||||
orderFullyPaidWebhook: false,
|
||||
giftCardSentWebhook: false,
|
||||
});
|
||||
).toStrictEqual(webhookStatusesFactory({}));
|
||||
});
|
||||
|
||||
it("Statuses should be set to false, when configuration is not active even if events were activated", async () => {
|
||||
|
@ -210,16 +193,7 @@ describe("getWebhookStatusesFromConfigurations", function () {
|
|||
smtpConfigurations: [smtpConfiguration],
|
||||
sendgridConfigurations: [nonActiveSendgridConfiguration],
|
||||
})
|
||||
).toStrictEqual({
|
||||
invoiceSentWebhook: false,
|
||||
notifyWebhook: false,
|
||||
orderCancelledWebhook: false,
|
||||
orderConfirmedWebhook: false,
|
||||
orderFulfilledWebhook: false,
|
||||
orderCreatedWebhook: false,
|
||||
orderFullyPaidWebhook: false,
|
||||
giftCardSentWebhook: false,
|
||||
});
|
||||
).toStrictEqual(webhookStatusesFactory({}));
|
||||
});
|
||||
|
||||
it("Status of the event should be set to true, when at least one active configuration has activated it", async () => {
|
||||
|
@ -241,16 +215,7 @@ describe("getWebhookStatusesFromConfigurations", function () {
|
|||
smtpConfigurations: [nonActiveSmtpConfiguration, smtpConfiguration],
|
||||
sendgridConfigurations: [nonActiveSendgridConfiguration],
|
||||
})
|
||||
).toStrictEqual({
|
||||
invoiceSentWebhook: true,
|
||||
notifyWebhook: false,
|
||||
orderCancelledWebhook: false,
|
||||
orderConfirmedWebhook: false,
|
||||
orderFulfilledWebhook: false,
|
||||
orderCreatedWebhook: false,
|
||||
orderFullyPaidWebhook: false,
|
||||
giftCardSentWebhook: false,
|
||||
});
|
||||
).toStrictEqual(webhookStatusesFactory({ enabledWebhooks: ["invoiceSentWebhook"] }));
|
||||
});
|
||||
|
||||
it("Status of the NOTIFY webhooks should be set to true, when at least one active configuration has activated one of its related events", async () => {
|
||||
|
@ -278,15 +243,6 @@ describe("getWebhookStatusesFromConfigurations", function () {
|
|||
smtpConfigurations: [nonActiveSmtpConfiguration, smtpConfiguration],
|
||||
sendgridConfigurations: [nonActiveSendgridConfiguration],
|
||||
})
|
||||
).toStrictEqual({
|
||||
invoiceSentWebhook: false,
|
||||
notifyWebhook: true,
|
||||
orderCancelledWebhook: false,
|
||||
orderConfirmedWebhook: false,
|
||||
orderFulfilledWebhook: false,
|
||||
orderCreatedWebhook: false,
|
||||
orderFullyPaidWebhook: false,
|
||||
giftCardSentWebhook: false,
|
||||
});
|
||||
).toStrictEqual(webhookStatusesFactory({ enabledWebhooks: ["notifyWebhook"] }));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { SendgridConfiguration } from "../sendgrid/configuration/sendgrid-config-schema";
|
||||
import { SmtpConfiguration } from "../smtp/configuration/smtp-config-schema";
|
||||
import { AppWebhook, eventToWebhookMapping } from "./webhook-management-service";
|
||||
import { webhookStatusesFactory } from "./webhook-status-dict";
|
||||
|
||||
/*
|
||||
* Returns dictionary of webhook statuses based on passed configurations.
|
||||
|
@ -14,16 +15,7 @@ export const getWebhookStatusesFromConfigurations = ({
|
|||
sendgridConfigurations: SendgridConfiguration[];
|
||||
}) => {
|
||||
// TODO: this dict should be generated in one place instead of manually edited
|
||||
const statuses: Record<AppWebhook, boolean> = {
|
||||
giftCardSentWebhook: false,
|
||||
invoiceSentWebhook: false,
|
||||
notifyWebhook: false,
|
||||
orderCancelledWebhook: false,
|
||||
orderConfirmedWebhook: false,
|
||||
orderCreatedWebhook: false,
|
||||
orderFulfilledWebhook: false,
|
||||
orderFullyPaidWebhook: false,
|
||||
};
|
||||
const statuses: Record<AppWebhook, boolean> = webhookStatusesFactory({});
|
||||
|
||||
smtpConfigurations.forEach(async (config) => {
|
||||
if (!config.active) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import { WebhookEventTypeAsyncEnum } from "../../../generated/graphql";
|
|||
import { invoiceSentWebhook } from "../../pages/api/webhooks/invoice-sent";
|
||||
import { orderCancelledWebhook } from "../../pages/api/webhooks/order-cancelled";
|
||||
import { FeatureFlagService } from "../feature-flag-service/feature-flag-service";
|
||||
import { giftCardSentWebhook } from "../../pages/api/webhooks/gift-card-sent";
|
||||
import { webhookStatusesFactory } from "./webhook-status-dict";
|
||||
|
||||
describe("WebhookManagementService", function () {
|
||||
const mockedClient = {} as Client;
|
||||
|
@ -76,16 +76,9 @@ describe("WebhookManagementService", function () {
|
|||
|
||||
const statuses = await webhookManagementService.getWebhooksStatus();
|
||||
|
||||
expect(statuses).toStrictEqual({
|
||||
invoiceSentWebhook: true,
|
||||
notifyWebhook: false,
|
||||
orderCancelledWebhook: false,
|
||||
orderConfirmedWebhook: false,
|
||||
orderCreatedWebhook: false,
|
||||
orderFulfilledWebhook: false,
|
||||
orderFullyPaidWebhook: false,
|
||||
giftCardSentWebhook: false,
|
||||
});
|
||||
expect(statuses).toStrictEqual(
|
||||
webhookStatusesFactory({ enabledWebhooks: ["invoiceSentWebhook"] })
|
||||
);
|
||||
expect(fetchAppWebhooksMock).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { createLogger } from "@saleor/apps-shared";
|
|||
import { WebhookEventTypeAsyncEnum } from "../../../generated/graphql";
|
||||
import { giftCardSentWebhook } from "../../pages/api/webhooks/gift-card-sent";
|
||||
import { FeatureFlagService } from "../feature-flag-service/feature-flag-service";
|
||||
import { FeatureFlagsState } from "../feature-flag-service/get-feature-flags";
|
||||
import { orderRefundedWebhook } from "../../pages/api/webhooks/order-refunded";
|
||||
|
||||
export const AppWebhooks = {
|
||||
giftCardSentWebhook,
|
||||
|
@ -23,6 +23,7 @@ export const AppWebhooks = {
|
|||
orderCreatedWebhook,
|
||||
orderFulfilledWebhook,
|
||||
orderFullyPaidWebhook,
|
||||
orderRefundedWebhook,
|
||||
};
|
||||
|
||||
export type AppWebhook = keyof typeof AppWebhooks;
|
||||
|
@ -40,6 +41,7 @@ export const eventToWebhookMapping: Record<MessageEventTypes, AppWebhook> = {
|
|||
ORDER_CREATED: "orderCreatedWebhook",
|
||||
ORDER_FULFILLED: "orderFulfilledWebhook",
|
||||
ORDER_FULLY_PAID: "orderFullyPaidWebhook",
|
||||
ORDER_REFUNDED: "orderRefundedWebhook",
|
||||
};
|
||||
|
||||
const logger = createLogger({
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { AppWebhook, AppWebhooks } from "./webhook-management-service";
|
||||
|
||||
export type WebhookStatuses = Record<AppWebhook, boolean>;
|
||||
|
||||
export const webhookStatusesFactory = ({
|
||||
enabledWebhooks,
|
||||
}: {
|
||||
enabledWebhooks?: AppWebhook[];
|
||||
}): WebhookStatuses => ({
|
||||
// TODO: This function clearly deserves a better implementation.
|
||||
giftCardSentWebhook: !!enabledWebhooks?.includes("giftCardSentWebhook"),
|
||||
invoiceSentWebhook: !!enabledWebhooks?.includes("invoiceSentWebhook"),
|
||||
notifyWebhook: !!enabledWebhooks?.includes("notifyWebhook"),
|
||||
orderCancelledWebhook: !!enabledWebhooks?.includes("orderCancelledWebhook"),
|
||||
orderConfirmedWebhook: !!enabledWebhooks?.includes("orderConfirmedWebhook"),
|
||||
orderCreatedWebhook: !!enabledWebhooks?.includes("orderCreatedWebhook"),
|
||||
orderFulfilledWebhook: !!enabledWebhooks?.includes("orderFulfilledWebhook"),
|
||||
orderFullyPaidWebhook: !!enabledWebhooks?.includes("orderFullyPaidWebhook"),
|
||||
orderRefundedWebhook: !!enabledWebhooks?.includes("orderRefundedWebhook"),
|
||||
});
|
|
@ -0,0 +1,87 @@
|
|||
import { OrderDetailsFragmentDoc } from "../../../../generated/graphql";
|
||||
import { NextWebhookApiHandler, SaleorAsyncWebhook } from "@saleor/app-sdk/handlers/next";
|
||||
import { gql } from "urql";
|
||||
import { saleorApp } from "../../../saleor-app";
|
||||
import { createLogger, createGraphQLClient } from "@saleor/apps-shared";
|
||||
import { OrderRefundedWebhookPayloadFragment } from "../../../../generated/graphql";
|
||||
import { sendEventMessages } from "../../../modules/event-handlers/send-event-messages";
|
||||
|
||||
const OrderRefundedWebhookPayload = gql`
|
||||
${OrderDetailsFragmentDoc}
|
||||
fragment OrderRefundedWebhookPayload on OrderRefunded {
|
||||
order {
|
||||
...OrderDetails
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const OrderRefundedGraphqlSubscription = gql`
|
||||
${OrderRefundedWebhookPayload}
|
||||
subscription OrderRefunded {
|
||||
event {
|
||||
...OrderRefundedWebhookPayload
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const orderRefundedWebhook = new SaleorAsyncWebhook<OrderRefundedWebhookPayloadFragment>({
|
||||
name: "Order Refunded in Saleor",
|
||||
webhookPath: "api/webhooks/order-refunded",
|
||||
asyncEvent: "ORDER_REFUNDED",
|
||||
apl: saleorApp.apl,
|
||||
subscriptionQueryAst: OrderRefundedGraphqlSubscription,
|
||||
});
|
||||
|
||||
const logger = createLogger({
|
||||
name: orderRefundedWebhook.webhookPath,
|
||||
});
|
||||
|
||||
const handler: NextWebhookApiHandler<OrderRefundedWebhookPayloadFragment> = async (
|
||||
req,
|
||||
res,
|
||||
context
|
||||
) => {
|
||||
logger.debug("Webhook received");
|
||||
|
||||
const { payload, authData } = context;
|
||||
const { order } = payload;
|
||||
|
||||
if (!order) {
|
||||
logger.error("No order data payload");
|
||||
return res.status(200).end();
|
||||
}
|
||||
|
||||
const recipientEmail = order.userEmail || order.user?.email;
|
||||
|
||||
if (!recipientEmail?.length) {
|
||||
logger.error(`The order ${order.number} had no email recipient set. Aborting.`);
|
||||
return res
|
||||
.status(200)
|
||||
.json({ error: "Email recipient has not been specified in the event payload." });
|
||||
}
|
||||
|
||||
const channel = order.channel.slug;
|
||||
const client = createGraphQLClient({
|
||||
saleorApiUrl: authData.saleorApiUrl,
|
||||
token: authData.token,
|
||||
});
|
||||
|
||||
await sendEventMessages({
|
||||
authData,
|
||||
channel,
|
||||
client,
|
||||
event: "ORDER_REFUNDED",
|
||||
payload: { order: payload.order },
|
||||
recipientEmail,
|
||||
});
|
||||
|
||||
return res.status(200).json({ message: "The event has been handled" });
|
||||
};
|
||||
|
||||
export default orderRefundedWebhook.createHandler(handler);
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false,
|
||||
},
|
||||
};
|
Loading…
Reference in a new issue