Billing and Finance
This page explains the financial operations that run through MIDDAG Account: invoice generation, tax invoice issuance, payment reconciliation, credit balance management, and dual-entity financial routing.
Financial data flow
Invoice generation
Invoices in MIDDAG Account are read-only reflections of Stripe data. The system does not generate invoices independently -- it syncs them from Stripe via webhooks.
The webhook flow:
- Stripe fires an event (e.g.,
invoice.created,invoice.paid,invoice.payment_failed). - The webhook hits the appropriate endpoint (
/webhooks/stripe/bror/webhooks/stripe/llc). - The system validates the webhook signature against the account-specific secret.
- The invoice data is persisted locally, linked to the Organization via the Stripe Customer ID.
- The invoice is tagged with the originating Stripe account (
middag_brormiddag_global).
The system processes multiple invoice event types: creation, update, payment, payment failure, voiding, and finalization. Duplicate events are handled via idempotency checks.
Customer experience: The customer sees a unified list of invoices from both entities in the portal, with a visual indicator showing which entity issued each one. They can filter by entity, status, or date range, and download PDFs directly from Stripe.
Tax invoice (NFSe) for Brazilian operations
NFSe (Nota Fiscal de Servico Eletronica) is a Brazilian tax invoice required for service transactions. It is issued exclusively for MIDDAG BR transactions.
Automatic issuance
When a BR payment is confirmed:
- The system detects the entity is
middag_br. - It extracts service details, amounts, and the customer's tax information.
- It submits an XML request to the ISSNet web service (Brasilia/DF municipal authority) via SOAP.
- If ISSNet responds immediately with approval, the NFSe is recorded as
issued. - If ISSNet queues it for processing, the NFSe is recorded as
pending.
Status verification
A cron job runs every 5 minutes to check pending NFSe:
- Queries ISSNet for updated status.
- Transitions
pendingtoissued(with the official NFSe number and verification code) or torejected(with the rejection reason). - NFSe pending for more than 24 hours triggers an admin notification.
Manual issuance
The admin can issue NFSe manually from the admin panel when automatic issuance fails or when special circumstances require manual data adjustment (e.g., correcting the service description or municipal tax code).
What the customer sees
Brazilian customers see NFSe listed alongside Stripe invoices in the portal, with a distinct label. They can download both the XML (for tax compliance) and a PDF version.
Payment reconciliation
Payment data flows from multiple sources and must be reconciled:
| Source | What it covers | Sync mechanism |
|---|---|---|
| Stripe BR | Card payments for BR entity | Webhooks |
| Stripe GLOBAL | Card payments for US entity | Webhooks |
| Banco Inter | Pix and Boleto for BR entity | Webhooks / callbacks |
A reconciliation cron runs every 15 minutes to verify consistency between Stripe records and local invoice data. Discrepancies are flagged for admin review.
Unmatched invoices -- When a Stripe invoice cannot be matched to an Organization (e.g., the Stripe Customer ID is not linked), the invoice is stored with a null Organization and the admin is notified for manual linking.
Credit balance management
For SVC entitlements, the CreditBalance tracks UST consumption:
| Operation | Effect on balance |
|---|---|
| Service activated | Balance initialized (may start at zero) |
| Credits purchased | Balance increases |
| SR opened | Credits reserved (held, not yet debited) |
| SR completed | Reserved credits debited from balance |
| SR cancelled | Reserved credits released back to balance |
| Credits expired | Balance decreases (per CreditPolicy) |
The CreditPolicy governs:
- Expiration period for included credits (default: 12 months) vs. purchased credits (default: 6 months)
- Consumption order (FIFO by default -- oldest credits consumed first)
- Grace period after expiration
- Low-balance alerts (default: when 20% remains)
Dual-entity financial routing
Every financial transaction is routed to the correct legal entity:
| Decision point | How it is determined |
|---|---|
| Stripe account | Based on Organization's billing entity (BR or GLOBAL) |
| Payment methods | BR: card + Pix + Boleto. GLOBAL: card only |
| Currency | BR: BRL. GLOBAL: USD |
| Tax invoice | BR: NFSe issued via ISSNet. GLOBAL: no tax invoice |
| HubSpot portal | BR and GLOBAL have separate portals |
An Organization can have Stripe Customer IDs and HubSpot Company IDs for both entities simultaneously. This supports customers with transactions in both currencies.
See Multi-Entity Operations for the full routing logic.
Admin financial dashboard
The admin panel provides a consolidated financial view:
- Revenue by period (monthly, quarterly, annual)
- Revenue split by entity (BR vs GLOBAL)
- Outstanding payments (unpaid boletos, failed cards)
- Overdue invoices with urgency indicators
- Active vs cancelled subscriptions
All data is sourced from local WooCommerce records, not directly from Stripe, ensuring consistency even if Stripe is temporarily unreachable.
Related pages
- Purchase and Renewal Flow -- The pipeline that generates invoices
- Multi-Entity Operations -- Entity routing details
- Service Provisioning -- Credit balance consumption