Work
OwnOwn product · Invoicing SaaS
Efectiback, facturación electrónica

Efectiback

An electronic invoicing SaaS for Peru: issues invoices, receipts and notes straight to SUNAT, with a REST API, a client portal and an operator panel.

Screenshot of Efectiback (1/3)
Screenshot of Efectiback (2/3)
Screenshot of Efectiback (3/3)
SYSTEM.md

The problem

Small businesses need to issue invoices that SUNAT accepts without relying on an accountant or a slow portal, and the systems they already use (POS, ERP, online store) must be able to integrate by API.

What I built

I built the platform end to end: a Java library with UBL 2.1, XAdES signing and a SOAP client; a Spring Boot API with hexagonal architecture; a client portal and a back office in Angular; and the landing page in Astro.

Outcome

In use by real clients. More than 350 automated backend tests, and issuing verified against SUNAT’s beta environment with an accepted CDR.

Decisions that mattered

  1. 01

    The queue lives in Postgres

    Sending to SUNAT uses a queue table with FOR UPDATE SKIP LOCKED and exponential-backoff retries: no RabbitMQ, no extra infrastructure.

  2. 02

    One validator per document type

    Invoices, receipts and notes validate with their own rules, extracted from SUNAT’s official spreadsheet. An error is rejected with HTTP 422 before queuing.

  3. 03

    Modular monolith, not microservices

    A single deployment with bounded contexts (issuing, identity, subscriptions). The engine is a library without Spring, tested on its own.

  4. 04

    Encrypted secrets

    Each company’s digital certificates and SOL key are stored encrypted with a master key, and the session uses an HttpOnly cookie.

  5. 05

    Minimal running cost

    Backend on Cloud Run, database on Supabase, files on Cloudflare R2 and front ends on Cloudflare Pages.

Technologies

  • Java 21
  • Spring Boot
  • PostgreSQL
  • Angular
  • Cloud Run
  • Cloudflare R2
  • Astro

C4 model. The pulsing line is the main path of a request.

Level 1 · who uses the system and what it connects to

Web portal1BackofficeREST · API keySOAP · signed XML2HTTPSHTTPSSmall-businessownerIssues and looks upinvoicesOperatorManages clients and plansPOS, ERP or onlinestore[REST]Issues invoices by APIEfectibackElectronic invoicing forPeruSUNAT[SOAP · XML]Receives and acceptsinvoicesResend[Resend]Emails invoicesjson.pe[json.pe]Looks up RUC and DNI

Level 2 · the applications and data stores it is made of

HTTPS1HTTPSREST · API keyREST · JWT2REST ·cookieHTTP · secretJDBC · FlywayS3 APISOAP3HTTPSSmall-businessownerIssues and looks upinvoicesOperatorManages clients and plansPOS, ERP or onlinestore[REST]Issues invoices by APIClient portal[Angular 22 · Cloudflare Pages]Issue, look up and downloadBack office[Angular 22 · Cloudflare Access]Onboarding, plans, billing,quotasCloud Scheduler[Cloud Scheduler]Triggers scheduled jobsInvoicing API[Spring Boot 3.5 · Java 21 · CloudRun]Modular monolith, hexagonalDDDResend[Resend]Emails invoicesDatabase[PostgreSQL · Supabase]Companies, documents andsend queueInvoice store[Cloudflare R2]Signed XML, CDR and PDF for5 yearsSUNAT[SOAP · XML]Receives and acceptsinvoices

Level 3 · the components inside the API

ACL · facadeplan quotacommandper-type rulescola_envios1sign +send2SOAP3XML · CDR · PDFIdentity[Bounded context]Users, JWT and API keysSubscriptions[Bounded context]Plans, quotas and billingPOS and catalog[Bounded context]Sales and inventoryLocal validator[motor-core]Rejects before queuingIssuing[Bounded context]Invoice, receipt, notes,voidingSend worker[@Scheduled · SKIP LOCKED]Signs, sends and archivesStore adapter[Puerto AlmacenComprobantes]R2 or disk, same contractInvoicing engine[motor-core · UBL 2.1 · XAdES]UBL, XAdES signing, SOAPclientSUNAT[SOAP · XML]Receives and acceptsinvoices
  • Person
  • System
  • Container
  • Database
  • External
  • Main path
  1. 01Portal or ERP
  2. 02REST API
  3. 03Postgres queue
  4. 04XAdES signing
  5. 05SUNAT
  6. 06CDR in R2

CV

Get my CV

Leave your email and I will send the CV there. First I check the address is yours with a code.