DEV Community

Fahad Azhar
Fahad Azhar

Posted on

Four countries, four currencies, four Shopify orgs, one engineer: how I run the stack

I look after four Shopify stores for the same brand. One in the UK, one in Pakistan, one in Saudi Arabia, one in the UAE. Pounds, rupees, riyals and dirhams. In the UK people pay by card; in Pakistan most people still want cash on delivery. And there's one developer on all of it, which is me.

Nobody sits down and designs a setup like this. You launch one store, then another country asks for its own, and a couple of years later you're maintaining four of everything. This is what I've changed to keep it manageable.

The day I found out they weren't one company (on paper)

I'd built a custom discount Function for the UK store and went to install it on Pakistan. Shopify said no:

app can only be installed on stores in the same organization

Turns out each store had been set up separately, so each one sits in its own Shopify organization. Four stores, four orgs.

So now there are four copies of the app, one per org, all built from the same code. The only things that differ are the shopify.app.toml and the settings, and I keep the settings in a metafield so I'm not editing code per store.

It ended up helping. When Pakistan needed something nobody else did, a small discount for paying online plus hiding Cash on Delivery for those customers, I just added two extensions to the Pakistan app. Both read the same cart attribute, so the storefront sets one flag and checkout handles the rest. No new app, no new install link.

If you're taking over more than one store, find out which org each one is in before you build anything. I learned it after.

Four stores, really one theme

All four run the same theme base with the same custom sections. That's great right up until you fix a bug on the UK store and someone finds the exact same bug on Pakistan a week later. That happened to me more than once.

So my rule now is simple: a fix isn't finished until I've checked every store it could exist on. I think of it as one theme deployed four times.

Two habits make that less scary:

  • Every morning a small Node script pulls a full copy of all four live themes through the Admin API. If something breaks, yesterday's version is sitting on my disk.
  • I never push templates/*.json from my local copy. That's where the store team's Theme Editor changes live, and they change them all the time. Liquid, CSS and JS I push freely; the template JSON I leave alone unless I've fetched the live version first.

Same books, four countries, and Google

Four stores selling mostly the same books look like duplicate pages to Google. hreflang is the fix, but if you just print all four regions on every page you end up pointing Google at pages that don't exist.

So I pulled every collection handle from all four stores and compared them. Only 53 out of 152 existed on more than one store. The snippet now only adds a tag for a store that actually has that collection, and x-default goes to the first store that has it instead of one fixed store.

One profit number in four currencies

The owner wanted to see profit for all four stores together. Getting that number to stay the same every time you open the page took two decisions.

First, nobody types revenue in. It comes straight from the orders, and I use current_total_price, not total_price. When I checked recent Pakistan orders, about 8% had been edited after checkout (items added or removed) without a refund. total_price still shows what the order was when it was placed, so every one of those was wrong.

Second, the exchange rate is fixed per month. The combined view converts everything to riyals at that month's average rate. I fetch it once and save it. Otherwise last month's profit would shift a little every day as the rate updates. The riyal and the dirham are pegged to the dollar, so those two are constants. Only the pound and the rupee actually move.

const USD_PEG = { SAR: 3.75, AED: 3.6725 } as const;

async function monthRate(ccy: "GBP" | "PKR", month: string) {
  const saved = await db.fxRates.find({ ccy, month });
  if (saved) return saved.rate;   // fetched once, then never again
  const rate = await fetchAverage(ccy, month);
  await db.fxRates.insert({ ccy, month, rate });
  return rate;
}
Enter fullscreen mode Exit fullscreen mode

Stock is its own story

Each store tracks its own stock, and Shopify won't easily tell you who changed a number, or why. So I built a ledger that records every change across all four stores. That one deserves its own post, so it's next.

If I were starting again

  • Check the organization of every store on day one.
  • Treat the stores as one theme, and check every fix everywhere.
  • Leave the template JSON alone unless you've just fetched it live.
  • Use current_total_price, and lock your exchange rates.

I'm Fahad. I build and fix Shopify stores, and I write up the problems I run into at fahadazhar.site. If you run more than one store, I'd love to know how you keep them in sync. Tell me in the comments.

Top comments (1)

Collapse
 
elias_0549e0e2ef8cf6ad89a profile image
RAMO •

The "one theme deployed four times" rule is the part I'd steal. Keeping per-store settings in a metafield instead of in code is what makes the four near-identical apps survivable, and the Pakistan example (one cart attribute read by both the online-payment discount and the hide-COD extension) is a neat way to avoid duplicating logic. The nightly Node script that pulls all four live themes is a good safety net too. Do you diff those snapshots automatically, so you notice when someone edits a live theme in the admin?