Skip to content

Taxes and Invoices

Tax handling and invoice generation in MIDDAG Account involve three systems: WooCommerce (tax calculation), Stripe (invoice generation), and ISSNet (Brazilian tax invoices). Which systems are involved depends on the billing entity for the transaction.

Dual-entity tax routing

Every transaction is routed to either MIDDAG BR (Brazilian entity) or MIDDAG GLOBAL (US LLC). The entity determines which tax and invoicing systems apply:

CapabilityMIDDAG BRMIDDAG GLOBAL
CurrencyBRLUSD
Stripe accountStripe BRStripe US
InvoiceStripe Invoice (BRL)Stripe Invoice (USD)
Tax invoice (NFSe)Yes -- ISSNet, Brasilia/DFNo
Payment methodsCredit card, Pix, BoletoCredit card
Tax rulesBrazilian municipal tax (ISS) + federalUS tax (varies by state)

The routing decision follows this hierarchy:

  1. Explicit product entity assignment (if the product's channels block enables only one entity)
  2. Organization's billing_entity setting
  3. Auto-detection from tax ID (CNPJ = BR; EIN/VAT = GLOBAL)
  4. Fallback to preferred currency (BRL = BR; USD = GLOBAL)

Admin can override the entity per transaction.

Tax handling in WooCommerce

WooCommerce manages tax calculation for orders:

  • Tax rates are configured in WooCommerce > Settings > Tax
  • Tax classes map to product types (standard, reduced, zero-rated)
  • Automatic tax calculation uses WooCommerce's built-in engine or a tax plugin

MIDDAG Account does not override or duplicate WooCommerce tax settings. Tax configuration remains entirely in WooCommerce.

Stripe invoices

Stripe generates an invoice for every payment. MIDDAG Account syncs these invoices locally through webhooks:

When invoices are created

EventInvoice created by
One-time payment completedStripe (automatically with charge)
Subscription renewal paidStripe (automatically with subscription)
Manual invoice sentAdmin creates in Stripe

Webhook sync

The InvoiceStripeService processes webhooks from both Stripe accounts (BR and GLOBAL). Key events:

Webhook eventAction in MIDDAG Account
invoice.createdLocal invoice record created
invoice.finalizedInvoice marked as finalized (immutable)
invoice.paidInvoice marked as paid, linked to order and entitlement
invoice.payment_failedPayment failure recorded, triggers recovery policy
invoice.voidedInvoice voided locally
invoice.marked_uncollectibleInvoice marked as uncollectible
invoice.overdueInvoice marked as overdue, admin notified

A reconciliation cron job runs every 15 minutes to catch any missed webhooks and resolve state inconsistencies between Stripe and local records.

Invoices are linked to entitlements indirectly through orders:

Stripe Invoice -> WooCommerce Order -> Entitlement

There is no direct entitlement_id on the invoice record. The chain is: invoice references a Stripe charge, the charge maps to a WC order, and the order maps to the entitlement.

Invoice PDF

Stripe generates PDF invoices automatically. These PDFs are available for download by the customer through the portal. MIDDAG Account does not generate its own invoice PDFs -- it uses Stripe's.

Tax invoices (NFSe) -- Brazilian operations only

For transactions processed through MIDDAG BR, a TaxInvoice (NFSe) -- Nota Fiscal de Servico Eletronica -- is issued via the ISSNet municipal tax system in Brasilia/DF.

When NFSe is issued

An NFSe is issued after a Stripe invoice is paid for a BR entity transaction. The flow:

ISSNet integration

The TaxInvoice domain integrates with ISSNet through SOAP API calls:

OperationDescription
Issue NFSeSubmit XML with provider, recipient, service, tax values
Query NFSeCheck status of submitted NFSe (authorized, rejected)
Cancel NFSeCancel a previously authorized NFSe (requires justification)

Because ISSNet does not support webhooks, a polling cron job runs every 5 minutes to check the status of pending NFSe submissions.

NFSe PDF storage

Authorized NFSe PDFs are generated and stored in Cloudflare R2. A cached URL is available through the REST API for portal download. Each TaxInvoice PDF references the originating Stripe Invoice for traceability.

Product NFSe readiness

Each product YAML can include NFSe-specific fields:

yaml
nfse:
  city_service_code: "01.07"
  federal_service_code: "1.07"
  description: "Licenciamento de software"

Products without these codes cannot have NFSe issued automatically. The catalog report (composer catalog:report) flags products that need NFSe codes.

What the admin sees

  • Invoices list -- All synced Stripe invoices with status, amount, currency, and entity
  • TaxInvoice list -- All NFSe records with ISSNet status (pending, authorized, rejected, cancelled)
  • Per-entitlement view -- Invoices and tax invoices linked to a specific entitlement via its order
  • Reconciliation dashboard -- Shows sync status between Stripe and local records