fix/333 taxes not calculated (#345)
* refactor: 🔊 improve logging in taxes * refactor: 🔥 redundant channels call * refactor: ♻️ get app config from payload * build: add changeset * refactor: 🔊 routers info -> debug * refactor: 🔊 redact logs
This commit is contained in:
parent
741e9104ed
commit
d55b2f9b2a
23 changed files with 174 additions and 105 deletions
5
.changeset/many-numbers-smile.md
Normal file
5
.changeset/many-numbers-smile.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"saleor-app-taxes": minor
|
||||
---
|
||||
|
||||
Improve webhook processing time by adding the app config to subscription payload instead of fetching it separately.
|
|
@ -4,5 +4,11 @@ fragment CalculateTaxesEvent on Event {
|
|||
taxBase {
|
||||
...TaxBase
|
||||
}
|
||||
recipient {
|
||||
privateMetadata {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,12 @@ export const logger = pino({
|
|||
},
|
||||
}
|
||||
: undefined,
|
||||
redact: [
|
||||
"metadata",
|
||||
"providerInstance.config.username",
|
||||
"providerInstance.config.password",
|
||||
"providerInstance.config.apiKey",
|
||||
],
|
||||
});
|
||||
|
||||
export const createLogger = logger.child.bind(logger);
|
||||
|
|
|
@ -65,6 +65,14 @@ const taxBaseLineSchema = z.object({
|
|||
|
||||
export const calculateTaxesPayloadSchema: z.ZodType<ExpectedWebhookPayload> = z.object({
|
||||
__typename: z.literal("CalculateTaxes"),
|
||||
recipient: z.object({
|
||||
privateMetadata: z.array(
|
||||
z.object({
|
||||
key: z.string(),
|
||||
value: z.string(),
|
||||
})
|
||||
),
|
||||
}),
|
||||
taxBase: z.object({
|
||||
currency: z.string(),
|
||||
channel: z.object({
|
||||
|
|
39
apps/taxes/src/modules/app-configuration/get-app-config.ts
Normal file
39
apps/taxes/src/modules/app-configuration/get-app-config.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { decrypt } from "@saleor/app-sdk/settings-manager";
|
||||
import { ExpectedWebhookPayload } from "../../lib/saleor/schema";
|
||||
import { ChannelsConfig, channelsSchema } from "../channels-configuration/channels-config";
|
||||
import { ProvidersConfig, providersSchema } from "../providers-configuration/providers-config";
|
||||
|
||||
export const getAppConfig = (payload: ExpectedWebhookPayload) => {
|
||||
const metadata = payload.recipient?.privateMetadata;
|
||||
let providersConfig = [] as ProvidersConfig;
|
||||
let channelsConfig = {} as ChannelsConfig;
|
||||
|
||||
const secretKey = process.env.SECRET_KEY;
|
||||
|
||||
if (!secretKey) {
|
||||
throw new Error("SECRET_KEY env variable is not set");
|
||||
}
|
||||
|
||||
// * The App Config contains two types of data: providers and channels.
|
||||
// * We must recognize which one we are dealing with and parse it accordingly.
|
||||
metadata?.forEach((item) => {
|
||||
const decrypted = decrypt(item.value, secretKey);
|
||||
const parsed = JSON.parse(decrypted);
|
||||
|
||||
const providersValidation = providersSchema.safeParse(parsed);
|
||||
|
||||
if (providersValidation.success) {
|
||||
providersConfig = providersValidation.data;
|
||||
return;
|
||||
}
|
||||
|
||||
const channelsValidation = channelsSchema.safeParse(parsed);
|
||||
|
||||
if (channelsValidation.success) {
|
||||
channelsConfig = channelsValidation.data;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
return { providers: providersConfig, channels: channelsConfig };
|
||||
};
|
|
@ -5,12 +5,19 @@ import {
|
|||
FetchAppDetailsQuery,
|
||||
UpdateAppMetadataDocument,
|
||||
} from "../../../generated/graphql";
|
||||
import { logger as pinoLogger } from "../../lib/logger";
|
||||
|
||||
export async function fetchAllMetadata(client: Client): Promise<MetadataEntry[]> {
|
||||
const logger = pinoLogger.child({ service: "fetchAllMetadata" });
|
||||
logger.debug("Fetching metadata from Saleor");
|
||||
|
||||
const { error, data } = await client
|
||||
.query<FetchAppDetailsQuery>(FetchAppDetailsDocument, {})
|
||||
.toPromise();
|
||||
|
||||
// * `metadata` name is required for secrets censorship
|
||||
logger.debug({ error, metadata: data }, "Metadata fetched");
|
||||
|
||||
if (error) {
|
||||
return [];
|
||||
}
|
||||
|
@ -19,11 +26,15 @@ export async function fetchAllMetadata(client: Client): Promise<MetadataEntry[]>
|
|||
}
|
||||
|
||||
export async function mutateMetadata(client: Client, metadata: MetadataEntry[]) {
|
||||
const logger = pinoLogger.child({ service: "mutateMetadata" });
|
||||
logger.debug({ metadata }, "Mutating metadata");
|
||||
// to update the metadata, ID is required
|
||||
const { error: idQueryError, data: idQueryData } = await client
|
||||
.query(FetchAppDetailsDocument, {})
|
||||
.toPromise();
|
||||
|
||||
logger.debug({ error: idQueryError, data: idQueryData }, "Metadata mutated");
|
||||
|
||||
if (idQueryError) {
|
||||
throw new Error(
|
||||
"Could not fetch the app id. Please check if auth data for the client are valid."
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import Avatax from "avatax";
|
||||
import { CreateTransactionModel } from "avatax/lib/models/CreateTransactionModel";
|
||||
import pino from "pino";
|
||||
import packageJson from "../../../package.json";
|
||||
import { logger } from "../../lib/logger";
|
||||
import { createLogger } from "../../lib/logger";
|
||||
import { AvataxConfig } from "./avatax-config";
|
||||
|
||||
type AvataxSettings = {
|
||||
|
@ -36,9 +37,11 @@ const createAvataxSettings = (config: AvataxConfig): AvataxSettings => {
|
|||
|
||||
export class AvataxClient {
|
||||
private client: Avatax;
|
||||
private logger: pino.Logger;
|
||||
|
||||
constructor(config: AvataxConfig) {
|
||||
logger.debug("AvataxClient constructor");
|
||||
this.logger = createLogger({ service: "AvataxClient" });
|
||||
this.logger.trace("AvataxClient constructor");
|
||||
const { username, password } = config;
|
||||
const credentials = {
|
||||
username,
|
||||
|
@ -46,15 +49,18 @@ export class AvataxClient {
|
|||
};
|
||||
const settings = createAvataxSettings(config);
|
||||
const avataxClient = new Avatax(settings).withSecurity(credentials);
|
||||
logger.info({ client: avataxClient }, "External Avatax client created");
|
||||
this.logger.trace({ client: avataxClient }, "External Avatax client created");
|
||||
this.client = avataxClient;
|
||||
}
|
||||
|
||||
async fetchTaxesForOrder(model: CreateTransactionModel) {
|
||||
this.logger.debug({ model }, "fetchTaxesForOrder called with:");
|
||||
|
||||
return this.client.createTransaction({ model });
|
||||
}
|
||||
|
||||
async ping() {
|
||||
this.logger.debug("ping called");
|
||||
try {
|
||||
const result = await this.client.ping();
|
||||
|
||||
|
|
|
@ -37,14 +37,15 @@ export const avataxConfigurationRouter = router({
|
|||
procedure: "avataxConfigurationRouter.get",
|
||||
});
|
||||
|
||||
logger.debug("avataxConfigurationRouter.get called");
|
||||
logger.debug({ input }, "avataxConfigurationRouter.get called with:");
|
||||
|
||||
const { apiClient, saleorApiUrl } = ctx;
|
||||
const avataxConfigurationService = new AvataxConfigurationService(apiClient, saleorApiUrl);
|
||||
|
||||
const result = await avataxConfigurationService.get(input.id);
|
||||
|
||||
logger.debug({ result }, "avataxConfigurationRouter.get finished");
|
||||
// * `providerInstance` name is required for secrets censorship
|
||||
logger.debug({ providerInstance: result }, "avataxConfigurationRouter.get finished");
|
||||
|
||||
return { ...result, config: obfuscateAvataxConfig(result.config) };
|
||||
}),
|
||||
|
@ -54,7 +55,7 @@ export const avataxConfigurationRouter = router({
|
|||
procedure: "avataxConfigurationRouter.post",
|
||||
});
|
||||
|
||||
logger.debug("avataxConfigurationRouter.post called");
|
||||
logger.debug({ input }, "avataxConfigurationRouter.post called with:");
|
||||
|
||||
const { apiClient, saleorApiUrl } = ctx;
|
||||
const avataxConfigurationService = new AvataxConfigurationService(apiClient, saleorApiUrl);
|
||||
|
@ -71,7 +72,7 @@ export const avataxConfigurationRouter = router({
|
|||
procedure: "avataxConfigurationRouter.delete",
|
||||
});
|
||||
|
||||
logger.debug("avataxConfigurationRouter.delete called");
|
||||
logger.debug({ input }, "avataxConfigurationRouter.delete called with:");
|
||||
|
||||
const { apiClient, saleorApiUrl } = ctx;
|
||||
const avataxConfigurationService = new AvataxConfigurationService(apiClient, saleorApiUrl);
|
||||
|
@ -88,7 +89,7 @@ export const avataxConfigurationRouter = router({
|
|||
procedure: "avataxConfigurationRouter.patch",
|
||||
});
|
||||
|
||||
logger.debug("avataxConfigurationRouter.patch called");
|
||||
logger.debug({ input }, "avataxConfigurationRouter.patch called with:");
|
||||
|
||||
const { apiClient, saleorApiUrl } = ctx;
|
||||
const avataxConfigurationService = new AvataxConfigurationService(apiClient, saleorApiUrl);
|
||||
|
|
|
@ -45,7 +45,7 @@ export class AvataxConfigurationService {
|
|||
async get(id: string): Promise<AvataxInstanceConfig> {
|
||||
this.logger.debug(`.get called with id: ${id}`);
|
||||
const { data } = await this.crudSettingsConfigurator.read(id);
|
||||
this.logger.debug({ setting: data }, `Fetched setting from crudSettingsConfigurator`);
|
||||
this.logger.debug(`Fetched setting from crudSettingsConfigurator`);
|
||||
|
||||
const validation = getSchema.safeParse(data);
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import pino from "pino";
|
||||
import { TaxBaseFragment } from "../../../generated/graphql";
|
||||
import { logger } from "../../lib/logger";
|
||||
import { createLogger } from "../../lib/logger";
|
||||
import { ChannelConfig } from "../channels-configuration/channels-config";
|
||||
import { TaxProvider } from "../taxes/tax-provider";
|
||||
import { avataxCalculate } from "./avatax-calculate";
|
||||
|
@ -10,21 +11,24 @@ export class AvataxProvider implements TaxProvider {
|
|||
readonly name = "avatax";
|
||||
config = defaultAvataxConfig;
|
||||
client: AvataxClient;
|
||||
private logger: pino.Logger;
|
||||
|
||||
constructor(config: AvataxConfig) {
|
||||
this.logger = createLogger({
|
||||
service: "AvataxProvider",
|
||||
});
|
||||
const avataxClient = new AvataxClient(config);
|
||||
logger.info({ client: avataxClient }, "Internal Avatax client created");
|
||||
this.logger.trace({ client: avataxClient }, "Internal Avatax client created");
|
||||
|
||||
this.config = config;
|
||||
this.client = avataxClient;
|
||||
}
|
||||
|
||||
async calculate(payload: TaxBaseFragment, channel: ChannelConfig) {
|
||||
logger.info("Avatax calculate");
|
||||
this.logger.debug({ payload, channel }, "Avatax calculate called with:");
|
||||
const model = avataxCalculate.preparePayload(payload, channel, this.config);
|
||||
logger.info(model, "Payload used for Avatax fetchTaxesForOrder");
|
||||
const result = await this.client.fetchTaxesForOrder(model);
|
||||
logger.info({ createOrderTransaction: result }, "Avatax createOrderTransaction response");
|
||||
this.logger.debug({ createOrderTransaction: result }, "Avatax createOrderTransaction response");
|
||||
return avataxCalculate.prepareResponse(result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ export const channelSchema = z.object({
|
|||
});
|
||||
export type ChannelConfig = z.infer<typeof channelSchema>;
|
||||
|
||||
const channelsSchema = z.record(channelSchema);
|
||||
export const channelsSchema = z.record(channelSchema);
|
||||
export type ChannelsConfig = z.infer<typeof channelsSchema>;
|
||||
|
||||
export const defaultChannelConfig: ChannelConfig = {
|
||||
|
|
|
@ -7,9 +7,13 @@ import { setAndReplaceChannelsInputSchema } from "./channels-config-input-schema
|
|||
import { TaxChannelsConfigurator } from "./channels-configurator";
|
||||
import { GetChannelsConfigurationService } from "./get-channels-configuration.service";
|
||||
|
||||
// todo: refactor with crud-settings
|
||||
export const channelsConfigurationRouter = router({
|
||||
fetch: protectedClientProcedure.query(async ({ ctx, input }) => {
|
||||
const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl });
|
||||
const logger = pinoLogger.child({
|
||||
saleorApiUrl: ctx.saleorApiUrl,
|
||||
procedure: "channelsConfigurationRouter.fetch",
|
||||
});
|
||||
|
||||
logger.debug("channelsConfigurationRouter.fetch called");
|
||||
|
||||
|
@ -21,14 +25,17 @@ export const channelsConfigurationRouter = router({
|
|||
upsert: protectedClientProcedure
|
||||
.input(setAndReplaceChannelsInputSchema)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl });
|
||||
logger.info(input, "channelsConfigurationRouter.upsert called with input");
|
||||
const logger = pinoLogger.child({
|
||||
saleorApiUrl: ctx.saleorApiUrl,
|
||||
procedure: "channelsConfigurationRouter.upsert",
|
||||
});
|
||||
logger.debug(input, "channelsConfigurationRouter.upsert called with input");
|
||||
|
||||
const config = await new GetChannelsConfigurationService({
|
||||
apiClient: ctx.apiClient,
|
||||
saleorApiUrl: ctx.saleorApiUrl,
|
||||
}).getConfiguration();
|
||||
logger.info(config, "Fetched current channels config to update it");
|
||||
logger.debug(config, "Fetched current channels config to update it");
|
||||
|
||||
const taxChannelsConfigurator = new TaxChannelsConfigurator(
|
||||
createSettingsManager(ctx.apiClient),
|
||||
|
@ -43,7 +50,7 @@ export const channelsConfigurationRouter = router({
|
|||
},
|
||||
};
|
||||
|
||||
logger.info(channelsConfig, "Merged configs. Will set it now");
|
||||
logger.debug(channelsConfig, "Merged configs. Will set it now");
|
||||
|
||||
await taxChannelsConfigurator.setConfig(channelsConfig);
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { Client } from "urql";
|
||||
import { logger as pinoLogger } from "../../lib/logger";
|
||||
import { createSettingsManager } from "../app-configuration/metadata-manager";
|
||||
import { ChannelsFetcher } from "../channels/channels-fetcher";
|
||||
import { createDefaultChannelsConfig } from "./channels-config";
|
||||
import { TaxChannelsConfigurator } from "./channels-configurator";
|
||||
|
||||
export class GetChannelsConfigurationService {
|
||||
|
@ -26,17 +24,11 @@ export class GetChannelsConfigurationService {
|
|||
saleorApiUrl
|
||||
);
|
||||
|
||||
const channelsFetcher = new ChannelsFetcher(apiClient);
|
||||
const channels = await channelsFetcher.fetchChannels();
|
||||
logger.info({ channels }, "Fetched Saleor channels that use TAX_APP for tax calculation");
|
||||
const defaultConfig = createDefaultChannelsConfig(channels ?? []);
|
||||
logger.info({ defaultConfig }, "Generated config from Saleor channels");
|
||||
|
||||
// todo: validate config
|
||||
const appChannelsConfig = (await taxConfigurator.getConfig()) ?? null;
|
||||
|
||||
logger.debug(appChannelsConfig, "Retrieved channels config from Metadata");
|
||||
|
||||
return { ...defaultConfig, ...appChannelsConfig };
|
||||
return { ...appChannelsConfig };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,11 +20,11 @@ export class CrudSettingsConfigurator {
|
|||
}
|
||||
|
||||
async readAll() {
|
||||
this.logger.debug(".readAll called");
|
||||
this.logger.trace(".readAll called");
|
||||
const result = await this.metadataManager.get(this.metadataKey, this.saleorApiUrl);
|
||||
|
||||
if (!result) {
|
||||
this.logger.debug("No metadata found");
|
||||
this.logger.trace("No metadata found");
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ export class CrudSettingsConfigurator {
|
|||
}
|
||||
|
||||
async read(id: string) {
|
||||
this.logger.debug(".read called");
|
||||
this.logger.trace(".read called");
|
||||
const result = await this.readAll();
|
||||
const { data: settings } = result;
|
||||
|
||||
|
@ -58,7 +58,7 @@ export class CrudSettingsConfigurator {
|
|||
}
|
||||
|
||||
async create(data: any) {
|
||||
this.logger.debug(data, ".create called with:");
|
||||
this.logger.trace(data, ".create called with:");
|
||||
|
||||
const settings = await this.readAll();
|
||||
const prevData = settings.data;
|
||||
|
@ -77,7 +77,7 @@ export class CrudSettingsConfigurator {
|
|||
}
|
||||
|
||||
async delete(id: string) {
|
||||
this.logger.debug(`.delete called with: ${id}`);
|
||||
this.logger.trace(`.delete called with: ${id}`);
|
||||
|
||||
const settings = await this.readAll();
|
||||
const prevData = settings.data;
|
||||
|
@ -91,7 +91,7 @@ export class CrudSettingsConfigurator {
|
|||
}
|
||||
|
||||
async update(id: string, data: any) {
|
||||
this.logger.debug(data, `.update called with: ${id}`);
|
||||
this.logger.trace(data, `.update called with: ${id}`);
|
||||
const { data: settings } = await this.readAll();
|
||||
const nextData = settings.map((item) => {
|
||||
if (item.id === id) {
|
||||
|
|
|
@ -5,7 +5,10 @@ import { PublicTaxProvidersConfigurationService } from "./public-providers-confi
|
|||
|
||||
export const providersConfigurationRouter = router({
|
||||
getAll: protectedClientProcedure.query(async ({ ctx }) => {
|
||||
const logger = pinoLogger.child({ saleorApiUrl: ctx.saleorApiUrl });
|
||||
const logger = pinoLogger.child({
|
||||
saleorApiUrl: ctx.saleorApiUrl,
|
||||
procedure: "providersConfigurationRouter.getAll",
|
||||
});
|
||||
|
||||
logger.debug("providersConfigurationRouter.fetch called");
|
||||
|
||||
|
|
|
@ -6,15 +6,19 @@ import { AvataxProvider } from "../avatax/avatax-provider";
|
|||
import { TaxJarProvider } from "../taxjar/taxjar-provider";
|
||||
import { TaxProvider } from "./tax-provider";
|
||||
import { TaxProviderError } from "./tax-provider-error";
|
||||
import pino from "pino";
|
||||
|
||||
export class ActiveTaxProvider {
|
||||
private client: TaxProvider;
|
||||
private logger: pino.Logger;
|
||||
|
||||
constructor(providerInstance: ProviderConfig) {
|
||||
const logger = createLogger({});
|
||||
this.logger = createLogger({
|
||||
service: "ActiveTaxProvider",
|
||||
});
|
||||
|
||||
const taxProviderName = providerInstance.provider;
|
||||
logger.info({ taxProviderName }, "Constructing tax provider: ");
|
||||
this.logger.trace({ taxProviderName }, "Constructing tax provider: ");
|
||||
|
||||
switch (taxProviderName) {
|
||||
case "taxjar":
|
||||
|
@ -34,6 +38,8 @@ export class ActiveTaxProvider {
|
|||
}
|
||||
|
||||
async calculate(payload: TaxBaseFragment, channel: ChannelConfig) {
|
||||
this.logger.debug({ payload, channel }, ".calculate called");
|
||||
|
||||
return this.client.calculate(payload, channel);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { TaxBaseFragment } from "../../../generated/graphql";
|
||||
import { ResponseTaxPayload } from "./types";
|
||||
import { ChannelConfig } from "../channels-configuration/channels-config";
|
||||
import { TaxProviderName } from "./providers/config";
|
||||
|
||||
type ExternalValidationResult = { ok: boolean; error?: string };
|
||||
import { ResponseTaxPayload } from "./types";
|
||||
|
||||
export interface TaxProvider {
|
||||
name: TaxProviderName;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import pino from "pino";
|
||||
import TaxJar from "taxjar";
|
||||
import { Config, TaxForOrderRes, TaxParams } from "taxjar/dist/util/types";
|
||||
import { logger } from "../../lib/logger";
|
||||
import { createLogger } from "../../lib/logger";
|
||||
import { TaxJarConfig } from "./taxjar-config";
|
||||
|
||||
const createTaxJarSettings = (config: TaxJarConfig): Config => {
|
||||
|
@ -14,21 +15,25 @@ const createTaxJarSettings = (config: TaxJarConfig): Config => {
|
|||
|
||||
export class TaxJarClient {
|
||||
private client: TaxJar;
|
||||
private logger: pino.Logger;
|
||||
|
||||
constructor(providerConfig: TaxJarConfig) {
|
||||
logger.debug("TaxJarClient constructor");
|
||||
this.logger = createLogger({ service: "TaxJarClient" });
|
||||
this.logger.trace("TaxJarClient constructor");
|
||||
const settings = createTaxJarSettings(providerConfig);
|
||||
const taxJarClient = new TaxJar(settings);
|
||||
logger.info({ client: taxJarClient }, "External TaxJar client created");
|
||||
this.logger.trace({ client: taxJarClient }, "External TaxJar client created");
|
||||
this.client = taxJarClient;
|
||||
}
|
||||
|
||||
async fetchTaxesForOrder(params: TaxParams) {
|
||||
this.logger.debug({ params }, "fetchTaxesForOrder called with:");
|
||||
const response: TaxForOrderRes = await this.client.taxForOrder(params);
|
||||
return response;
|
||||
}
|
||||
|
||||
async ping() {
|
||||
this.logger.debug("ping called");
|
||||
try {
|
||||
await this.client.categories();
|
||||
return { authenticated: true };
|
||||
|
|
|
@ -43,8 +43,8 @@ export const taxjarConfigurationRouter = router({
|
|||
|
||||
const result = await taxjarConfigurationService.get(input.id);
|
||||
|
||||
logger.debug({ result }, "taxjarConfigurationRouter.get finished");
|
||||
|
||||
// * `providerInstance` name is required for secrets censorship
|
||||
logger.debug({ providerInstance: result }, "taxjarConfigurationRouter.get finished");
|
||||
return { ...result, config: obfuscateTaxJarConfig(result.config) };
|
||||
}),
|
||||
post: protectedClientProcedure.input(postInputSchema).mutation(async ({ ctx, input }) => {
|
||||
|
|
|
@ -29,7 +29,7 @@ export class TaxJarConfigurationService {
|
|||
async getAll(): Promise<TaxJarInstanceConfig[]> {
|
||||
this.logger.debug(".getAll called");
|
||||
const { data } = await this.crudSettingsConfigurator.readAll();
|
||||
this.logger.debug({ settings: data }, `Fetched settings from crudSettingsConfigurator`);
|
||||
this.logger.debug(`Fetched settings from crudSettingsConfigurator`);
|
||||
const validation = providersSchema.safeParse(data);
|
||||
|
||||
if (!validation.success) {
|
||||
|
@ -47,7 +47,7 @@ export class TaxJarConfigurationService {
|
|||
async get(id: string): Promise<TaxJarInstanceConfig> {
|
||||
this.logger.debug(`.get called with id: ${id}`);
|
||||
const { data } = await this.crudSettingsConfigurator.read(id);
|
||||
this.logger.debug({ setting: data }, `Fetched setting from crudSettingsConfigurator`);
|
||||
this.logger.debug(`Fetched setting from crudSettingsConfigurator`);
|
||||
|
||||
const validation = getSchema.safeParse(data);
|
||||
|
||||
|
|
|
@ -1,35 +1,36 @@
|
|||
import pino from "pino";
|
||||
import { TaxBaseFragment } from "../../../generated/graphql";
|
||||
import { logger } from "../../lib/logger";
|
||||
import { ChannelConfig, defaultChannelConfig } from "../channels-configuration/channels-config";
|
||||
import { createLogger } from "../../lib/logger";
|
||||
import { ChannelConfig } from "../channels-configuration/channels-config";
|
||||
import { TaxProvider } from "../taxes/tax-provider";
|
||||
import { taxJarCalculate } from "./taxjar-calculate";
|
||||
import { TaxJarClient } from "./taxjar-client";
|
||||
import { defaultTaxJarConfig, TaxJarConfig } from "./taxjar-config";
|
||||
import { TaxJarConfig } from "./taxjar-config";
|
||||
|
||||
export class TaxJarProvider implements TaxProvider {
|
||||
readonly name = "taxjar";
|
||||
config = defaultTaxJarConfig;
|
||||
channel = defaultChannelConfig;
|
||||
client: TaxJarClient;
|
||||
readonly name = "taxjar";
|
||||
private logger: pino.Logger;
|
||||
|
||||
constructor(config: TaxJarConfig) {
|
||||
const avataxClient = new TaxJarClient(config);
|
||||
|
||||
this.config = config;
|
||||
this.client = avataxClient;
|
||||
this.logger = createLogger({
|
||||
service: "TaxJarProvider",
|
||||
});
|
||||
}
|
||||
|
||||
async calculate(payload: TaxBaseFragment, channel: ChannelConfig) {
|
||||
logger.info("TaxJar calculate");
|
||||
this.logger.debug({ payload, channel }, "TaxJar calculate called with:");
|
||||
const linesWithDiscount = taxJarCalculate.prepareLinesWithDiscountPayload(
|
||||
payload.lines,
|
||||
payload.discounts
|
||||
);
|
||||
const linesWithChargeTaxes = linesWithDiscount.filter((line) => line.chargeTaxes === true);
|
||||
const taxParams = taxJarCalculate.preparePayload(payload, channel, linesWithDiscount);
|
||||
logger.info(taxParams, "Payload used for TaxJar fetchTaxesForOrder");
|
||||
const fetchedTaxes = await this.client.fetchTaxesForOrder(taxParams);
|
||||
logger.info({ fetchedTaxes }, "TaxJar createOrderTransaction response");
|
||||
this.logger.debug({ fetchedTaxes }, "TaxJar createOrderTransaction response");
|
||||
|
||||
return taxJarCalculate.prepareResponse(
|
||||
payload,
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { SaleorSyncWebhook } from "@saleor/app-sdk/handlers/next";
|
||||
import { UntypedCalculateTaxesDocument } from "../../../../generated/graphql";
|
||||
import { saleorApp } from "../../../../saleor-app";
|
||||
import { createClient } from "../../../lib/graphql";
|
||||
import { createLogger } from "../../../lib/logger";
|
||||
import { calculateTaxesPayloadSchema, ExpectedWebhookPayload } from "../../../lib/saleor/schema";
|
||||
import { GetChannelsConfigurationService } from "../../../modules/channels-configuration/get-channels-configuration.service";
|
||||
import { PrivateTaxProvidersConfigurationService } from "../../../modules/providers-configuration/private-providers-configuration-service";
|
||||
import { getAppConfig } from "../../../modules/app-configuration/get-app-config";
|
||||
|
||||
import { ActiveTaxProvider } from "../../../modules/taxes/active-tax-provider";
|
||||
|
||||
|
@ -25,7 +23,7 @@ export const checkoutCalculateTaxesSyncWebhook = new SaleorSyncWebhook<ExpectedW
|
|||
|
||||
export default checkoutCalculateTaxesSyncWebhook.createHandler(async (req, res, ctx) => {
|
||||
const logger = createLogger({ event: ctx.event });
|
||||
const { authData, payload } = ctx;
|
||||
const { payload } = ctx;
|
||||
logger.info({ payload }, "Handler called with payload");
|
||||
|
||||
const validation = calculateTaxesPayloadSchema.safeParse(payload);
|
||||
|
@ -37,27 +35,14 @@ export default checkoutCalculateTaxesSyncWebhook.createHandler(async (req, res,
|
|||
}
|
||||
|
||||
const { data } = validation;
|
||||
logger.info({ data }, "Payload is valid.");
|
||||
logger.info("Payload validated succesfully");
|
||||
|
||||
const { providers, channels } = getAppConfig(data);
|
||||
logger.debug("Successfully parsed providers & channels from payload");
|
||||
|
||||
try {
|
||||
const client = createClient(authData.saleorApiUrl, async () =>
|
||||
Promise.resolve({ token: authData.token })
|
||||
);
|
||||
|
||||
const providersConfig = await new PrivateTaxProvidersConfigurationService(
|
||||
client,
|
||||
authData.saleorApiUrl
|
||||
).getAll();
|
||||
|
||||
const channelsConfig = await new GetChannelsConfigurationService({
|
||||
saleorApiUrl: authData.saleorApiUrl,
|
||||
apiClient: client,
|
||||
}).getConfiguration();
|
||||
|
||||
logger.info({ providersConfig }, "Providers configuration returned");
|
||||
|
||||
const channelSlug = payload.taxBase.channel.slug;
|
||||
const channelConfig = channelsConfig[channelSlug];
|
||||
const channelSlug = data.taxBase.channel.slug;
|
||||
const channelConfig = channels[channelSlug];
|
||||
|
||||
if (!channelConfig) {
|
||||
logger.error(`Channel config not found for channel ${channelSlug}`);
|
||||
|
@ -65,7 +50,7 @@ export default checkoutCalculateTaxesSyncWebhook.createHandler(async (req, res,
|
|||
return res.send({});
|
||||
}
|
||||
|
||||
const providerInstance = providersConfig.find(
|
||||
const providerInstance = providers.find(
|
||||
(instance) => instance.id === channelConfig.providerInstanceId
|
||||
);
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { SaleorSyncWebhook } from "@saleor/app-sdk/handlers/next";
|
||||
import { UntypedCalculateTaxesDocument } from "../../../../generated/graphql";
|
||||
import { saleorApp } from "../../../../saleor-app";
|
||||
import { createClient } from "../../../lib/graphql";
|
||||
import { createLogger } from "../../../lib/logger";
|
||||
import { calculateTaxesPayloadSchema, ExpectedWebhookPayload } from "../../../lib/saleor/schema";
|
||||
import { GetChannelsConfigurationService } from "../../../modules/channels-configuration/get-channels-configuration.service";
|
||||
import { PrivateTaxProvidersConfigurationService } from "../../../modules/providers-configuration/private-providers-configuration-service";
|
||||
import { getAppConfig } from "../../../modules/app-configuration/get-app-config";
|
||||
import { ActiveTaxProvider } from "../../../modules/taxes/active-tax-provider";
|
||||
|
||||
export const config = {
|
||||
|
@ -24,7 +22,7 @@ export const orderCalculateTaxesSyncWebhook = new SaleorSyncWebhook<ExpectedWebh
|
|||
|
||||
export default orderCalculateTaxesSyncWebhook.createHandler(async (req, res, ctx) => {
|
||||
const logger = createLogger({ event: ctx.event });
|
||||
const { authData, payload } = ctx;
|
||||
const { payload } = ctx;
|
||||
logger.info({ payload }, "Handler called with payload");
|
||||
|
||||
const validation = calculateTaxesPayloadSchema.safeParse(payload);
|
||||
|
@ -36,26 +34,14 @@ export default orderCalculateTaxesSyncWebhook.createHandler(async (req, res, ctx
|
|||
}
|
||||
|
||||
const { data } = validation;
|
||||
logger.info({ data }, "Payload is valid.");
|
||||
logger.info("Payload validated succesfully");
|
||||
|
||||
const { providers, channels } = getAppConfig(data);
|
||||
logger.debug("Parsed providers & channels from payload");
|
||||
|
||||
try {
|
||||
const client = createClient(authData.saleorApiUrl, async () =>
|
||||
Promise.resolve({ token: authData.token })
|
||||
);
|
||||
const providersConfig = await new PrivateTaxProvidersConfigurationService(
|
||||
client,
|
||||
authData.saleorApiUrl
|
||||
).getAll();
|
||||
|
||||
const channelsConfig = await new GetChannelsConfigurationService({
|
||||
saleorApiUrl: authData.saleorApiUrl,
|
||||
apiClient: client,
|
||||
}).getConfiguration();
|
||||
|
||||
logger.info({ providersConfig }, "Providers configuration returned");
|
||||
|
||||
const channelSlug = payload.taxBase.channel.slug;
|
||||
const channelConfig = channelsConfig[channelSlug];
|
||||
const channelSlug = data.taxBase.channel.slug;
|
||||
const channelConfig = channels[channelSlug];
|
||||
|
||||
if (!channelConfig) {
|
||||
logger.error(`Channel config not found for channel ${channelSlug}`);
|
||||
|
@ -63,7 +49,7 @@ export default orderCalculateTaxesSyncWebhook.createHandler(async (req, res, ctx
|
|||
return res.send({});
|
||||
}
|
||||
|
||||
const providerInstance = providersConfig.find(
|
||||
const providerInstance = providers.find(
|
||||
(instance) => instance.id === channelConfig.providerInstanceId
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue