Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .changeset/fluffy-adults-draw.md
Original file line number Diff line number Diff line change
@@ -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<TRANSACTION_CHARGE_REQUESTED>`
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
});
```
161 changes: 123 additions & 38 deletions src/handlers/shared/sync-webhook-response-builder.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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:
Expand All @@ -75,7 +42,7 @@ export type SyncWebhookResponsesMap = {
| "AUTHORIZATION_FAILURE"
| "AUTHORIZATION_REQUEST"
| "AUTHORIZATION_ACTION_REQUIRED";
amount: number;
amount?: number;
pspReference?: string;
data?: unknown;
time?: string;
Expand All @@ -102,6 +69,109 @@ export type SyncWebhookResponsesMap = {
message?: string;
actions?: readonly TransactionActions[];
};
};

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"
| "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[];
};
// 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 = {
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";
Expand Down Expand Up @@ -175,9 +245,24 @@ export type SyncWebhookResponsesMap = {
};
};

type SaleorVersion = "3.20" | "3.21";

type TransactionWebhookResponses<V extends SaleorVersion> = V extends "3.21"
? _321TransactionWebhookResponsesMap
: _320TransactionWebhookResponsesMap;

export type SyncWebhookResponsesMap<V extends SaleorVersion = "3.20"> = 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<V>;

/**
* Identity function, but it works on Typescript level to pick right payload based on first param
*/
export const buildSyncWebhookResponsePayload = <E extends SyncWebhookEventType>(
payload: SyncWebhookResponsesMap[E],
): SyncWebhookResponsesMap[E] => payload;
export const buildSyncWebhookResponsePayload = <
E extends keyof SyncWebhookResponsesMap<V>,
V extends SaleorVersion = "3.20",
>(
payload: SyncWebhookResponsesMap<V>[E],
): SyncWebhookResponsesMap<V>[E] => payload;
Loading