Back to DevLog

Building Multi-Tenant E-commerce: Products, Orders, and Shipping Integration

3 min read

Just wrapped up a solid coding session working on AdminStack's product management system. Today was all about bridging the gap between digital products and physical shipping - something that's been on my todo list for weeks.

What Got Built

Started with a comprehensive database migration that added 9 new columns to the products table. The big additions were email configuration fields (SendGrid template IDs, from/reply-to addresses), shipping support (is_physical flag, brand colors), and Stripe integration fields. I also expanded the orders table with tracking URLs, carrier info, and shipping dates.

The coolest part was seeding real products into the system - BunkerVault, ImageStack, MemStack Pro, AdminStack itself, AlgoStack, EpsteinScan, and even my Vibe Coding book. It's satisfying to see your actual product lineup living in the database.

UI Improvements

The product edit form got a major upgrade with three new collapsible sections:

  • Shipping configuration (physical product toggle)
  • Email & shipping settings (SendGrid integration, brand color picker)
  • Stripe configuration (account IDs, webhook secrets)

I kept the sections collapsed by default because nobody wants to scroll through a massive form. The brand color picker was particularly fun to build - it's both a color input and a hex text field that sync with each other.

The dashboard widget now shows "Pending Shipments" which counts physical orders that haven't been delivered yet. Small touches like this make the admin experience so much better.

The Bug That Almost Got Me

Ran into a frustrating bug where order status changes weren't persisting. Spent way too long debugging before realizing the API was explicitly deleting the status field from updates. Classic case of defensive programming gone wrong - there was a separate status endpoint with state machine validation, but the main edit flow was silently failing.

The fix was simple: remove the delete statement and let status updates flow through the main PATCH endpoint. Sometimes the best solution is just removing code.

Security Patterns

One thing I'm proud of is how I handled Stripe secrets. Instead of storing actual API keys in the database, products store environment variable names (like IMAGESTACK_STRIPE_SECRET_KEY). At runtime, we do process.env[product.stripe_secret_key_env] to get the actual key. Added regex validation to ensure only valid env var names can be stored.

What's Next

The foundation is solid but there's still work to do. The migration needs to run in production (always nerve-wracking), and Phase 2 will tackle the actual shipping email flows. I also need to build the missing /messages and /infrastructure pages that are linked in the sidebar but don't exist yet.

The webhook integration is particularly interesting - BunkerVault purchases get linked by product name lookup, while other products use Stripe product IDs with retroactive order linking. It's these little integration details that make or break the user experience.

Overall, a productive session that moves AdminStack closer to handling both digital and physical products seamlessly. The multi-tenant architecture continues to prove its worth as each organization can now have their own product catalog with custom branding and shipping configurations.

Share this post