From b28482463d9584e7c230a59ddbc5ed51c08a3d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20=C5=BBuraw?= <9116238+krzysztofzuraw@users.noreply.github.com> Date: Thu, 15 May 2025 11:11:57 +0200 Subject: [PATCH 1/2] Add Saleor version to buildSyncWebhookResponsePayload --- .changeset/fluffy-adults-draw.md | 20 +++ .../shared/sync-webhook-response-builder.ts | 129 ++++++++++++------ 2 files changed, 108 insertions(+), 41 deletions(-) create mode 100644 .changeset/fluffy-adults-draw.md diff --git a/.changeset/fluffy-adults-draw.md b/.changeset/fluffy-adults-draw.md new file mode 100644 index 00000000..b0732beb --- /dev/null +++ b/.changeset/fluffy-adults-draw.md @@ -0,0 +1,20 @@ +--- +"@saleor/app-sdk": patch +--- + +Added optional second generic parameter to `buildSyncWebhookResponsePayload` called `SaleorVersion`. +This change improves TypeScript type safety when working with different Saleor versions that have varying payload requirements. + +After this change you can for example use `buildSyncWebhookResponsePayload` with different version and differently type responses: + +```ts +// 3.20 is default `SaleorVersion` so you can also write `buildSyncWebhookResponsePayload` +const respOne = buildSyncWebhookResponsePayload<"TRANSACTION_CHARGE_REQUESTED", "3.20">({ + result: "CHARGE_SUCCESS", + amount: 100, // Required in 3.20 +}); + +const respTwo = buildSyncWebhookResponsePayload<"TRANSACTION_CHARGE_REQUESTED", "3.21">({ + result: "CHARGE_SUCCESS", // amount is optional in 3.21 +}); +``` diff --git a/src/handlers/shared/sync-webhook-response-builder.ts b/src/handlers/shared/sync-webhook-response-builder.ts index 3477a0c4..f9ac1c08 100644 --- a/src/handlers/shared/sync-webhook-response-builder.ts +++ b/src/handlers/shared/sync-webhook-response-builder.ts @@ -1,36 +1,6 @@ -import { SyncWebhookEventType } from "../../types"; - type TransactionActions = "CHARGE" | "REFUND" | "CANCEL"; -export type SyncWebhookResponsesMap = { - CHECKOUT_CALCULATE_TAXES: { - shipping_price_gross_amount: number; - shipping_price_net_amount: number; - shipping_tax_rate: number; - lines: Array<{ - total_gross_amount: number; - total_net_amount: number; - tax_rate: number; - }>; - }; - CHECKOUT_FILTER_SHIPPING_METHODS: { - excluded_methods: Array<{ - id: string; - reason?: string; - }>; - }; - ORDER_CALCULATE_TAXES: SyncWebhookResponsesMap["CHECKOUT_CALCULATE_TAXES"]; - ORDER_FILTER_SHIPPING_METHODS: SyncWebhookResponsesMap["CHECKOUT_FILTER_SHIPPING_METHODS"]; - SHIPPING_LIST_METHODS_FOR_CHECKOUT: Array<{ - id: string; - name?: string; - amount: number; - currency: string; // or enum? - /** - * Integer - */ - maximum_delivery_days?: number; - }>; +type _320TransactionWebhookResponsesMap = { // https://docs.saleor.io/developer/extending/webhooks/synchronous-events/transaction#response TRANSACTION_CHARGE_REQUESTED: { result: "CHARGE_SUCCESS" | "CHARGE_FAILURE"; @@ -61,9 +31,6 @@ export type SyncWebhookResponsesMap = { message?: string; actions?: readonly TransactionActions[]; }; - PAYMENT_GATEWAY_INITIALIZE_SESSION: { - data: unknown; - }; // https://docs.saleor.io/developer/extending/webhooks/synchronous-events/transaction#response-4 TRANSACTION_INITIALIZE_SESSION: { result: @@ -75,7 +42,7 @@ export type SyncWebhookResponsesMap = { | "AUTHORIZATION_FAILURE" | "AUTHORIZATION_REQUEST" | "AUTHORIZATION_ACTION_REQUIRED"; - amount: number; + amount?: number; pspReference?: string; data?: unknown; time?: string; @@ -83,8 +50,41 @@ export type SyncWebhookResponsesMap = { message?: string; actions?: readonly TransactionActions[]; }; - // https://docs.saleor.io/developer/extending/webhooks/synchronous-events/transaction#response-5 - TRANSACTION_PROCESS_SESSION: { +}; + +type _321TransactionWebhookResponsesMap = { + // https://docs.saleor.io/developer/extending/webhooks/synchronous-events/transaction#response + TRANSACTION_CHARGE_REQUESTED: { + result: "CHARGE_SUCCESS" | "CHARGE_FAILURE"; + amount?: number; + pspReference?: string; + time?: string; + externalUrl?: string; + message?: string; + actions?: readonly TransactionActions[]; + }; + // https://docs.saleor.io/developer/extending/webhooks/synchronous-events/transaction#sync-flow-2 + TRANSACTION_REFUND_REQUESTED: { + result: "REFUND_SUCCESS" | "REFUND_FAILURE"; + amount?: number; + pspReference?: string; + time?: string; + externalUrl?: string; + message?: string; + actions?: readonly TransactionActions[]; + }; + // https://docs.saleor.io/developer/extending/webhooks/synchronous-events/transaction#response-1 + TRANSACTION_CANCELATION_REQUESTED: { + result: "CANCEL_SUCCESS" | "CANCEL_FAILURE"; + amount?: number; + pspReference?: string; + time?: string; + externalUrl?: string; + message?: string; + actions?: readonly TransactionActions[]; + }; + // https://docs.saleor.io/developer/extending/webhooks/synchronous-events/transaction#response-4 + TRANSACTION_INITIALIZE_SESSION: { result: | "CHARGE_SUCCESS" | "CHARGE_FAILURE" @@ -94,7 +94,7 @@ export type SyncWebhookResponsesMap = { | "AUTHORIZATION_FAILURE" | "AUTHORIZATION_REQUEST" | "AUTHORIZATION_ACTION_REQUIRED"; - amount: number; + amount?: number; pspReference?: string; data?: unknown; time?: string; @@ -102,6 +102,38 @@ export type SyncWebhookResponsesMap = { message?: string; actions?: readonly TransactionActions[]; }; +}; + +type CoreSyncWebhookResponses = { + CHECKOUT_CALCULATE_TAXES: { + shipping_price_gross_amount: number; + shipping_price_net_amount: number; + shipping_tax_rate: number; + lines: Array<{ + total_gross_amount: number; + total_net_amount: number; + tax_rate: number; + }>; + }; + CHECKOUT_FILTER_SHIPPING_METHODS: { + excluded_methods: Array<{ + id: string; + reason?: string; + }>; + }; + SHIPPING_LIST_METHODS_FOR_CHECKOUT: Array<{ + id: string; + name?: string; + amount: number; + currency: string; // or enum? + /** + * Integer + */ + maximum_delivery_days?: number; + }>; + PAYMENT_GATEWAY_INITIALIZE_SESSION: { + data: unknown; + }; PAYMENT_METHOD_PROCESS_TOKENIZATION_SESSION: | { result: "SUCCESSFULLY_TOKENIZED"; @@ -175,9 +207,24 @@ export type SyncWebhookResponsesMap = { }; }; +type SaleorVersion = "3.20" | "3.21"; + +type TransactionWebhookResponses = V extends "3.21" + ? _321TransactionWebhookResponsesMap + : _320TransactionWebhookResponsesMap; + +export type SyncWebhookResponsesMap = CoreSyncWebhookResponses & { + // those two are extracted here to avoid circular dependency of types + ORDER_CALCULATE_TAXES: CoreSyncWebhookResponses["CHECKOUT_CALCULATE_TAXES"]; + ORDER_FILTER_SHIPPING_METHODS: CoreSyncWebhookResponses["CHECKOUT_FILTER_SHIPPING_METHODS"]; +} & TransactionWebhookResponses; + /** * Identity function, but it works on Typescript level to pick right payload based on first param */ -export const buildSyncWebhookResponsePayload = ( - payload: SyncWebhookResponsesMap[E], -): SyncWebhookResponsesMap[E] => payload; +export const buildSyncWebhookResponsePayload = < + E extends keyof SyncWebhookResponsesMap, + V extends SaleorVersion = "3.20", +>( + payload: SyncWebhookResponsesMap[E], +): SyncWebhookResponsesMap[E] => payload; From da986692652dde55c28dbfd72b3b98b784bf75f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20=C5=BBuraw?= <9116238+krzysztofzuraw@users.noreply.github.com> Date: Mon, 19 May 2025 09:02:19 +0200 Subject: [PATCH 2/2] fix missing webhook response --- .../shared/sync-webhook-response-builder.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/handlers/shared/sync-webhook-response-builder.ts b/src/handlers/shared/sync-webhook-response-builder.ts index f9ac1c08..c5b6f798 100644 --- a/src/handlers/shared/sync-webhook-response-builder.ts +++ b/src/handlers/shared/sync-webhook-response-builder.ts @@ -50,6 +50,25 @@ type _320TransactionWebhookResponsesMap = { message?: string; actions?: readonly TransactionActions[]; }; + // https://docs.saleor.io/developer/extending/webhooks/synchronous-events/transaction#response-5 + TRANSACTION_PROCESS_SESSION: { + result: + | "CHARGE_SUCCESS" + | "CHARGE_FAILURE" + | "CHARGE_REQUEST" + | "CHARGE_ACTION_REQUIRED" + | "AUTHORIZATION_SUCCESS" + | "AUTHORIZATION_FAILURE" + | "AUTHORIZATION_REQUEST" + | "AUTHORIZATION_ACTION_REQUIRED"; + amount: number; + pspReference?: string; + data?: unknown; + time?: string; + externalUrl?: string; + message?: string; + actions?: readonly TransactionActions[]; + }; }; type _321TransactionWebhookResponsesMap = { @@ -102,6 +121,25 @@ type _321TransactionWebhookResponsesMap = { message?: string; actions?: readonly TransactionActions[]; }; + // https://docs.saleor.io/developer/extending/webhooks/synchronous-events/transaction#response-5 + TRANSACTION_PROCESS_SESSION: { + result: + | "CHARGE_SUCCESS" + | "CHARGE_FAILURE" + | "CHARGE_REQUEST" + | "CHARGE_ACTION_REQUIRED" + | "AUTHORIZATION_SUCCESS" + | "AUTHORIZATION_FAILURE" + | "AUTHORIZATION_REQUEST" + | "AUTHORIZATION_ACTION_REQUIRED"; + amount?: number; + pspReference?: string; + data?: unknown; + time?: string; + externalUrl?: string; + message?: string; + actions?: readonly TransactionActions[]; + }; }; type CoreSyncWebhookResponses = {