2// Standalone Pax8 product sync - direct database and API access
3const { Pool } = require('pg');
4const axios = require('axios');
6const PAX8_API_BASE = 'https://api.pax8.com/v1';
7const PAX8_AUTH_URL = 'https://api.pax8.com/v1/oauth/token';
8const PAX8_CLIENT_ID = 'Kc57c2kDnwisVNia0m3yKOQGoiglaq3d';
9const PAX8_CLIENT_SECRET = 'kFQarde_yooSLBnF-y6ZXohOr_6lZT87GX_SrL7taji-LTaaCmSopvo6nSLodF_M';
10const TENANT_ID = '00000000-0000-0000-0000-000000000001';
11const VENDOR = 'Microsoft';
14const pool = new Pool({
15 host: 'rmm-psa-db-do-user-28531160-0.i.db.ondigitalocean.com',
18 password: 'AVNS_J8RJAmsEwsHFG52_-F2',
19 database: 'defaultdb',
20 ssl: { rejectUnauthorized: false }
23console.log('š Pax8 Product Sync - Standalone');
24console.log(`Tenant: ${TENANT_ID}`);
25console.log(`Vendor: ${VENDOR}\n`);
30async function getAccessToken() {
31 console.log('š Getting Pax8 access token...');
34 const response = await axios.post(PAX8_AUTH_URL, {
35 grant_type: 'client_credentials',
36 client_id: PAX8_CLIENT_ID,
37 client_secret: PAX8_CLIENT_SECRET
39 headers: { 'Content-Type': 'application/json' }
42 console.log('ā Access token acquired\n');
43 return response.data.access_token;
45 console.error('ā OAuth2 authentication failed:', error.response?.data || error.message);
55async function listProducts(accessToken, vendor) {
56 console.log(`š¦ Fetching ${vendor} products from Pax8...`);
59 const response = await axios.get(`${PAX8_API_BASE}/products`, {
61 'Authorization': `Bearer ${accessToken}`,
62 'Content-Type': 'application/json'
67 const products = response.data.content || response.data.products || response.data;
68 console.log(`ā Found ${products.length} products\n`);
71 console.error('ā Failed to fetch products:', error.response?.data || error.message);
79async function syncProducts() {
81 const accessToken = await getAccessToken();
82 const products = await listProducts(accessToken, VENDOR);
84 if (!products || products.length === 0) {
85 console.log('No products to sync');
89 console.log('š¾ Syncing products to database...\n');
95 for (const product of products) {
97 const pax8ProductId = product.id || product.productId;
98 const name = product.name || product.productName || 'Unknown Product';
99 const description = product.description || product.productDescription || '';
100 const priceRetail = product.pricing?.retail || product.price || 0;
101 const priceExTax = product.pricing?.cost || priceRetail;
102 const pax8Vendor = product.vendor || VENDOR;
103 const pax8Category = product.category || null;
104 const pax8Subcategory = product.subcategory || product.subCategory || null;
105 const pax8Sku = product.sku || product.productSku || null;
106 const billingTerm = product.billingTerm || product.billing?.term || 'monthly';
107 const isAutoRenew = product.autoRenew !== false;
108 const pax8Metadata = JSON.stringify(product);
111 const existingResult = await pool.query(
112 'SELECT product_id FROM products WHERE pax8_product_id = $1',
116 if (existingResult.rows.length > 0) {
120 name = $1, description = $2, price_retail = $3, price_ex_tax = $4,
121 pax8_vendor = $5, pax8_category = $6, pax8_subcategory = $7, pax8_sku = $8,
122 billing_term = $9, is_auto_renew = $10, pax8_metadata = $11,
123 last_synced_at = NOW(), updated_at = NOW()
124 WHERE pax8_product_id = $12`,
125 [name, description, priceRetail, priceExTax, pax8Vendor, pax8Category,
126 pax8Subcategory, pax8Sku, billingTerm, isAutoRenew, pax8Metadata, pax8ProductId]
129 console.log(` ā» Updated: ${name}`);
133 `INSERT INTO products (
134 tenant_id, name, description, supplier, price_retail, price_ex_tax,
135 is_service, divisible, is_stock,
136 pax8_product_id, pax8_vendor, pax8_category, pax8_subcategory,
137 pax8_sku, billing_term, is_auto_renew, pax8_metadata,
138 is_active, last_synced_at, created_at, updated_at
140 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,
143 [TENANT_ID, name, description, 'Pax8', priceRetail, priceExTax,
144 true, true, false, pax8ProductId, pax8Vendor, pax8Category, pax8Subcategory,
145 pax8Sku, billingTerm, isAutoRenew, pax8Metadata, true]
148 console.log(` + Added: ${name}`);
151 } catch (productErr) {
153 console.error(` ā Error: ${product.name}:`, productErr.message);
157 console.log('\nā
Sync Complete!');
158 console.log(` Total: ${products.length}`);
159 console.log(` Added: ${synced}`);
160 console.log(` Updated: ${updated}`);
161 console.log(` Errors: ${errors}`);
164 console.error('\nā Sync failed:', error.message);
173 .then(() => process.exit(0))