Laravel and Sage. Which Sage, Though?
Sage is a family of products with almost nothing in common at the API layer. Working out which one a client runs, and where it runs, decides the architecture before a line is written.
A discovery call where someone says "we use Sage" has told you roughly as much as "we use a database". It narrows the vendor and nothing else. The products that share the name were built in different decades by different companies, several of them acquired, and at the point where your Laravel application has to talk to one, they have close to nothing in common.
This is not a complaint about Sage. It is a very large product family serving businesses from a sole trader to a manufacturer with plants in four countries, and no single interface would serve both. But it does mean the first hour of the project is a question rather than a schema.
The question that decides everything
Sage Accounting, the small-business product, is a hosted service with a REST interface and OAuth 2. If that is what the client runs, the integration looks like any other modern SaaS integration and the work is the business rules.
Sage 50 is a desktop application with a data file. Depending on the edition and the era, the way in is a driver or a local service that runs on the machine holding that file. The important word in that sentence is local: the thing you would call is not on the internet and was never meant to be.
Sage 200 splits. One edition is hosted by Sage with a published interface. The other is installed on the customer's own servers, and its interface runs there, behind whatever the customer's network does. Those two are not the same integration and they are not the same estimate.
Sage Intacct and Sage X3 are different products again, aimed at larger finance teams, with their own interfaces, their own authentication and their own vocabulary for the same accounting concepts.
So the questions to get answered before anyone commits to a date:
- Which product, and which edition of it.
- Where it physically runs, and who administers that machine.
- Which version, because interfaces have been added and retired across versions.
- Who at the client can grant access, and how long their approval takes.
That last one is not a technical question and it is frequently the longest item on the plan.
When there is nothing on the internet to call
Say it is Sage 50 on a server in the client's office, or Sage 200 on their own infrastructure. Your Laravel application is on a host somewhere with a public address. There is no route between them, and no library fixes that.
There are three honest shapes for this, and they are choices about the client's infrastructure as much as about your code.
A connector the client installs. A small process on their network, next to the Sage data, which talks outward to your application over HTTPS. Because it makes the connection, no inbound firewall rule is needed, which is what makes it acceptable to most IT departments. It also becomes software you have to version, monitor and support on a machine you cannot log into.
A private network path. A VPN or a dedicated tunnel between their network and your host. Cleaner to reason about once it exists, and it exists when their IT team has time, which is the risk. Worth pushing for when the client already has this arrangement with another supplier.
File exchange. Export from your side on a schedule, drop it where the product imports from, or read the export it produces. Dismissed too quickly by people who prefer APIs. It is observable, a failed run leaves the file sitting there, and a person can open it and see what was wrong.
Whichever it is, the design consequence is the same and it should be stated to the client in the first week: the data in your application is a copy with a delay, the delay depends on a machine in their building being switched on, and the interface has to say so rather than presenting a stale figure as current.
Queue everything, and mean it
Against Sage Accounting this is ordinary advice. Against an on-premise install it is the difference between a working system and one that falls over every Thursday afternoon when their weekly reports run and the server is busy.
A synchronous call into a system like that will eventually take ninety seconds, and if it is inside a web request you have a timeout, a user who has seen an error, and no reliable way to know whether the write landed. Put every interaction on a queue, give it a generous timeout, and make the retry safe - the same at-least-once reality applies here, with the extra hazard that the other side may not be able to tell you whether your first attempt succeeded.
Keep a record of each attempt with the request, the response and the timestamp, and keep it for longer than you think you need. When the finance team asks in March why a February invoice is missing, that table is the only thing that answers.
VAT, and staying out of it
For UK clients this comes up immediately, because Making Tax Digital means VAT returns are submitted from software rather than typed into a website.
The instinct is to build the submission. Usually the correct move is the opposite. If the business keeps its VAT record in a supported Sage product, that product submits, and your integration's job is to make sure the numbers reaching it are right and arrive before the deadline. Adding a second submission path creates a reconciliation problem with a tax authority at the end of it.
Where it does become your problem is the shape of the data you hand over. Tax point versus invoice date, the treatment of a credit note issued in a later period, rounding at line level against rounding at invoice level, and reverse charge on cross-border supplies. Get those wrong and the submission is correct software producing an incorrect return. The engagement notes for our UK work go into what that means contractually.
What to do before writing code
Ask for a copy of the data, or read access to a test company. Not a specification, not a screenshot: the actual records, with the fields the client's staff have renamed and the customer accounts that were set up in 2011 and never tidied. Every estimate that goes badly wrong was made from a description of the data rather than the data.
Then write down the direction of truth for each entity, in a document the finance team signs off rather than one only developers read. It is a page of prose. It is also the only artefact from the project that still matters in two years, because the code gets rewritten and that page is what the rewrite is checked against.
If the ledger is Xero rather than Sage, the access question disappears and a different one replaces it - the token lifecycle, which has a failure mode that disconnects customers silently. Our integration engineering page covers how both kinds of engagement are scoped.
