Skip to content
Nikolaos Pogas

← Selected work

Ploutos

Multi-tenant e-commerce SaaS with AADE myDATA built in.

Live demoSource is private. The live demo is public.

Problem

Greek shops selling online must report every sale to AADE myDATA. Running a separate store per client does not scale for one engineer.

What I built

  • One Next.js app and one shared PostgreSQL database. Every tenant isolated with row-level security.
  • A provisioning CLI, create-ploutos-app, that sets up a new client in one command.
  • A myDATA transmission layer with four providers behind one interface: direct to AADE, Primer, Viva and Elorus. Verified against the AADE and Primer test environments.
  • A payment webhook that issues the fiscal document, with gap-free numbering in a PostgreSQL function. A failed transmission is retried in the background. It never fails the payment.
  • The full document lifecycle: retail receipts, B2B invoices, credit notes for full and partial returns, and the movement document that goes with every courier shipment.
  • Shop admin covering orders, returns and refunds, stock movements, and a monthly page for the accountant that flags any sale missing its document.

How it works

One database, many shops

All tenants share one PostgreSQL database on Supabase. Row-level security policies decide which rows a request can see, so isolation lives in the database, not in application code that could forget a filter.

A new client is one command: create-ploutos-app sets it up.

Payment first, fiscal document second

When a payment succeeds, the payment webhook asks a PostgreSQL function for the next document number. The function hands out numbers atomically, so two orders paid at the same moment never get the same number.

The fiscal document then goes to myDATA. If transmission fails, it is retried in the background. The payment is never failed because of it: the customer has already paid.

Interchangeable myDATA providers

The transmission layer has one interface and four providers: direct to AADE, Primer, Viva and Elorus, plus a mock for tests. Each shop picks its provider in settings. The rest of the app does not know which one is in use.

Direct transmission and Primer are both verified against their test environments.

One sale, one document

A duplicate here is not a display bug. It is a second legal document for one sale. Issuance is idempotent: a document number is claimed once, and the payment webhook and the checkout success page can both ask for the receipt without producing two.

AADE wraps its reply in an envelope with the real response XML-escaped inside. Read it without unwrapping and a successful submission looks like a failure, and a naive retry issues the sale twice. The parser unwraps it first, and a test pins that case.

Data flow

  1. Customer pays

    storefront of one tenant

  2. Payment webhook

    issues the fiscal document

  3. PostgreSQL function

    atomic document number

  4. myDATA transmission layer

    one interface

  5. Swappable backend

    • AADE direct
    • Primer
    • Viva
    • Elorus
Order to fiscal document. If transmission fails, it is retried in the background. The payment is never failed.

Screens

  • Screenshot to follow

    A tenant storefront
  • Screenshot to follow

    Shop admin
  • Screenshot to follow

    create-ploutos-app provisioning a client

Stack

Next.js · TypeScript · Supabase · PostgreSQL RLS · AADE myDATA