~/contents
| client | TEK — cannabis cultivation and dispensary, Washington DC |
| role | Technical advisor and developer, since early 2026 |
| live | tek-district.com — B2B wholesale ordering in production with trade customers |
| stack | WooCommerce (existing), with a custom wholesale plugin built on top — sole developer |
| regulator | DC Alcoholic Beverage and Cannabis Administration (ABCA) |
TEK both grow cannabis and sell it, which means they’re two businesses with two different technology problems. The retail side sells to people. The cultivation side sells to other dispensaries — wholesale, in trade quantities, at negotiated prices.
I’ve been their technical advisor since early 2026. The first substantial thing I built was the wholesale side, and it’s live: dispensaries sign up as trade accounts, see their own pricing, and place orders directly, while TEK see what’s coming in and can set discounts and deals per customer.
Their storefront already ran on WooCommerce, which handles retail perfectly well and has no opinion about trade customers. So the wholesale system is a plugin I wrote on top of it — the roles, the pricing model and the ordering flow — rather than a second platform bolted alongside the first.

Why B2B is not e-commerce with bigger numbers
The instinct is to treat wholesale as a shop with a login. It isn’t, and the differences all sit in the same place: in B2B, the price is not a property of the product.
In consumer retail a product has a price. Everyone sees it, it’s on the page, and showing it to the wrong person is impossible because there is no wrong person. In wholesale, the price is a property of the relationship. A trade customer’s price depends on the role or tier they’ve been given, and on whatever has been separately negotiated with them.
Which makes pricing an access-control problem wearing a commercial costume. A dispensary must see exactly their own price — never a competitor’s, never the default, never a tier they weren’t granted. And this isn’t a hypothetical concern: in a trade market, one customer discovering that a rival buys the same product cheaper is a commercial incident, and it lands on the supplier.
So the rule I built to was that pricing is resolved server-side, per authenticated account, before anything reaches the client. A price a user shouldn’t see should never travel to their device, not be hidden once it arrives. That distinction is invisible when everything works and it is the entire difference between a private price and a price that’s merely not displayed.
The second half of it is precedence. With role-based prices underneath and per-customer discounts and deals on top, more than one price can be applicable at once, so which one wins has to be defined and deterministic rather than emergent. In consumer commerce an ambiguous discount is an annoyance. In wholesale it’s an invoice dispute with a business customer, and those are expensive in a way that has nothing to do with the amount.
Signing up is an application, not a registration
A related thing the consumer model gets wrong: a dispensary creating an account is not a user signing up. It’s a business applying for a trade relationship.
That means there’s a real state between “has an account” and “can buy” — registered, but not yet approved and not yet assigned a tier, and therefore not yet able to see a single price. Modelling that state explicitly, rather than treating approval as a flag bolted onto a normal user, is what keeps pricing private by default. The safe state is the one you get automatically.
Ordering, not checkout
The last structural difference is where the money isn’t.
A verified trade account places an order. TEK receive it, and invoice the customer directly. Settlement happens off the platform entirely — no card, no gateway, no payment step in the flow.
That’s ordinary for wholesale, where buying is a relationship with terms rather than a transaction with a checkout button. It also means the system never touches a payment, which in this category is worth noticing: the restrictions that mainstream processors place on the industry simply don’t apply to a flow that doesn’t process payments. The constraint didn’t have to be solved because the right shape for the business avoided it.
Worth saying plainly, because it’s the kind of thing that gets over-engineered: the correct amount of payment infrastructure here was none.
Building where the rules keep moving
Most retail businesses assemble technology from defaults: an e-commerce platform, a payment processor, an email tool, some ads. Nobody thinks about it because the defaults work.
That path is closed here, at several points at once. Mainstream payment processors maintain restricted-business categories that exclude this industry regardless of state legality. Advertising platforms restrict it. App stores apply their own policies. And underneath all of it sits an actual regulator — in DC, ABCA, which licenses and oversees medical cannabis in the District.
So every ordinary decision becomes a question with a real answer to find: not which tool is best, but which tools will still be available to this business next quarter.
And DC’s rules keep changing, which is a different engineering problem from building to a fixed standard. A stable regulation you design for once, and it is expensive but at least known. A moving one means what was permitted when you specified the system may not be when you ship, and what’s blocked today may open next year. So I think that argues for keeping regulatory assumptions in as few places as possible, avoiding platforms whose terms could remove the business overnight, and preferring reversible decisions over efficient ones wherever the difference is affordable.
It’s worth contrasting with the government platform — the same discipline under opposite conditions. That one was Dutch and European: GDPR, ISO, and a data-residency requirement that was immovable and had to be settled before a line of schema was written. This one is American and DC-specific, where the binding rules are still being written. A fixed constraint you design for; a moving one you design around. Doing both is most of what experience with regulated delivery actually means.
Where the age gate has to sit
The clearest illustration is the first thing anyone sees: the site opens with an age gate. Nobody reaches a product, a price or the trade login without passing 21+ verification first.
That’s an unusual place to put it. Ordinary commerce gates checkout — you can browse freely and prove things about yourself when you pay. Here the requirement attaches to seeing the catalogue at all, so the gate has to sit in front of everything, including the wholesale entrance. It’s a small piece of engineering and a good example of the general shape: in this category the compliance requirement usually lands somewhere structurally earlier than instinct puts it.
What's being built now
The current project is the retail counterpart: an ordering system with a shape that’s familiar from fast food and unusual in this category — order ahead, then either collect at a pickup point or be served in the lounge. Roughly how a McDonald’s app works, applied to a regulated product and a space where customers stay. Online and in-store, and the build is starting now.
The interesting problem is that the two halves want opposite things. Ordering ahead from a phone is a storefront flow with a known solution. Ordering inside the building — customer already present, staff handling the same order, reconciling with what’s happening at the counter — is closer to an operations tool. Building those as one product, rather than two that disagree about the state of an order, is the job.
Nothing there is shipped, so there’s nothing to claim yet.
Why the engagement works the way it does
An advisory relationship across a six-hour time difference only works if the response loop is short. So the commitments on my services page are there because of engagements like this one rather than the other way round: same-day response regardless of European office hours, a direct line instead of a ticket queue, and working in the client’s own tools.
If this is the kind of problem you have, here is how I work on it. The cloud side of an engagement like this is described in TasteHub.