From 542bdcaa849426b7524d01811c58505f75361e1e Mon Sep 17 00:00:00 2001 From: Adrian Pilarczyk Date: Tue, 22 Aug 2023 14:26:54 +0200 Subject: [PATCH] feat: :sparkles: add boilerplate for order_refunded --- .../subscriptions/OrderRefunded.graphql | 29 ++++++++++ .../modules/avatax/avatax-webhook.service.ts | 13 ++++- .../taxes/get-active-connection-service.ts | 7 ++- .../src/modules/taxes/tax-provider-webhook.ts | 2 + .../modules/taxjar/taxjar-webhook.service.ts | 7 ++- apps/taxes/src/pages/api/manifest.ts | 2 + .../src/pages/api/webhooks/order-refunded.ts | 56 +++++++++++++++++++ 7 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 apps/taxes/graphql/subscriptions/OrderRefunded.graphql create mode 100644 apps/taxes/src/pages/api/webhooks/order-refunded.ts diff --git a/apps/taxes/graphql/subscriptions/OrderRefunded.graphql b/apps/taxes/graphql/subscriptions/OrderRefunded.graphql new file mode 100644 index 0000000..4c3d2c9 --- /dev/null +++ b/apps/taxes/graphql/subscriptions/OrderRefunded.graphql @@ -0,0 +1,29 @@ +fragment OrderRefundedSubscription on Order { + id + avataxId: metafield(key: "avataxId") + channel { + id + slug + } +} + +fragment OrderRefundedEventSubscription on Event { + __typename + ... on OrderRefunded { + order { + ...OrderRefundedSubscription + } + recipient { + privateMetadata { + key + value + } + } + } +} + +subscription OrderRefundedSubscription { + event { + ...OrderRefundedEventSubscription + } +} diff --git a/apps/taxes/src/modules/avatax/avatax-webhook.service.ts b/apps/taxes/src/modules/avatax/avatax-webhook.service.ts index 95a46aa..ec708ff 100644 --- a/apps/taxes/src/modules/avatax/avatax-webhook.service.ts +++ b/apps/taxes/src/modules/avatax/avatax-webhook.service.ts @@ -1,14 +1,18 @@ import { AuthData } from "@saleor/app-sdk/APL"; -import { OrderConfirmedSubscriptionFragment, TaxBaseFragment } from "../../../generated/graphql"; +import { + OrderConfirmedSubscriptionFragment, + OrderRefundedSubscriptionFragment, +} from "../../../generated/graphql"; import { Logger, createLogger } from "../../lib/logger"; +import { CalculateTaxesPayload } from "../../pages/api/webhooks/checkout-calculate-taxes"; import { OrderCancelledPayload } from "../../pages/api/webhooks/order-cancelled"; +import { OrderRefundedPayload } from "../../pages/api/webhooks/order-refunded"; import { ProviderWebhookService } from "../taxes/tax-provider-webhook"; import { AvataxClient } from "./avatax-client"; import { AvataxConfig, defaultAvataxConfig } from "./avatax-connection-schema"; import { AvataxCalculateTaxesAdapter } from "./calculate-taxes/avatax-calculate-taxes-adapter"; import { AvataxOrderCancelledAdapter } from "./order-cancelled/avatax-order-cancelled-adapter"; import { AvataxOrderConfirmedAdapter } from "./order-confirmed/avatax-order-confirmed-adapter"; -import { CalculateTaxesPayload } from "../../pages/api/webhooks/checkout-calculate-taxes"; export class AvataxWebhookService implements ProviderWebhookService { config = defaultAvataxConfig; @@ -24,6 +28,7 @@ export class AvataxWebhookService implements ProviderWebhookService { }); const avataxClient = new AvataxClient(config); + refundOrder: (payload: OrderRefundedSubscriptionFragment) => Promise; this.config = config; this.client = avataxClient; } @@ -49,4 +54,8 @@ export class AvataxWebhookService implements ProviderWebhookService { await adapter.send(payload); } + + async refundOrder(payload: OrderRefundedPayload) { + // todo: implement + } } diff --git a/apps/taxes/src/modules/taxes/get-active-connection-service.ts b/apps/taxes/src/modules/taxes/get-active-connection-service.ts index 35fb7e5..7df50f7 100644 --- a/apps/taxes/src/modules/taxes/get-active-connection-service.ts +++ b/apps/taxes/src/modules/taxes/get-active-connection-service.ts @@ -7,6 +7,7 @@ import { import { Logger, createLogger } from "../../lib/logger"; import { OrderCancelledPayload } from "../../pages/api/webhooks/order-cancelled"; +import { OrderRefundedPayload } from "../../pages/api/webhooks/order-refunded"; import { getAppConfig } from "../app/get-app-config"; import { AvataxWebhookService } from "../avatax/avatax-webhook.service"; import { ProviderConnection } from "../provider-connections/provider-connections"; @@ -57,7 +58,11 @@ class ActiveTaxProviderService implements ProviderWebhookService { } async cancelOrder(payload: OrderCancelledPayload) { - this.client.cancelOrder(payload); + return this.client.cancelOrder(payload); + } + + async refundOrder(payload: OrderRefundedPayload) { + return this.client.refundOrder(payload); } } diff --git a/apps/taxes/src/modules/taxes/tax-provider-webhook.ts b/apps/taxes/src/modules/taxes/tax-provider-webhook.ts index 9c08236..5eecca4 100644 --- a/apps/taxes/src/modules/taxes/tax-provider-webhook.ts +++ b/apps/taxes/src/modules/taxes/tax-provider-webhook.ts @@ -2,6 +2,7 @@ import { SyncWebhookResponsesMap } from "@saleor/app-sdk/handlers/next"; import { OrderConfirmedSubscriptionFragment } from "../../../generated/graphql"; import { CalculateTaxesPayload } from "../../pages/api/webhooks/checkout-calculate-taxes"; import { OrderCancelledPayload } from "../../pages/api/webhooks/order-cancelled"; +import { OrderRefundedPayload } from "../../pages/api/webhooks/order-refunded"; export type CalculateTaxesResponse = SyncWebhookResponsesMap["ORDER_CALCULATE_TAXES"]; @@ -11,4 +12,5 @@ export interface ProviderWebhookService { calculateTaxes: (payload: CalculateTaxesPayload) => Promise; confirmOrder: (payload: OrderConfirmedSubscriptionFragment) => Promise; cancelOrder: (payload: OrderCancelledPayload) => Promise; + refundOrder: (payload: OrderRefundedPayload) => Promise; } diff --git a/apps/taxes/src/modules/taxjar/taxjar-webhook.service.ts b/apps/taxes/src/modules/taxjar/taxjar-webhook.service.ts index 3c6ba9d..a1e5fd1 100644 --- a/apps/taxes/src/modules/taxjar/taxjar-webhook.service.ts +++ b/apps/taxes/src/modules/taxjar/taxjar-webhook.service.ts @@ -5,6 +5,7 @@ import { } from "../../../generated/graphql"; import { Logger, createLogger } from "../../lib/logger"; import { CalculateTaxesPayload } from "../../pages/api/webhooks/checkout-calculate-taxes"; +import { OrderRefundedPayload } from "../../pages/api/webhooks/order-refunded"; import { ProviderWebhookService } from "../taxes/tax-provider-webhook"; import { TaxJarCalculateTaxesAdapter } from "./calculate-taxes/taxjar-calculate-taxes-adapter"; import { TaxJarOrderConfirmedAdapter } from "./order-confirmed/taxjar-order-confirmed-adapter"; @@ -46,6 +47,10 @@ export class TaxJarWebhookService implements ProviderWebhookService { } async cancelOrder(payload: OrderCancelledEventSubscriptionFragment) { - // TaxJar isn't implemented yet + // todo: implement + } + + async refundOrder(payload: OrderRefundedPayload) { + // todo: implement } } diff --git a/apps/taxes/src/pages/api/manifest.ts b/apps/taxes/src/pages/api/manifest.ts index 0e31a9a..f2a21fe 100644 --- a/apps/taxes/src/pages/api/manifest.ts +++ b/apps/taxes/src/pages/api/manifest.ts @@ -7,6 +7,7 @@ import { orderCalculateTaxesSyncWebhook } from "./webhooks/order-calculate-taxes import { orderConfirmedAsyncWebhook } from "./webhooks/order-confirmed"; import { REQUIRED_SALEOR_VERSION } from "../../../saleor-app"; import { orderCancelledAsyncWebhook } from "./webhooks/order-cancelled"; +import { orderRefundedAsyncWebhook } from "./webhooks/order-refunded"; export default createManifestHandler({ async manifestFactory({ appBaseUrl }) { @@ -37,6 +38,7 @@ export default createManifestHandler({ checkoutCalculateTaxesSyncWebhook.getWebhookManifest(apiBaseURL), orderConfirmedAsyncWebhook.getWebhookManifest(apiBaseURL), orderCancelledAsyncWebhook.getWebhookManifest(apiBaseURL), + orderRefundedAsyncWebhook.getWebhookManifest(apiBaseURL), ], }; diff --git a/apps/taxes/src/pages/api/webhooks/order-refunded.ts b/apps/taxes/src/pages/api/webhooks/order-refunded.ts new file mode 100644 index 0000000..1e75020 --- /dev/null +++ b/apps/taxes/src/pages/api/webhooks/order-refunded.ts @@ -0,0 +1,56 @@ +import { SaleorAsyncWebhook } from "@saleor/app-sdk/handlers/next"; +import { + OrderRefundedEventSubscriptionFragment, + UntypedOrderRefundedSubscriptionDocument, +} from "../../../../generated/graphql"; +import { saleorApp } from "../../../../saleor-app"; +import { createLogger } from "../../../lib/logger"; +import { getActiveConnectionService } from "../../../modules/taxes/get-active-connection-service"; +import { WebhookResponse } from "../../../modules/app/webhook-response"; + +export const config = { + api: { + bodyParser: false, + }, +}; + +export type OrderRefundedPayload = Extract< + OrderRefundedEventSubscriptionFragment, + { __typename: "OrderRefunded" } +>; + +export const orderRefundedAsyncWebhook = new SaleorAsyncWebhook({ + name: "OrderRefunded", + apl: saleorApp.apl, + event: "ORDER_REFUNDED", + query: UntypedOrderRefundedSubscriptionDocument, + webhookPath: "/api/webhooks/order-refunded", +}); + +export default orderRefundedAsyncWebhook.createHandler(async (req, res, ctx) => { + const logger = createLogger({ event: ctx.event }); + const { payload } = ctx; + const webhookResponse = new WebhookResponse(res); + + logger.info("Handler called with payload"); + + if (!payload.order) { + return webhookResponse.error(new Error("Insufficient order data")); + } + + try { + const appMetadata = payload.recipient?.privateMetadata ?? []; + const channelSlug = payload.order.channel.slug; + const taxProvider = getActiveConnectionService(channelSlug, appMetadata, ctx.authData); + + logger.info("Refunding order..."); + + await taxProvider.refundOrder(payload); + + logger.info("Order refunded"); + + return webhookResponse.success(); + } catch (error) { + return webhookResponse.error(new Error("Error while refunding tax provider order")); + } +});