feat: add order-cancelled avatax adapter

This commit is contained in:
Adrian Pilarczyk 2023-07-28 15:16:20 +02:00
parent 0013af2f6d
commit d458452573
10 changed files with 82 additions and 15 deletions

View file

@ -1,9 +1,6 @@
fragment OrderCancelledSubscription on Order { fragment OrderCancelledSubscription on Order {
id id
privateMetadata { externalId: metafield(key: "externalId")
key
value
}
channel { channel {
id id
slug slug

View file

@ -53,6 +53,11 @@ export type ValidateAddressArgs = {
address: AvataxAddress; address: AvataxAddress;
}; };
export type VoidTransactionArgs = {
transactionCode: string;
companyCode: string;
};
export class AvataxClient { export class AvataxClient {
private client: Avatax; private client: Avatax;
private logger: Logger; private logger: Logger;
@ -73,6 +78,16 @@ export class AvataxClient {
return this.client.commitTransaction(args); return this.client.commitTransaction(args);
} }
async voidTransaction({
transactionCode,
companyCode,
}: {
transactionCode: string;
companyCode: string;
}) {
return this.client.voidTransaction({ transactionCode, companyCode });
}
async validateAddress({ address }: ValidateAddressArgs) { async validateAddress({ address }: ValidateAddressArgs) {
return this.client.resolveAddress(address); return this.client.resolveAddress(address);
} }

View file

@ -12,6 +12,8 @@ import { AvataxConfig, defaultAvataxConfig } from "./avatax-connection-schema";
import { AvataxCalculateTaxesAdapter } from "./calculate-taxes/avatax-calculate-taxes-adapter"; import { AvataxCalculateTaxesAdapter } from "./calculate-taxes/avatax-calculate-taxes-adapter";
import { AvataxOrderCreatedAdapter } from "./order-created/avatax-order-created-adapter"; import { AvataxOrderCreatedAdapter } from "./order-created/avatax-order-created-adapter";
import { AvataxOrderFulfilledAdapter } from "./order-fulfilled/avatax-order-fulfilled-adapter"; import { AvataxOrderFulfilledAdapter } from "./order-fulfilled/avatax-order-fulfilled-adapter";
import { AvataxOrderCancelledAdapter } from "./order-cancelled/avatax-order-cancelled-adapter";
import { OrderCancelledPayload } from "../../pages/api/webhooks/order-cancelled";
export class AvataxWebhookService implements ProviderWebhookService { export class AvataxWebhookService implements ProviderWebhookService {
config = defaultAvataxConfig; config = defaultAvataxConfig;
@ -52,9 +54,10 @@ export class AvataxWebhookService implements ProviderWebhookService {
return response; return response;
} }
async cancelOrder(payload: OrderCancelledEventSubscriptionFragment) { async cancelOrder(payload: OrderCancelledPayload) {
// todo: implement
this.logger.debug("cancelOrder", payload); this.logger.debug("cancelOrder", payload);
return { ok: true }; const adapter = new AvataxOrderCancelledAdapter(this.config);
await adapter.send(payload);
} }
} }

View file

@ -0,0 +1,31 @@
import { Logger, createLogger } from "../../../lib/logger";
import { OrderCancelledPayload } from "../../../pages/api/webhooks/order-cancelled";
import { WebhookAdapter } from "../../taxes/tax-webhook-adapter";
import { AvataxClient, VoidTransactionArgs } from "../avatax-client";
import { AvataxConfig } from "../avatax-connection-schema";
import { AvataxOrderCancelledPayloadTransformer } from "./avatax-order-cancelled-payload-transformer";
export type AvataxOrderCancelledTarget = VoidTransactionArgs;
export class AvataxOrderCancelledAdapter implements WebhookAdapter<OrderCancelledPayload, void> {
private logger: Logger;
constructor(private readonly config: AvataxConfig) {
this.logger = createLogger({ name: "AvataxOrderCancelledAdapter" });
}
async send(payload: OrderCancelledPayload) {
this.logger.debug("Transforming the Saleor payload for cancelling transaction with Avatax...");
const payloadTransformer = new AvataxOrderCancelledPayloadTransformer(this.config);
const target = payloadTransformer.transform({ ...payload });
this.logger.debug("Calling Avatax commitTransaction with transformed payload...");
const client = new AvataxClient(this.config);
await client.voidTransaction(target);
this.logger.debug("Avatax commitTransaction succesfully responded");
}
}

View file

@ -0,0 +1,21 @@
import { OrderCancelledPayload } from "../../../pages/api/webhooks/order-cancelled";
import { taxProviderUtils } from "../../taxes/tax-provider-utils";
import { AvataxConfig } from "../avatax-connection-schema";
import { AvataxOrderCancelledTarget } from "./avatax-order-cancelled-adapter";
export class AvataxOrderCancelledPayloadTransformer {
constructor(private readonly config: AvataxConfig) {}
transform({ order }: OrderCancelledPayload): AvataxOrderCancelledTarget {
if (!order) {
throw new Error("Order is required");
}
const transactionCode = taxProviderUtils.resolveOptionalOrThrow(order.externalId);
return {
transactionCode,
companyCode: this.config.companyCode ?? "",
};
}
}

View file

@ -13,6 +13,7 @@ import { AvataxWebhookService } from "../avatax/avatax-webhook.service";
import { ProviderConnection } from "../provider-connections/provider-connections"; import { ProviderConnection } from "../provider-connections/provider-connections";
import { TaxJarWebhookService } from "../taxjar/taxjar-webhook.service"; import { TaxJarWebhookService } from "../taxjar/taxjar-webhook.service";
import { ProviderWebhookService } from "./tax-provider-webhook"; import { ProviderWebhookService } from "./tax-provider-webhook";
import { OrderCancelledPayload } from "../../pages/api/webhooks/order-cancelled";
// todo: refactor to a factory // todo: refactor to a factory
class ActiveTaxProviderService implements ProviderWebhookService { class ActiveTaxProviderService implements ProviderWebhookService {
@ -57,8 +58,8 @@ class ActiveTaxProviderService implements ProviderWebhookService {
return this.client.fulfillOrder(payload); return this.client.fulfillOrder(payload);
} }
async cancelOrder(payload: OrderCancelledEventSubscriptionFragment) { async cancelOrder(payload: OrderCancelledPayload) {
return this.client.cancelOrder(payload); this.client.cancelOrder(payload);
} }
} }

View file

@ -3,8 +3,8 @@
* If it ever happens, we have nothing to fall back to, so we throw an error. * If it ever happens, we have nothing to fall back to, so we throw an error.
* Should only be used for values that are required for further calculation. * Should only be used for values that are required for further calculation.
*/ */
function resolveOptionalOrThrow<T>(value: T | undefined, error?: Error): T { function resolveOptionalOrThrow<T>(value: T | undefined | null, error?: Error): T {
if (value === undefined) { if (value === undefined || value === null) {
throw error throw error
? error ? error
: new Error("Could not resolve data. Value needed for further calculation is undefined."); : new Error("Could not resolve data. Value needed for further calculation is undefined.");

View file

@ -1,10 +1,10 @@
import { SyncWebhookResponsesMap } from "@saleor/app-sdk/handlers/next"; import { SyncWebhookResponsesMap } from "@saleor/app-sdk/handlers/next";
import { import {
OrderCancelledEventSubscriptionFragment,
OrderCreatedSubscriptionFragment, OrderCreatedSubscriptionFragment,
OrderFulfilledSubscriptionFragment, OrderFulfilledSubscriptionFragment,
TaxBaseFragment, TaxBaseFragment,
} from "../../../generated/graphql"; } from "../../../generated/graphql";
import { OrderCancelledPayload } from "../../pages/api/webhooks/order-cancelled";
export type CalculateTaxesResponse = SyncWebhookResponsesMap["ORDER_CALCULATE_TAXES"]; export type CalculateTaxesResponse = SyncWebhookResponsesMap["ORDER_CALCULATE_TAXES"];
@ -14,5 +14,5 @@ export interface ProviderWebhookService {
calculateTaxes: (payload: TaxBaseFragment) => Promise<CalculateTaxesResponse>; calculateTaxes: (payload: TaxBaseFragment) => Promise<CalculateTaxesResponse>;
createOrder: (payload: OrderCreatedSubscriptionFragment) => Promise<CreateOrderResponse>; createOrder: (payload: OrderCreatedSubscriptionFragment) => Promise<CreateOrderResponse>;
fulfillOrder: (payload: OrderFulfilledSubscriptionFragment) => Promise<{ ok: boolean }>; fulfillOrder: (payload: OrderFulfilledSubscriptionFragment) => Promise<{ ok: boolean }>;
cancelOrder: (payload: OrderCancelledEventSubscriptionFragment) => Promise<any>; cancelOrder: (payload: OrderCancelledPayload) => Promise<void>;
} }

View file

@ -50,6 +50,5 @@ export class TaxJarWebhookService implements ProviderWebhookService {
async cancelOrder(payload: OrderCancelledEventSubscriptionFragment) { async cancelOrder(payload: OrderCancelledEventSubscriptionFragment) {
// todo: implement // todo: implement
this.logger.debug("cancelOrder", payload); this.logger.debug("cancelOrder", payload);
return { ok: true };
} }
} }

View file

@ -13,7 +13,7 @@ export const config = {
}, },
}; };
type OrderCancelledPayload = Extract< export type OrderCancelledPayload = Extract<
OrderCancelledEventSubscriptionFragment, OrderCancelledEventSubscriptionFragment,
{ __typename: "OrderCancelled" } { __typename: "OrderCancelled" }
>; >;