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

Pi-pi.ee sells waterless urinal systems to businesses across Europe โ maintenance companies, venue operators, facility managers. The product nearly sells itself: lower operating costs, no water connection, less maintenance. The sales process was the bottleneck.
B2B buyers in Europe have specific expectations that consumer e-commerce platforms treat as edge cases. A German buyer expects their VAT number to be validated against the VIES registry, the MwSt to be removed automatically from the invoice, and the correct reverse charge notation to appear on the document. A Portuguese buyer needs Multibanco as a payment option. A Finnish buyer expects the invoice in Finnish. Shopify Plus could handle portions of this โ at โฌ60,000+/year, plus bespoke development for everything it could not do natively. The business needed a platform built around B2B compliance from the ground up, not a consumer checkout with VAT bolted on.
I built a headless B2B e-commerce platform where EU VAT compliance is a first-class architectural concern, not an afterthought. VAT rates for all 32 countries are stored as static JSON, updated by a script before each build. Calculation happens at checkout with no external API call in the critical path โ no latency, no third-party failures, no dependency on a service that can go down during checkout.
VIES integration validates EU VAT numbers in real time and auto-fills the company's registered address directly from the EU registry. Six payment methods โ cards, PayPal, Revolut, SEPA Direct Debit, bank transfer, and Multibanco โ cover every major European B2B payment preference without requiring multiple Stripe accounts or separate integrations. Server-rendered PDF invoices with correct reverse charge notation are generated and emailed automatically on payment confirmation, with Cyrillic character support for Eastern European markets. Orders and customer records sync to Notion automatically, keeping the operations team in a tool they already know.
Professional buyers can now self-serve completely: validate their VAT number, see accurate country-specific pricing, pay via their preferred method, and receive a compliant invoice โ without a sales call.
| Metric | Value |
|---|---|
| Languages | 28 fully localized |
| Markets | 32 European countries |
| Payment methods | 6 (Cards, PayPal, Revolut, SEPA, Bank Transfer, Multibanco) |
| VAT calculation | 0 runtime dependencies (static JSON) |
| PDF invoices | Server-rendered with Cyrillic support |
| SEO | 100% server-rendered (Next.js App Router) |
Selling B2B across 32 European markets in 28 languages means organic discovery has to work in every one of them โ not just English. Technical SEO was part of the build from day one, not a phase that gets postponed until "after launch":
hreflang declarations for all 28 languages ร 32 markets โ every locale points to the correct regional alternates so a Polish buyer searching in Polish lands on pl-PL, not the English fallbackProduct schema with correct currency, B2B pricing, and availability โ the structured data survives localization, which is the most common place schema silently breaks on multilingual sitesOrganization, BreadcrumbList, and FAQ schema for the long-tail B2B queries that drive most of this marketFor the technically curious, here is how the core pieces are built.
Price calculation without API requests โ data loaded statically, types guarantee correctness:
// src/lib/pricing/utils.ts
export interface PriceResult {
basePrice: number; // Net price in EUR
vatRate: number; // 0.22 = 22%
vatAmount: number; // VAT amount in EUR
totalPrice: number; // Net + VAT
isEU: boolean; // EU country flag
formattedTotal: string; // "โฌ365.78"
}
export function calculatePrice(countryCode: string): PriceResult {
const vatRate = getVatRate(countryCode); // From static JSON
const basePrice = 299; // EUR
const vatAmount = Math.round(basePrice * vatRate * 100) / 100;
const totalPrice = Math.round((basePrice + vatAmount) * 100) / 100;
return {
basePrice,
vatRate,
vatAmount,
totalPrice,
isEU: isEuCountry(countryCode),
formattedTotal: new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR",
}).format(totalPrice),
};
}Unified handler for all payment types โ instant (cards) and async (SEPA, bank transfers):
// src/app/api/webhooks/stripe/route.ts
const ASYNC_PAYMENT_METHODS = ["sepa_debit", "customer_balance", "multibanco"];
export async function POST(request: NextRequest) {
const event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
switch (event.type) {
case "payment_intent.succeeded": {
const orderData = parseOrderData(paymentIntent);
await sendOrderNotification(orderData); // Warehouse
await sendInvoiceEmail(orderData); // Customer
await upsertOrder({ ...orderData, status: "Paid" }); // CRM
break;
}
case "payment_intent.requires_action": {
// Bank transfer โ send instructions
if (nextActionType === "display_bank_transfer_instructions") {
const bankTransfer = getBankTransferDetails(paymentIntent);
await sendOrderConfirmationEmail(orderData, bankTransfer);
}
break;
}
}
}Invoices generated on server with Cyrillic support, reverse charge for B2B, and payment-specific instructions:
// src/lib/pdf/invoice-pdf.tsx
Font.register({
family: "Roboto",
fonts: [
{ src: "https://fonts.gstatic.com/.../Roboto.ttf", fontWeight: 400 },
{ src: "https://fonts.gstatic.com/.../Roboto-Bold.ttf", fontWeight: 700 },
],
});
export function InvoicePDF({ order, status, bankTransfer }: InvoicePDFProps) {
return (
<Document>
<Page size="A4" style={styles.page}>
{/* Header with logo and company info */}
<View style={styles.header}>...</View>
{/* Status badge: PAID / PENDING / PROCESSING */}
<View style={[styles.statusBadge, statusStyle]}>
<Text>{statusText}</Text>
</View>
{/* Line items table */}
{order.items.map((item) => (
<View style={styles.tableRow}>
<Text>{productNames[item.productId]}</Text>
<Text>{item.quantity}</Text>
<Text>{formatCurrency(item.price * item.quantity)}</Text>
</View>
))}
{/* Bank transfer instructions (if applicable) */}
{bankTransfer && (
<View style={styles.bankTransferBox}>
<Text>IBAN: {bankTransfer.iban}</Text>
<Text>Reference: {bankTransfer.reference}</Text>
</View>
)}
{/* Reverse charge note for B2B */}
{order.isReverseCharge && (
<Text>VAT reverse charge per Article 196 Directive 2006/112/EC</Text>
)}
</Page>
</Document>
);
}Internationalization via next-intl with SEO and server-side rendering support:
// src/i18n/config.ts
export const locales = [
"bg",
"cs",
"da",
"de",
"el",
"en",
"es",
"et",
"fi",
"fr",
"hr",
"hu",
"it",
"lt",
"lv",
"nl",
"no",
"pl",
"pt",
"ro",
"ru",
"sk",
"sl",
"sv",
"tr",
"uk",
"vi",
"zh",
] as const;
export const countries = [
"AT",
"AX",
"BE",
"BG",
"CH",
"CY",
"CZ",
"DE",
"DK",
"EE",
"ES",
"FI",
"FR",
"GB",
"GR",
"HR",
"HU",
"IE",
"IT",
"LT",
"LU",
"LV",
"MT",
"NL",
"NO",
"PL",
"PT",
"RO",
"SE",
"SI",
"SK",
] as const;
// Each country has VAT pattern, example, phone format
export const countryInfo: Record<Country, CountryConfig> = {
DE: {
vatPrefix: "DE",
vatPattern: /^DE\d{9}$/,
vatExample: "DE123456789",
phoneCode: "+49",
},
// ... 31 more countries
};We asked Iurii for a webshop, but what impressed our B2B buyers first was the company site sitting in front of it โ clear service pages in every market language, a proper about and contact section,
Kadri Saar ๐ช๐ช
Sales Manager, pi-pi.ee
Topics
After the webshop launched, Iurii added a Telegram bot that pushes a notification for every order the moment it comes in, plus a daily sales summary so I don't have to open the admin panel to know
Mark Tamm ๐ช๐ช
Account Manager, pi-pi.ee
Services
Iurii built our entire B2B platform from scratch in a remarkably short time โ a fully multilingual webshop covering 32 European markets in 28 languages, with automated VAT calculation, VIES
Mark Tamm ๐ช๐ช
Account Manager, pi-pi.ee
Iurii set up a second Telegram bot just for our side of the business. Once a month it sends me every invoice and accounting document I need without anyone having to dig through folders, and it
Tatiana ๐ช๐ช
Accountant, pi-pi.ee
Services
AvailableNeed something similar?
I build custom solutions โ from APIs to full products. Let's talk about your project.
International e-commerce platform with 30 locales, product configurators, AI chatbot, and fully automated order flow: Stripe โ Zoho CRM โ Airtable โ Mailgun โ
Internal sales portal that turns a wholesale deal into a full set of trade paperwork โ pro forma, contract, commercial invoice, packing list, CMR and more โ
B2B quote-to-order flow in Next.js: an RFQ โ quote โ order state machine, per-deal line items, guarded status transitions, and converting an accepted quote
Gated B2B pricing in Next.js: per-account price lists, server-side access control so prices never leak to crawlers, and a deal-record data model where every