DEV Community

Anthony Leignel
Anthony Leignel

Posted on • Edited on

I built a fully automated Factur-X EN16931 batch pipeline in PHP — no SaaS, no agent, just cron and strict business logic

Automated Factur-X batch pipeline — full monthly cycle from CSV deposit to secured ZIP delivery, no SaaS, no database

How a Monthly Cron Cycle Replaces an Entire Invoicing Platform

The goal wasn't to automate individual tasks.

It was to automate an entire monthly invoicing cycle.

From CSV import to Factur-X generation, payment tracking, secure delivery, and revenue reporting, every step runs through the same deterministic pipeline.

No SaaS.

No database.

No background service.

Just PHP, cron, and business rules.


Links


Why I built it this way

The requirement was simple on paper: take client data every month, generate Factur-X EN16931 compliant invoices, track payments, and deliver everything securely.

The constraint was harder: no SaaS dependency, no shared infrastructure, no implicit processing. Every step had to be explicit, traceable, and stoppable.

That constraint shaped every architectural decision.


The monthly cycle

Onboarding

Before the first invoice is ever generated, every client is initialized automatically from a form submission. A unique identifier is generated, configuration files are created, archiving folders are structured, and payment tracking is prepared. No manual intervention required. The system is ready to process.

D1 to D7: CSV deposit and batch generation

Each client deposits their billing data via a secured form. The system validates structure and formats strictly, with no implicit tolerance and no silent correction. A malformed field stops the entire client batch, isolates it, and triggers an automatic notification for correction before reprocessing.

If valid, batch generation runs daily via cron. Invoice composition is driven entirely by business rules, including rates, VAT zones, service periods, and multi-line and multi-month billing. The output is a Factur-X EN16931-compliant PDF/A-3 document with embedded XML, making it both human-readable and machine-readable.

D8: B2B invoice and automatic pricing tier

On day 8, the system detects the volume of invoices generated during the period and derives the applicable pricing tier automatically. The monthly B2B invoice is then generated and sent by email with the PDF attached. No manual input. The cron runs, the logic decides.

D13 to D14: The only manual step

This is the single human touchpoint in the entire cycle: recording received bank transfers. Everything else is automated. This separation is intentional, because payment validation is a business decision, not a technical one.

D15: Balance recalculation and payment control

Two operations run on day 15. First, client balances are recalculated from recorded payments. Unpaid clients are automatically deactivated, blocking any further send operations until the situation is resolved. Second, paid receipts are sent to clients with confirmed payment status via cron.

D16: Secured delivery

The monthly ZIP is sent once. A deduplication guard ensures nothing can be sent twice, regardless of cron retries or manual triggers. Delivery uses a temporary token, making the file accessible only through a time-limited secured link. The archive is retained independently.


What makes it deterministic

Every processing decision is explicit. There is no magic, no silent fallback, no implicit correction.

If a CSV is invalid the client batch stops.

If a client is unpaid sends are blocked.

If a ZIP was already sent the guard refuses the duplicate.

Every operation is timestamped in technical logs. Client state is controlled via explicit flags. The revenue journal is updated on every confirmed payment.

The system is designed to be readable by anyone who picks it up. Not clever. Predictable.


Stack

  • PHP 8: backend logic and cron execution
  • mPDF: PDF generation
  • factur-x (Python, Akretion): Factur-X XML injection and PDF/A-3 conversion
  • Flat-file storage: contracts, invoices, counters, logs, revenues
  • No database. No SaaS. No external API dependency

What I Learned

Building this pipeline wasn't about automating invoice generation.

It was about designing a system where every execution produces the same predictable result.

Once the business rules were defined, cron simply became the engine that executes them.

Automation wasn't the goal.

Reliability was.


Go Further

Explore more technical articles

Read more technical notes


https://palks-studio.com

Top comments (2)

Collapse
 
17j profile image
Rahul Joshi

A refreshing deep dive into "sovereign" automation that proves you don't need a heavy SaaS stack to handle complex regulatory standards like EN16931. It’s a great example of how lean engineering—using just PHP and Cron—can still deliver a production-grade, fully automated batch pipeline for Factur-X invoicing.

Collapse
 
palks_studio profile image
Anthony Leignel

Thanks a lot for the feedback.
That approach is actually the foundation behind all the systems I design: autonomous deployments, no SaaS dependency, no integrated AI layers by default, and infrastructures that remain understandable and maintainable over time.

A lot of modern stacks have become extremely dependent on external services and constantly evolving layers. In many cases, that also increases operational complexity and the potential attack surface.

I think more companies will progressively move back toward simpler and more controlled architectures, especially with the growing security concerns and infrastructure risks we are already seeing today.