i18n B2B e-commerce platform for waterless urinal products across 32 European countries with automated VAT handling, PDF invoicing, and CRM integration.
International e-commerce platform with 30 locales, product configurators, AI chatbot, and fully automated order flow: Stripe → Zoho CRM → Airtable → Mailgun → PDF.
Key Results

Pikkuna is a Finnish manufacturer of vinyl curtains and PVC products with a loyal local customer base and, at the time we started, zero international presence. The decision to expand internationally made the complexity immediate: 32 countries, a different VAT rate for every EU market, 13 currencies, and customers who expect to read — and be billed — in their own language.
Off-the-shelf platforms were ruled out early. Shopify could not handle the product configurators the business required: custom dimensions, material choices, and mounting options that recalculate price in real time. More critically, every order was being processed by hand. Confirming payment, creating a deal in the CRM, generating an accounting entry, booking a PostNord shipment, producing a PDF invoice and emailing it to the customer — a 30-minute workflow, spread across four tools, repeated for every single order. That was manageable at a dozen orders a week. It would not survive a European launch.
I built the entire platform from scratch: storefront, checkout, product configurators, and every backend integration. The central design decision was to treat the manual order workflow as the primary problem. Everything else — the i18n, the VAT logic, the configurators — was important, but the order flow was the thing that would determine whether the expansion actually worked operationally.
A single Stripe webhook now handles the entire lifecycle the moment payment is confirmed. Zoho CRM receives the deal, Airtable gets a backup record, Netvisor logs the accounting entry, PostNord books the shipment — all in parallel. Then, sequentially, the PDF invoice is generated and emailed to the customer. From payment to invoice in the customer's inbox: under two minutes, with no one touching it.
The i18n system covers 30 locales. Each country carries its own currency, VAT rate, and shipping zone, all updated automatically before each build from live exchange rate and VAT APIs. Checkout pricing is always accurate, and there are no runtime API calls in the critical purchase path. The product configurators handle custom dimensions and pricing in real time on the client side. A RAG-based AI chatbot handles multilingual support — that system is documented separately.
Processing time dropped from 30 minutes to under 2 minutes per order. For a company expanding to 32 countries without hiring an operations team, that gap was the difference between scaling and drowning.
| Metric | Value |
|---|---|
| Languages | 30 fully localized |
| Markets | 32 countries, 13 currencies |
| Order automation | Fully automated (payment → invoice) |
| Analytics capture | Server-side (GA4 + Meta CAPI) |
| API integrations | 30+ endpoints |
| Regions | 3 (Frankfurt, Stockholm, Cleveland) |
Server-side tracking (GA4 + Meta CAPI) captures the conversions that adblockers and iOS hide from client-side tracking — recovering attribution that would otherwise be invisible.
Selling across 30 languages and 32 countries means SEO is not an afterthought you bolt on later — it has to survive localization, server-side rendering, and a constantly changing product catalogue. I owned the full technical SEO stack for this build:
hreflang across 30 locales — every product, category, and content page declares its language and regional alternates correctly, so Google serves the Estonian buyer the Estonian page and the German buyer the German oneProduct schema — pricing, currency, availability, and reviews are emitted per locale, with the structured data surviving the i18n layer (the most common place schema breaks on multilingual sites)BreadcrumbList and Organization schema site-wide; FAQ pages for the most-searched questions in each marketlastModified driven by product data — no plugin, no external tool, just code that runs at build timeThe same patterns are now my default for every project — covered in the Technical SEO I Build Into Every Project write-up.
For the technically curious, here is how the core pieces are built.
Stripe webhook handles the entire order lifecycle in a single flow with retry logic:
// src/app/api/stripe-webhook/route.ts
export async function POST(req: Request) {
const event = stripe.webhooks.constructEvent(body, sig, secret);
if (event.type === "checkout.session.completed") {
const session = event.data.object;
// Parallel operations where possible
await Promise.all([
createZohoDeal(session), // CRM
createAirtableRecord(session), // Backup DB
sendToNetvisor(session), // Accounting
]);
// Sequential dependent operations
const invoice = await generateInvoicePDF(session);
await sendEmailWithInvoice(session, invoice);
// Server-side analytics (adblocker/iOS-proof)
await Promise.all([sendGA4PurchaseEvent(session), sendMetaCAPIEvent(session)]);
}
}A prebuild pipeline automatically updates VAT rates and exchange rates:
// scripts/update-vat-rates.js
async function updateVatRates() {
const euCountries = ['AT', 'BE', 'BG', ...];
const rates = {};
for (const country of euCountries) {
const res = await fetch(
`https://apilayer.net/api/rate?country_code=${country}`
);
const data = await res.json();
rates[country] = data.standard_rate;
}
await fs.writeFile('vat-rates.json', JSON.stringify(rates));
}
// next-intl.config.js (32 countries with unique settings)
const countries = {
finland: {
locale: 'fi', currency: 'EUR',
vatRate: vatRates.FI, // Auto-updated
exchangeRate: 1,
shippingZone: 'finland',
vatName: 'ALV'
},
germany: {
locale: 'de', currency: 'EUR',
vatRate: vatRates.DE,
exchangeRate: 1,
shippingZone: 'eu',
vatName: 'MwSt'
},
unitedKingdom: {
locale: 'en', currency: 'GBP',
vatRate: 0, // Non-EU
exchangeRate: exchangeRates.GBP,
shippingZone: 'america_asia',
customsUrl: 'https://...' // Customs info
},
// ... 29 other countries
};Chatbot uses Upstash Vector for semantic search over knowledge base:
// src/lib/rag-chat.ts
export async function getRelevantContext(query: string) {
// Generate embedding via OpenAI
const embedding = await openai.embeddings.create({
model: "text-embedding-3-large",
input: query,
});
// Search similar documents in Upstash Vector
const results = await vectorIndex.query({
vector: embedding.data[0].embedding,
topK: 5,
includeMetadata: true,
});
// Return context for prompt
return results.map((r) => r.metadata?.content).join("\n\n");
}
// API route with streaming
export async function POST(req: Request) {
const { messages } = await req.json();
const context = await getRelevantContext(messages.at(-1).content);
const result = streamText({
model: openai("gpt-4o-mini"),
system: `Answer based on this context:\n${context}`,
messages,
});
return result.toDataStreamResponse();
}Using Puppeteer with @sparticuz/chromium for serverless-compatible generation:
// src/lib/generateInvoice.ts
import chromium from '@sparticuz/chromium';
import puppeteer from 'puppeteer-core';
export async function generateInvoicePDF(order: Order) {
const browser = await puppeteer.launch({
args: chromium.args,
executablePath: await chromium.executablePath(),
headless: chromium.headless,
});
const page = await browser.newPage();
// Render React component to HTML
const html = renderToStaticMarkup(
<InvoiceTemplate order={order} />
);
await page.setContent(html);
const pdf = await page.pdf({ format: 'A4' });
await browser.close();
return pdf;
}As COO, I worked closely with Iurii, who led our IT department with rare clarity and ownership. He consistently turned business needs into reliable technical solutions, improved our infrastructure, and kept delivery predictable even under pressure. Communication was straightforward, priorities were transparent, and results were measurable. I'd gladly work with him again and strongly recommend him.
Andrii 🇫🇮
COO, Suomen Pehmeä Ikkuna Oy (Pikkuna)
I have worked together with Iurii Rogulia in company Suomen Pehmeä Ikkuna Oy, where he conducted IT development and support. Thanks to his expertise, all tasks were solved in time. It's clear from the sales statistics the moment when Iurii finished and launched new webshop. That led to double sales. I personally recommend Iurii as professional expert in IT development sphere.
Basil A 🇫🇮
Head of Customer Support, Suomen Pehmeä Ikkuna Oy (Pikkuna)
Topics
As Head of Production I had the opportunity to work closely with Iurii during his time with our company. Although I do not have a technical background, it was clear to me that he is a highly competent and professional IT specialist. He approaches his work in a structured and well-documented manner, ensuring long-term stability rather than quick fixes. I particularly value his ability to fully understand business needs before implementing solutions, as well as his responsiveness in urgent situations. His contribution extended beyond his formal responsibilities, often bringing valuable insights to the team. I confidently recommend Iurii for any technical role.
AvailableNeed something similar?
I build custom solutions — from APIs to full products. Let's talk about your project.
i18n B2B e-commerce platform for waterless urinal products across 32 European countries with automated VAT handling, PDF invoicing, and CRM integration.
RAG system on OpenAI and Upstash Vector with 30 language support. Includes streaming chatbot, hybrid search (semantic + keyword), AI ticket classifier, and
Databases
E-commerce order automation that cut 160+ hours/month of manual work to zero: Stripe webhook to VAT invoice and tracking in under 2 minutes.
Libraries
Databases
Production transactional emails with React Email and Resend: PDF invoice generation via react-pdf, Vercel Blob temporary storage, and BullMQ Stripe webhook