articleApril 4, 2026#sage-100#erp#integration#shopify#ebay

ForgeSync: A Sage 100 Integration Hub That Outlived Its Own Storefront

Originally a Magento 2 module, ForgeSync is now a standalone service wiring Sage 100 to Shopify and eBay: order export, inventory, payout reconciliation, and the hard lessons that shaped it.

Updated July 2026. This post originally described ForgeSync as a Magento 2 module. Since then the client replatformed to Shopify, added an eBay channel, and ForgeSync grew into a standalone integration hub. The store changed; the hub kept running. That turned out to be the whole point, so we rewrote the post to tell the full story.

Act one: a Magento module with opinions

ForgeSync started as a ground-up replacement for a legacy vendor connector inside a B2B industrial distributor’s Magento 2 store. The legacy module worked, mostly, but nobody could say exactly what it did, and every Magento upgrade was a gamble.

The rebuild was deliberately boring: service contracts and dependency injection only, no ObjectManager shortcuts, plugins instead of preferences, encrypted credentials, and an fs_-prefixed schema of 15 InnoDB tables that imported 422,000+ ERP records on a two-hour cycle. Its most interesting piece was pricing: Sage 100 computes customer prices through a stored procedure with a six-level price-code cascade, five-tier quantity breaks, and six pricing methods. We reimplemented that cascade in PHP and verified it against the stored procedure’s output until they matched exactly, then let the storefront show logged-in customers the same price their invoice would carry. Forty unit tests pinned the behavior.

That version shipped, ran in production, and did its job. Then the ground moved.

Act two: the storefront leaves, the integration stays

In 2026 the client replatformed from Magento to Shopify and added eBay as a marketplace channel. Here’s the thing about integrations that live inside a storefront: when the storefront dies, they die with it.

So ForgeSync’s second life is a standalone Laravel service that owns the wire between Sage 100 and every place the client sells. The storefront became a client of the hub instead of its host. When the next platform migration happens (and there is always a next one), the integration is no longer part of the blast radius.

What the hub does today:

  • Imports customers, items, warehouse inventory, price codes, sales orders, invoices, and tracking from Sage on an incremental sync: deltas by default, full or windowed re-imports on demand.
  • Order export through a queue with a drain cycle, retries, and alerting. Web orders become Sage sales orders with the right customer, terms, tax handling, freight, and deposit.
  • Customer resolution for guest checkouts: match by stored link, then by email, then auto-create in the ERP, so guest orders stop piling up as junk accounts.
  • Net-30 terms for approved accounts via a draft-order flow, with credit holds surfaced to the customer instead of discovered at invoicing.
  • Shipments and tracking written back to the storefront as fulfillments, so buyers get tracking from the store they bought from.
  • Payout reconciliation: storefront payouts land against ERP deposits through a clearing account, with card fees and refunds accounted for line by line, not averaged away.
  • The eBay loop: marketplace listings mapped to real item-master codes (including the aliases and custom labels that accumulate over decades), orders flowing into Sage like any other order, and the compliance plumbing marketplaces require, like account-deletion notification endpoints.

The lessons that cost the most

Everything above is architecture. What follows is the part you only learn in production, and it’s why this post exists.

Sage rejects whole orders over single fields. A sales order header comment longer than 30 characters doesn’t get truncated; the entire order bounces with a terse one-line error. Same for a deposit check number over 10 characters, which matters because marketplace order IDs are longer than that. Every field the hub writes is clamped to what Sage will actually accept, because the alternative is orders silently piling up in a retry queue.

Tax is recalculated at invoicing. The total your customer saw at checkout and the total Sage invoices can drift unless the integration plans for the recalculation. Pennies, usually. Accountants do not care that it’s pennies.

The stock web-services layer has blind spots. Sage 100’s bundled web services serve inventory from the default warehouse only, and they ignore some of their own documented filter parameters. If your business ships from more than one warehouse, the brochure integration shows your customers the wrong number. Getting multi-warehouse truth takes deeper access than the default layer offers.

Never trust a heartbeat alone. Our scheduler once died in a way that left its heartbeat looking healthy for a day and a half: the process manager reaped background jobs while the parent kept reporting in. The fix was mechanical (a service-manager setting), but the lesson was structural: liveness is per-job freshness measured against each job’s expected cadence, verified by a watchdog that lives outside the application it watches.

Test databases lie about production databases. A query pattern that passed the entire test suite crashed in production because the test engine and the production engine disagree about escape characters in string literals. Byte-level differences between engines are invisible until they aren’t. Which brings us to method.

The method: capture, then verify byte for byte

Before replacing the legacy connector, we captured what actually crossed the wire: over 2.18 million request and response payloads against an 80-endpoint contract. Those captures became the test bed. New code is verified by replaying reality against it, down to golden tests that require byte-for-byte identical output to the live system. When old and new agree on everything, cutover is a configuration flip with a rollback path instead of a leap of faith.

That method has now survived two platform changes and a marketplace launch on this one client, and it’s how we approach every integration: the systems tell you what they do, if you record them instead of trusting the documentation.

Where it stands

The hub runs in production today behind a test suite that has grown from 40 unit tests to 473 tests and 1,240 assertions, with observability designed on the assumption that anything can die silently: a one-glance status command, per-job silence watchdogs, an order-reconciliation check that catches missed webhooks, and an independent health probe outside the app. Alerts go to single-purpose channels with an email fallback, and every alert says what broke in plain language.

The store has changed platforms once and grown a marketplace channel since ForgeSync first shipped. The integration outlived both changes. If your system of record deserves the same treatment, that’s what we do.

Working through something similar?

Most of what we publish comes straight from engagements. If the problem in this piece looks like yours, we've probably already built the fix once.

Book a discovery call