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 {
id
privateMetadata {
key
value
}
externalId: metafield(key: "externalId")
channel {
id
slug

View file

@ -53,6 +53,11 @@ export type ValidateAddressArgs = {
address: AvataxAddress;
};
export type VoidTransactionArgs = {
transactionCode: string;
companyCode: string;
};
export class AvataxClient {
private client: Avatax;
private logger: Logger;
@ -73,6 +78,16 @@ export class AvataxClient {
return this.client.commitTransaction(args);
}
async voidTransaction({
transactionCode,
companyCode,
}: {
transactionCode: string;
companyCode: string;
}) {
return this.client.voidTransaction({ transactionCode, companyCode });
}
async validateAddress({ address }: ValidateAddressArgs) {
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 { AvataxOrderCreatedAdapter } from "./order-created/avatax-order-created-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 {
config = defaultAvataxConfig;
@ -52,9 +54,10 @@ export class AvataxWebhookService implements ProviderWebhookService {
return response;
}
async cancelOrder(payload: OrderCancelledEventSubscriptionFragment) {
// todo: implement
async cancelOrder(payload: OrderCancelledPayload) {
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 { TaxJarWebhookService } from "../taxjar/taxjar-webhook.service";
import { ProviderWebhookService } from "./tax-provider-webhook";
import { OrderCancelledPayload } from "../../pages/api/webhooks/order-cancelled";
// todo: refactor to a factory
class ActiveTaxProviderService implements ProviderWebhookService {
@ -57,8 +58,8 @@ class ActiveTaxProviderService implements ProviderWebhookService {
return this.client.fulfillOrder(payload);
}
async cancelOrder(payload: OrderCancelledEventSubscriptionFragment) {
return this.client.cancelOrder(payload);
async cancelOrder(payload: OrderCancelledPayload) {
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.
* Should only be used for values that are required for further calculation.
*/
function resolveOptionalOrThrow<T>(value: T | undefined, error?: Error): T {
if (value === undefined) {
function resolveOptionalOrThrow<T>(value: T | undefined | null, error?: Error): T {
if (value === undefined || value === null) {
throw error
? error
: 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 {
OrderCancelledEventSubscriptionFragment,
OrderCreatedSubscriptionFragment,
OrderFulfilledSubscriptionFragment,
TaxBaseFragment,
} from "../../../generated/graphql";
import { OrderCancelledPayload } from "../../pages/api/webhooks/order-cancelled";
export type CalculateTaxesResponse = SyncWebhookResponsesMap["ORDER_CALCULATE_TAXES"];
@ -14,5 +14,5 @@ export interface ProviderWebhookService {
calculateTaxes: (payload: TaxBaseFragment) => Promise<CalculateTaxesResponse>;
createOrder: (payload: OrderCreatedSubscriptionFragment) => Promise<CreateOrderResponse>;
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) {
// todo: implement
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,
{ __typename: "OrderCancelled" }
>;