Inventory Sync
Keep product data synchronized between Pixlpay and external systems.
Overview
This pattern shows how to:
- Sync products from Pixlpay to external systems
- Keep data in sync using webhooks
- Handle conflicts and updates
Full Sync
Periodically sync all products:
javascript
const { PixlpayClient } = require('./pixlpay');
const pixlpay = new PixlpayClient(
process.env.PIXLPAY_STORE_URL,
process.env.PIXLPAY_API_TOKEN
);
async function syncAllProducts() {
console.log('Starting full product sync...');
let page = 1;
let totalSynced = 0;
while (true) {
const response = await pixlpay.getProducts({ page, per_page: 100 });
for (const product of response.data) {
await syncProduct(product);
totalSynced++;
}
if (page >= response.meta.last_page) break;
page++;
}
console.log(`Synced ${totalSynced} products`);
}
async function syncProduct(product) {
await externalSystem.upsert('products', {
external_id: `pixlpay_${product.id}`,
name: product.name,
description: product.description,
price: parseFloat(product.price),
active: product.is_active,
type: product.type,
image_url: product.images?.[0]?.url,
updated_at: product.updated_at,
});
}
// Run daily
setInterval(syncAllProducts, 24 * 60 * 60 * 1000);Real-Time Sync with Webhooks
Keep data in sync as changes happen:
javascript
app.post('/webhooks/pixlpay', express.raw({ type: 'application/json' }), async (req, res) => {
// Verify signature...
const event = JSON.parse(req.body);
switch (event.event_type) {
case 'product.created':
await handleProductCreated(event.data);
break;
case 'product.updated':
await handleProductUpdated(event.data);
break;
case 'product.deleted':
await handleProductDeleted(event.data);
break;
}
res.status(200).json({ status: 'received' });
});
async function handleProductCreated(data) {
const product = await pixlpay.getProduct(data.product_id);
await syncProduct(product.data);
console.log(`Created: ${product.data.name}`);
}
async function handleProductUpdated(data) {
const product = await pixlpay.getProduct(data.product_id);
await syncProduct(product.data);
console.log(`Updated: ${product.data.name}`);
}
async function handleProductDeleted(data) {
await externalSystem.delete('products', {
external_id: `pixlpay_${data.product_id}`
});
console.log(`Deleted: product ${data.product_id}`);
}Bidirectional Sync
Sync changes from external system back to Pixlpay:
javascript
// When external system updates a product
async function onExternalProductUpdate(externalProduct) {
// Check if it's a Pixlpay product
if (!externalProduct.external_id?.startsWith('pixlpay_')) {
return;
}
const pixlpayId = externalProduct.external_id.replace('pixlpay_', '');
// Update in Pixlpay (when write endpoints available)
await pixlpay.updateProduct(pixlpayId, {
name: externalProduct.name,
price: externalProduct.price,
is_active: externalProduct.active,
});
}Conflict Resolution
Handle conflicts when both systems update:
javascript
async function syncWithConflictResolution(pixlpayProduct, externalProduct) {
const pixlpayUpdated = new Date(pixlpayProduct.updated_at);
const externalUpdated = new Date(externalProduct.updated_at);
if (pixlpayUpdated > externalUpdated) {
// Pixlpay is newer, update external
await externalSystem.update(externalProduct.id, {
name: pixlpayProduct.name,
price: pixlpayProduct.price,
});
} else if (externalUpdated > pixlpayUpdated) {
// External is newer, update Pixlpay
await pixlpay.updateProduct(pixlpayProduct.id, {
name: externalProduct.name,
price: externalProduct.price,
});
}
// If equal, no action needed
}Sync Status Tracking
Track sync status for debugging:
javascript
const syncStatus = {
lastFullSync: null,
lastWebhookReceived: null,
productsInSync: 0,
productsOutOfSync: 0,
errors: [],
};
async function checkSyncStatus() {
const pixlpayProducts = await getAllProducts();
const externalProducts = await externalSystem.getAll('products');
syncStatus.productsInSync = 0;
syncStatus.productsOutOfSync = 0;
for (const pp of pixlpayProducts) {
const ep = externalProducts.find(e => e.external_id === `pixlpay_${pp.id}`);
if (!ep) {
syncStatus.productsOutOfSync++;
} else if (pp.updated_at !== ep.pixlpay_updated_at) {
syncStatus.productsOutOfSync++;
} else {
syncStatus.productsInSync++;
}
}
return syncStatus;
}Best Practices
- Use webhooks for real-time - Don't poll frequently
- Run full sync periodically - Catch any missed webhooks
- Track sync metadata - Store
updated_atfor conflict resolution - Handle deletions carefully - Soft delete to allow recovery
- Log all sync operations - Audit trail for debugging