Pikkuna — E-commerce for Vinyl Curtains & PVC Products

October 12, 2024

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

Next.js 15React 18Tailwind CSSMaterial TailwindFramer MotionSwiper

Backend

Node.js 22Next.js API Routes (30+)ZodPuppeteer@sparticuz/chromium

AI/RAG

Vercel AI SDK 5OpenAI GPT-4o-minitext-embedding-3-largeUpstash Vector

Database

Upstash VectorUpstash RedisVercel BlobAirtable

Infrastructure

Vercel (3 regions)next-pwaHLS video streamingVercel Analytics

Integrations

StripeZoho CRM + DeskMailgunPostNord APINetvisorGA4Meta CAPI

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:

MetricValue
Languages30 fully localized
Markets35 countries, 13 currencies
Order automation100% (payment → invoice)
Analytics capture100% (GA4 + Meta CAPI server-side)
API integrations30+ endpoints
Regions3 (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.

Iurii RoguliaAvailable

Need something similar?

I build custom solutions — from APIs to full products. Let's talk about your project.

View all projects