Pikkuna — E-commerce for Vinyl Curtains & PVC Products
International e-commerce platform with 30 locales, product configurators, AI chatbot, and fully automated order flow: Stripe → Zoho CRM → Airtable → Mailgun → PDF.
Tech Stack
Frontend
Backend
AI/RAG
Database
Infrastructure
Integrations
Key Results
- 30 languages fully localized
- 35 countries, 13 currencies with automatic VAT
- 100% automated order flow (payment → invoice)
- Server-side tracking: GA4 + Meta CAPI (100% capture)
The Challenge
A Finnish company selling vinyl curtains wanted to expand to European and international markets. They needed e-commerce with multiple language support, automatic VAT calculation for each country, interactive configurators for custom products, and full automation from payment to delivery.
Requirements:
- Complex product configurations (custom dimensions, materials, mounting options)
- Real-time pricing with local VAT and currency
- Professional order management integrated with CRM
- AI customer support in any language
- Full automation: payment → CRM → accounting → shipping → invoice
The Solution
I built a complete e-commerce platform on Next.js 15 with App Router and server components by default. Implemented i18n system for 30 locales with next-intl, where each country has its currency, VAT rate, and shipping cost. Created fully automated order flow: Stripe webhook → Zoho CRM (leads/contacts/deals) → Airtable (backup) → PostNord (shipping) → Netvisor (accounting) → Mailgun (PDF invoice). Added RAG-based AI chatbot using OpenAI and Upstash Vector for customer support in any language.
Order Flow Automation via Stripe Webhook
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 (100% capture)
await Promise.all([sendGA4PurchaseEvent(session), sendMetaCAPIEvent(session)]);
}
}
Internationalization with Auto-Updated VAT and Exchange Rates
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 (35 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
},
// ... 32 other countries
};
RAG System for AI Chatbot
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();
}
PDF Invoice Generation on Vercel Serverless
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;
}
Results
The platform launched in October 2024 and handles orders across Europe:
| Metric | Value |
|---|---|
| Languages | 30 fully localized |
| Markets | 35 countries, 13 currencies |
| Order automation | 100% (payment → invoice) |
| Analytics capture | 100% (GA4 + Meta CAPI server-side) |
| API integrations | 30+ endpoints |
| Regions | 3 (Frankfurt, Stockholm, Cleveland) |
The automated systems reduced manual order processing from 30 minutes to under 2 minutes per order. Server-side tracking ensures 100% event capture regardless of ad blockers.
AvailableNeed something similar?
I build custom solutions — from APIs to full products. Let's talk about your project.