Laravel in Toronto
Laravel development for Toronto companies
A Laravel development company working with Toronto teams. Sales tax that differs by province, French as a legal requirement the moment you serve Quebec, and both of them reaching the database.
A Toronto company usually sells across Canada rather than in Ontario alone. That single commercial fact produces both of the engineering decisions that define these projects, and both land in the database rather than the interface.
Canadian tax varies by province, and one Laravel column cannot hold it
Some provinces levy a single harmonised tax. Others have a federal component and a provincial one, reported separately to different authorities.
An application that stores one tax total per order cannot separate them afterwards. The breakdown is not recoverable from the total, and the first time it is needed is during a filing, when the answer has to be exact.
So the schema records what was charged at the time of sale: which components, at which rates, under which province, per line. That costs a few columns on day one and is a migration across every historical order later - a migration whose output is a guess, because the rate that applied on a given date is not in the data.
The same per-line reasoning applies to refunds. Refunding part of an order means returning the right tax for that part, which is only possible if the breakdown was stored at that granularity.
For Toronto, French is data rather than strings
The second decision, and the one that is got wrong most often.
Interface strings - buttons, labels, validation messages - belong in translation files and are a solved problem. Content that belongs to a record does not. A product name, a policy description, a help article: these are data with a lifecycle. They get edited by a person who is not a developer, reviewed, published, and sometimes rolled back.
Putting that content in a language file means a product rename is a deployment, which means it will not happen, which means the French version quietly falls behind the English one. That is exactly the outcome the requirement exists to prevent, and it is visible to anyone who looks.
So translated content lives beside the record, with its own completeness state so that an unpublished French version is a known gap rather than a silent fallback to English.
The decision is cheap at the start and expensive once there are three years of records, because by then the content exists and the question is a data migration rather than a schema choice.
The regimes stack
PIPEDA is the federal baseline. Quebec adds its own regime above it, and that one goes further. Both are triggered by the same mundane fact, which is having customers in the province, so the French obligation and the privacy obligation tend to arrive in the same week.
The regime details sit on the Canada market page. What matters at the city level is smaller - a product built in Toronto is almost always sold across Canada, so the Quebec rules apply to it whether or not that was ever a decision.
What gets built for Toronto clients
Insurance quote and claims flows, financial product back ends, SaaS applications sold to Canadian enterprises.
What they share is that the complexity is in rules rather than screens - eligibility, pricing, who may see and do what. That belongs enforced at the data rather than at the route, because a route guard is a redirect and not a permission model, and the difference shows the first time an endpoint is called directly.
A Toronto invoice has to show the tax the way the province says
Calculating the right amount is half the obligation. Presenting it correctly is the other half, and it is the half that gets a business a letter.
A Quebec invoice shows GST and QST as separate lines with separate registration numbers, because they are separate taxes administered by separate authorities. A harmonised province shows one. Showing a single blended figure across the country is simpler, cheaper to build, and wrong in a way that is visible on the document. So the invoice stores its tax as component lines with the rate, the name and the registration number that applied, rather than as a total it worked out.
Place of supply decides which rules run, and it is not always the customer's billing address. For services it can be where the service is performed or where the recipient is resident; for digital goods it is its own regime. Encoding that decision once, as a resolver that takes the transaction and returns the jurisdiction with its reasoning, keeps it out of the twelve places that would otherwise each guess.
Credit notes inherit all of it. A refund issued in March against a January invoice carries January's rates and January's components, not today's, and cannot be modelled as a negative invoice generated fresh. It references the original and reverses what that document actually said.
Laravel data residency when Toronto work has to stay in Canada
A client says the data stays in Canada. That sentence has more surface area than it looks, and the database is the easy part.
The hard parts are the copies, and this is where a residency commitment usually breaks without anybody noticing. Object storage holding uploads, in whatever region it was created in. Backups, which may replicate elsewhere by default. The search index. Queue payloads in a managed Redis. Exception reports sent to a hosted service whose processing region nobody chose. Email, including the transactional mail that quotes a record in its body. Each is a copy, and each has to be placed deliberately.
Which means the commitment is an inventory before it is a configuration. List every destination data reaches, name its region, and the gaps are visible - usually two or three third parties nobody had counted as storage because they were thought of as tooling.
Multi-tenancy makes it a design decision rather than a deployment one. If some clients require Canadian residency and others do not, the application either runs separate deployments per region or carries a region on the tenant and routes every store through it. The first is more operational work and far easier to prove; the second is cheaper and only as strong as the least careful code path. Choosing deliberately, early, is considerably less expensive than discovering the answer during a client's security review.
Commercially
The invoice comes from Codefacture Yazılım A.Ş., registered in Türkiye, in US dollars or euros by bank transfer, with no Turkish VAT under the export-of-services exemption. Scope fixed in writing before work starts and priced per project.
