const { StateSetClient } = require('stateset-node');
const express = require('express');
require('dotenv').config();
class OrderManagementSystem {
constructor() {
this.stateset = new StateSetClient({
apiKey: process.env.STATESET_API_KEY
});
this.app = express();
this.setupRoutes();
}
setupRoutes() {
this.app.use(express.json());
// Create order endpoint
this.app.post('/api/orders', async (req, res) => {
try {
const order = await this.createOrder(req.body);
res.json({ success: true, order });
} catch (error) {
this.handleApiError(error, res);
}
});
// Get orders endpoint
this.app.get('/api/orders', async (req, res) => {
try {
const orders = await this.getOrders(req.query);
res.json({ success: true, orders });
} catch (error) {
this.handleApiError(error, res);
}
});
// Update order status
this.app.patch('/api/orders/:id/status', async (req, res) => {
try {
const order = await this.updateOrderStatus(
req.params.id,
req.body.status
);
res.json({ success: true, order });
} catch (error) {
this.handleApiError(error, res);
}
});
// Webhook endpoint
this.app.post('/webhooks/stateset',
express.raw({ type: 'application/json' }),
async (req, res) => {
await this.handleWebhook(req, res);
}
);
}
async createOrder(orderData) {
// Validate order data
this.validateOrderData(orderData);
// Check inventory
await this.checkInventory(orderData.items);
// Create order with idempotency
const idempotencyKey = `order-${Date.now()}-${orderData.customer.email}`;
const order = await this.stateset.orders.create(orderData, {
idempotencyKey
});
// Send confirmation email
await this.sendOrderConfirmation(order);
return order;
}
async getOrders(filters) {
const orders = await this.stateset.orders.list({
limit: filters.limit || 20,
status: filters.status,
created_after: filters.from_date,
created_before: filters.to_date,
sort: filters.sort || 'created_at',
order: filters.order || 'desc'
});
return orders;
}
async updateOrderStatus(orderId, newStatus) {
// Validate status transition
const order = await this.stateset.orders.get(orderId);
this.validateStatusTransition(order.status, newStatus);
// Update order
const updated = await this.stateset.orders.update(orderId, {
status: newStatus
});
// Trigger relevant workflows
await this.triggerStatusWorkflow(updated);
return updated;
}
async handleWebhook(req, res) {
// Verify signature
if (!this.verifyWebhook(req)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body);
try {
switch (event.type) {
case 'order.created':
await this.onOrderCreated(event.data.object);
break;
case 'order.shipped':
await this.onOrderShipped(event.data.object);
break;
case 'return.created':
await this.onReturnCreated(event.data.object);
break;
}
res.json({ received: true });
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: 'Processing failed' });
}
}
validateOrderData(data) {
const required = ['customer', 'items', 'shipping_address'];
for (const field of required) {
if (!data[field]) {
throw new Error(`Missing required field: ${field}`);
}
}
if (!data.items.length) {
throw new Error('Order must contain at least one item');
}
}
async checkInventory(items) {
for (const item of items) {
const inventory = await this.stateset.inventory.get(item.sku);
if (inventory.available < item.quantity) {
throw new Error(
`Insufficient inventory for ${item.sku}. ` +
`Available: ${inventory.available}`
);
}
}
}
validateStatusTransition(currentStatus, newStatus) {
const validTransitions = {
'pending': ['processing', 'cancelled'],
'processing': ['shipped', 'cancelled'],
'shipped': ['delivered', 'returned'],
'delivered': ['returned'],
'cancelled': [],
'returned': []
};
if (!validTransitions[currentStatus].includes(newStatus)) {
throw new Error(
`Invalid status transition from ${currentStatus} to ${newStatus}`
);
}
}
handleApiError(error, res) {
const statusMap = {
'VALIDATION_ERROR': 400,
'UNAUTHORIZED': 401,
'FORBIDDEN': 403,
'NOT_FOUND': 404,
'RATE_LIMITED': 429,
'INTERNAL_ERROR': 500
};
const status = statusMap[error.code] || 500;
res.status(status).json({
success: false,
error: {
code: error.code,
message: error.message,
details: error.details
}
});
}
start(port = 3000) {
this.app.listen(port, () => {
console.log(`Order Management System running on port ${port}`);
});
}
}
// Start the system
const oms = new OrderManagementSystem();
oms.start();