import axios, { AxiosInstance, AxiosResponse } from 'axios';
import { createLogger } from '../utils/logger';
import { withRetry } from '../utils/retry';
import { ExternalServiceError } from '../utils/errors';
const logger = createLogger();
export interface VeeqoOrderItem {
product_id: string;
quantity: number;
price_per_unit?: number;
sellable_id?: string;
}
export interface VeeqoAddress {
first_name: string;
last_name: string;
company?: string;
address1: string;
address2?: string;
city: string;
state: string;
zip: string;
country: string;
phone_number?: string;
email?: string;
}
export interface VeeqoOrder {
id?: string;
number?: string;
status?: string;
channel_order_code?: string;
deliver_to?: VeeqoAddress;
line_items_attributes: VeeqoOrderItem[];
additional_options?: {
send_notification_email?: boolean;
shipping_method_override?: string;
};
}
export interface VeeqoOrderResponse extends VeeqoOrder {
id: string;
number: string;
created_at: string;
updated_at: string;
}
export interface VeeqoOrderListResponse {
orders: VeeqoOrderResponse[];
page: number;
per_page: number;
total_pages: number;
total_count: number;
}
export interface VeeqoInventoryItem {
id: string;
product_id: string;
sellable_id: string;
quantity: number;
allocated: number;
available: number;
}
export class VeeqoClient {
private client: AxiosInstance;
private apiKey: string;
constructor() {
this.apiKey = process.env.VEEQO_API_KEY || '';
if (!this.apiKey) {
throw new Error('VEEQO_API_KEY environment variable is required');
}
this.client = axios.create({
baseURL: 'https://api.veeqo.com',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
}
});
// Add response interceptor for error handling
this.client.interceptors.response.use(
response => response,
error => {
logger.error('Veeqo API error', {
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data
});
return Promise.reject(error);
}
);
}
async createOrder(order: VeeqoOrder, idempotencyKey?: string): Promise<VeeqoOrderResponse> {
try {
const headers: Record<string, string> = {};
if (idempotencyKey) {
headers['Idempotency-Key'] = idempotencyKey;
}
logger.info('Creating Veeqo order', { order });
return await withRetry(async () => {
const response: AxiosResponse<VeeqoOrderResponse> = await this.client.post(
'/orders',
{ order },
{ headers }
);
logger.info('Veeqo order created', { orderId: response.data.id, orderNumber: response.data.number });
return response.data;
}, { maxRetries: 3 });
} catch (error) {
logger.error('Failed to create Veeqo order', { error });
throw new ExternalServiceError('Failed to create order in Veeqo', error);
}
}
async getOrder(orderId: string): Promise<VeeqoOrderResponse> {
try {
logger.info('Getting Veeqo order', { orderId });
return await withRetry(async () => {
const response: AxiosResponse<VeeqoOrderResponse> = await this.client.get(`/orders/${orderId}`);
logger.info('Veeqo order retrieved', { orderId: response.data.id });
return response.data;
}, { maxRetries: 3 });
} catch (error) {
logger.error('Failed to get Veeqo order', { error, orderId });
throw new ExternalServiceError('Failed to get order from Veeqo', error);
}
}
async listOrders(page = 1, perPage = 20): Promise<VeeqoOrderListResponse> {
try {
logger.info('Listing Veeqo orders', { page, perPage });
return await withRetry(async () => {
const response: AxiosResponse<VeeqoOrderListResponse> = await this.client.get('/orders', {
params: { page, per_page: perPage }
});
logger.info('Veeqo orders retrieved', {
page: response.data.page,
totalCount: response.data.total_count
});
return response.data;
}, { maxRetries: 3 });
} catch (error) {
logger.error('Failed to list Veeqo orders', { error });
throw new ExternalServiceError('Failed to list orders from Veeqo', error);
}
}
async updateOrder(orderId: string, order: Partial<VeeqoOrder>): Promise<VeeqoOrderResponse> {
try {
logger.info('Updating Veeqo order', { orderId, order });
return await withRetry(async () => {
const response: AxiosResponse<VeeqoOrderResponse> = await this.client.put(
`/orders/${orderId}`,
{ order }
);
logger.info('Veeqo order updated', { orderId: response.data.id });
return response.data;
}, { maxRetries: 3 });
} catch (error) {
logger.error('Failed to update Veeqo order', { error, orderId });
throw new ExternalServiceError('Failed to update order in Veeqo', error);
}
}
async deleteOrder(orderId: string): Promise<boolean> {
try {
logger.info('Deleting Veeqo order', { orderId });
return await withRetry(async () => {
await this.client.delete(`/orders/${orderId}`);
logger.info('Veeqo order deleted', { orderId });
return true;
}, { maxRetries: 3 });
} catch (error) {
logger.error('Failed to delete Veeqo order', { error, orderId });
throw new ExternalServiceError('Failed to delete order from Veeqo', error);
}
}
async syncInventory(productId?: string): Promise<VeeqoInventoryItem[]> {
try {
logger.info('Syncing Veeqo inventory', { productId });
return await withRetry(async () => {
const params: Record<string, string> = {};
if (productId) {
params.product_id = productId;
}
const response: AxiosResponse<{ sellables: VeeqoInventoryItem[] }> = await this.client.get(
'/sellables',
{ params }
);
logger.info('Veeqo inventory synced', { itemCount: response.data.sellables.length });
return response.data.sellables;
}, { maxRetries: 3 });
} catch (error) {
logger.error('Failed to sync Veeqo inventory', { error });
throw new ExternalServiceError('Failed to sync inventory from Veeqo', error);
}
}
}