João Pedro Gaspar
← IndexFIG. 3

HSM / Singularity

Two education brands on one headless storefront I built alone, with a custom app that withholds federal taxes at checkout when the buyer is a company.

Client
HSM · Singularity
Role
Solo: storefront, components, tax app and forms
Year
2026
Stack
Shopify HydrogenReact Router 7OxygenShopify FunctionsCheckout UI extensionsMetaobjectsSalesforce APITypeScript

What I owned

  1. 01The whole storefront, alone: two education brands on one headless Hydrogen build.
  2. 02The tax-withholding app: discount and validation Functions, a checkout UI extension and admin settings.
  3. 03Forms built from metaobjects that deliver leads to Salesforce without losing one.
FIG. 3A company's tax ID becomes a withholding line at checkout.checkoutsubtotaltotal11.222.333/0001-81tax withheldchecks the CNPJcheckout blocknote on the carttax rulerates in adminno way to withhold taxCNPJ fieldnumber checked ✓copied to the cartruns againedited in the adminexact amountsame math, same cent1 cent apartright amount · no deploy101214161820222426
01 / 10

Problem ·When a company buys a course in Brazil, part of the federal taxes has to be withheld from the price. Shopify's checkout has no way to do that.

FIG. 3A company's tax ID becomes a withholding line at checkout.

Try it as a company

When a company buys, federal taxes are withheld at source: it pays the order minus the withholding. Shopify's tax settings can only add tax. Type a CPF, a valid CNPJ or a broken one, and move the order value across the tiers.

Simulation. Illustrative data, real mechanism.

Illustrative tiers: below R$ 250 → 0 · up to R$ 5,000 → 5% · above → 6.5%

Subtotal
R$ 1.890,00
Withholding at source · 5%
− R$ 94,50
Total
R$ 1.795,50

Company above the first tier: the discount Function returns the withholding as a fixed amount.

Context

HSM and Singularity are two executive-education brands, selling courses and events from one Shopify admin. I built the headless storefront alone, on Hydrogen and React Router 7: one codebase deployed as two storefronts on Oxygen, with the sections, the forms and a custom app for taxes.

The core technical problem

When a company buys in Brazil, part of the federal taxes is withheld at source: the company pays the order minus the withholding and settles the rest with the tax authority. Shopify has no such concept. Its tax settings add tax; they can't subtract it, and they don't change based on who is buying. And the number on the checkout has to match the invoice to the cent.

What I built

  • Withholding as a discount, in a Shopify FunctionThe rule runs in a discount Function and returns a fixed order-level amount labeled “Withholding at source.” It handles value tiers, the partial exemption for companies under Simples Nacional, and three ways to recognize a company purchase: the CNPJ, a customer tag or a B2B company.
  • CNPJ validated in the native fieldA validation Function checks the CNPJ check digits and blocks the checkout before payment when the number is malformed.
  • A checkout extension that keeps the Function in syncThe Function can't read the native tax ID field live. The extension copies the CNPJ into a cart metafield, every write re-runs the rule, and the extension itself shows the per-tax breakdown.
  • Rates as dataTiers and rates live in a JSON metafield read by the Function, the extension and a settings form in the admin. Changing a rate is an admin edit, with no deploy.
  • Forms built from metaobjects, delivered to SalesforceFields and steps are metaobjects, and each product picks its form. The server rebuilds validation from the admin definition and creates the lead in Salesforce over OAuth. When the CRM rejects a value, it moves into the description and the request is retried, so a typo in the admin never loses a lead.
  • Two brands, one buildThe brand is resolved at runtime from the environment, the theme is scoped by an attribute on the HTML element, and both brands' transactional emails come from one set of templates.

Why it's built this way

  • A discount, because Shopify tax only goes upModeling the withholding as a fixed order discount is how the checkout total shows what the company actually pays.
  • Integer cents on both sidesThe Function and the extension compute separately. With floating point, some totals would disagree by a cent in front of the customer. Both use the same integer arithmetic with half-up rounding.
  • The form definition is enforced on the serverValidation is rebuilt from the admin, so a client can't skip a required field, and whoever edits the form doesn't wait for a deploy.
  • Fail soft at the CRMCRMs reject picklist values all the time. A lost lead costs more than a messy description field.

Status

In development, ahead of launch on the final domains. The storefronts run on preview deployments and the tax app is already installed on the store.

Next sheet · FIG. 4Livra →