Skip to main content
New German e-invoicing mandate from 2027: Kontier issues XRechnung and ZUGFeRD natively German e-invoicing 2027: XRechnung native

Subscription billing for the EU market: data model and events

7 min read Kontier editorial team

A business customer in Vienna goes from 20 to 25 seats on 12 March. The product unlocks the seats, billing prorates the difference for March, and the payment provider collects it by SEPA direct debit. In May the customer supplies a new VAT ID after moving the contract to a different group company. A job updates the customer record, and the next time the March invoice is generated it shows the new number. April's debit comes back marked "mandate revoked", and the product never hears about it.

None of these failures is exotic. They happen when subscription billing is built as a table holding plan, quantity and next invoice date. This article sets out an architecture that handles EU requirements from the start: which objects exist, which of them are immutable, where the tax decision is made, and how payment status gets back to the product. If you're buying rather than building, the matching checklist is in our post on billing software for European SaaS.

The data model: eight objects and whether they can change

Most failures come from using a mutable record for something that should have been fixed at a point in time. A model that holds up for EU billing separates them strictly:

Object Mutable? Key fields
Customer yes, with history B2B/B2C, country, VAT ID, validation status, valid-from
Price version no, only new versions product, pricing logic, currency, tax category, valid-from
Subscription yes, through events customer, price version, quantity, term, status
Subscription event no type (start, upgrade, cancellation), effective date, old and new values
Usage event no customer, meter, quantity, timestamp, idempotency key
Invoice until finalised number, lines, copy of customer data, tax decision
Payment attempt no invoice, method, mandate, outcome, return reason
Mandate yes, with history reference, Core or B2B scheme, status, signature date

The core rule: a subscription is the sum of its events. You can cache the current state, but an invoice is calculated from the events in a period, not from the state on the day the billing run happens. Otherwise a run on 1 April bills the 12 March upgrade at April's price if prices went up on 1 April.

A common design keeps state inside the payment provider: plan, quantity and billing cycle live there and your backend just reads them. That works until the first invoice needs a tax decision the provider doesn't know about, or until you add a second provider for direct debits. Keep subscriptions and events in your own billing layer and treat the payment provider as the execution layer.

One upgrade, four records

Back to the example, with made-up numbers: 20 seats at €40 a month, billed monthly. Five seats are added on 12 March. March has 31 days, and 12 to 31 March is 20 of them.

  1. Subscription event: upgrade, effective 12 March, quantity 20 to 25, price version unchanged.
  2. Invoice line: 5 seats × €40 × 20/31 = €129.03 net, service period 12 to 31 March.
  3. Tax decision: customer in Austria, VAT ID valid on 12 March according to VIES, so reverse charge with the required wording on the invoice. Stored: check date, result and rule applied.
  4. Payment attempt: SEPA debit for €129.03, due once the pre-notification period has passed.

Whether you bill the difference immediately or on the next monthly invoice is a product choice. What matters is that effective date, service period and invoice date are three separate fields. VAT cares about the service period, collection about the due date, and revenue recognition about the effective date.

The tax decision is made once, when the invoice is created

For electronically supplied services to consumers, the place of supply is the customer's country once the EU-wide €10,000 threshold for such sales is exceeded (Article 58 of the EU VAT Directive). Sales to businesses in other member states usually fall under reverse charge. Which rule applies depends on customer type, country, evidence and the service. That decision belongs in exactly one service, called when the invoice is generated, returning a single result: rate, country of taxation, rule, reason and the evidence used.

The result is stored on the invoice and never recalculated. That fixes the May problem from the opening: the new VAT ID applies to invoices from May onwards. The March invoice keeps March's number and validation result. If the customer really wanted a correction, that's a cancellation and a new invoice, not an update.

Two more things belong in that service. VAT rates are versioned master data with effective dates, not constants in code. And credit notes inherit the tax decision of the original invoice instead of making a new one. Otherwise a credit note for a reverse-charge invoice turns up in the export with Austrian VAT on it.

Payment status flows back to the product

Billing in Europe usually means SEPA direct debit alongside cards. Under a SEPA Core debit the customer can reclaim an authorised payment within eight weeks without giving a reason, and within 13 months if there was no valid mandate. A payment attempt is therefore a sequence of events: submitted, credited, and possibly returned weeks later with a reason code.

In architecture terms:

  • The payment provider reports by webhook. Process those messages idempotently, because they can arrive twice and out of order.
  • A return reopens the receivable and emits an event that dunning and the product can subscribe to.
  • Retry logic depends on the return reason. Insufficient funds justifies another attempt. A revoked mandate doesn't.
  • Product access is controlled through its own entitlement that reacts to dunning levels, not directly to a single failed collection.

That fixes the third problem from the opening: April's returned debit is an event the product receives. Mandates and retries are covered in more depth in our post on SEPA direct debit for SaaS.

Invoices are documents; exports are projections

A finalised invoice is an immutable document with a sequential number, and in Germany it must be kept for eight years. From 2028 it must be an EN 16931 e-invoice for domestic B2B in Germany, and under ViDA intra-EU cross-border B2B invoicing goes electronic from 1 July 2030. The structured format is therefore the primary representation of the invoice. The PDF is an additional view of it.

Everything else is a projection built from invoices and payments: the DATEV export, OSS return data per country and quarter, IFRS 15 revenue schedules, the MRR dashboard. Projections may be recalculated at any time. Invoices may not. Keep that split and you can fix a broken export without touching a single document.

What to decide first

If you're building subscription billing for the EU, or rebuilding an existing system, these five decisions shape everything else:

  1. Subscriptions are calculated from events with effective dates, not from current state.
  2. Prices and VAT rates are versions with effective dates, and each invoice points to the version it used.
  3. The tax decision is made in one service, stored on the invoice and inherited by credit notes.
  4. Payments are event sequences with return reasons. The product learns about them through an entitlement, not from the payment provider directly.
  5. Exports and reports are projections that can be recalculated. Finalised invoices never are.

Test the architecture against the opening example: upgrade on 12 March, new VAT ID in May, returned debit in April. If all three end up correctly in invoice, tax, access and export without manual intervention, the model holds. Kontier exposes subscriptions, usage events, EU tax logic and SEPA collection through one API; the developer documentation is the place to start.

Not tax or legal advice. This post describes the law and common practice in general terms. For your own case, ask your tax adviser. As of:

Book a demo

A technical demo: 15 minutes with an engineer on your pricing model and tax setup.

How we handle your details: privacy policy.

Prefer email? contact@frontieralgorithmics.com

Message Kontier

Write to us right here. We reply by email, usually within one business day.

How we handle your details: privacy policy.

Personal invitation · 30 days

Try Kontier for 30 days, with a personal invite

We set up a trial account for you and send the invite personally. No credit card, the trial ends on its own.

Your invite arrives within 48 hours at the address you enter, sent by a person, not a script.

Separate from the public 14-day trial, which starts with a card on the pricing page.