PRD: Invoice lifecycle & issuance
| Module | Tax & Invoice (CORE-10) | PRD ID | PRD-INV-001 |
| Status | Shipped | Owner | Tax & Invoice squad |
| Date | 2026-04-13 | Version | v1.0 |
| Packages | @nx/invoice · @nx/taxation · @nx/iiapi · @nx/t-van | URD | INV · REQ · MOD |
TL;DR
Turns a completed payment into a legal Vietnamese electronic invoice - automatically queued and issued through an authorized provider, tracked through its lifecycle with retry, submitted to the tax authority, and recorded in an immutable audit trail. For owners and cashiers it means every paid order can become a numbered, compliant invoice; for buyers it means self-claiming their own invoice from a receipt QR. The outcome: a paid order is no longer legally incomplete - it ends in an issued invoice with full traceability.
1. Context & Problem
A completed payment is not legally complete in Vietnam until a numbered electronic invoice is issued through an authorized e-invoice provider and (when required) submitted to the tax authority. The module already records the seller tax identity, tax groups, and invoice configuration, but there is no path from a paid order to an issued invoice - no engine that consumes payment-success, drives issuance, tracks status, retries failures, or keeps an audit trail.
This increment builds the issuance engine on top of that configuration: an @nx/invoice package that turns payment-success into a queued issuance, issues through the provider, tracks the invoice through its lifecycle, and records every event immutably. The provider path runs through iiapi (VNPAY / VNIS) as the single issuance gateway, with buyer-info capture and a self-service claim flow so an invoice can be requested at the counter or claimed by the buyer.
2. Goals & Non-Goals
Goals
- Queue an invoice for issuance automatically on a successful payment, issue it via the provider, and capture the invoice number and tax-authority code.
- Track the invoice through
pending → processing → success / failed / cancelledwith a configured retry policy on failure. - Submit issued invoices to the tax authority (CQT via T-VAN) when enabled and track the submitted status.
- Record an immutable audit-trail entry for every invoice event.
- Support invoice adjustment, replacement, and cancellation (with reason) against an issued invoice.
- Capture buyer info (name, tax code, address, email) and support a buyer self-service claim via receipt QR with a claim token and deadline.
- Support the four issuance modes: real-time on payment, manual at the counter, scheduled batch, and buyer self-service.
Non-Goals
- Multiple invoice providers beyond the current iiapi / T-VAN set.
- PDF rendering of the invoice - produced provider-side, not in-repo.
- Tax-rate calculation at sale time - owned by the pricing engine.
- Tax declaration / filing automation.
- Seller tax identity, tax groups, and invoice configuration - owned by Tax Identity & Groups and the invoice config feature.
3. Success Metrics
| Metric | Target / signal |
|---|---|
| Issuance coverage | 100% of eligible paid orders queue an invoice on payment-success |
| Issuance success rate | Issued / queued trends up; failures retried per policy before terminal failure |
| Tax-authority acceptance | Submitted invoices accepted by CQT (where submission is enabled) |
| Audit completeness | Every state change has a matching immutable audit-trail entry; zero gaps |
| Claim conversion | Buyer self-service claims completed before deadline vs. links issued |
4. Personas & Use Cases
| Persona | Goal in this feature |
|---|---|
| Owner | Ensure every sale produces a compliant invoice; adjust / replace / cancel when needed |
| Cashier | Collect buyer info and issue the invoice at the counter |
| Buyer | Self-claim their own invoice from the receipt QR |
| Accountant (downstream) | Rely on issued invoices + audit trail for tax reporting |
Core scenarios: a payment succeeds → an invoice is queued and issued via the provider → its number and tax-authority code are captured → it is submitted to CQT and tracked → every event is recorded; alternatively a cashier issues manually with buyer info, or the buyer self-claims via receipt QR before a deadline.
5. User Stories
- As an owner, I want an invoice issued automatically when a payment succeeds, so every sale becomes legally compliant without manual work.
- As an owner, I want failed issuances retried per a configured policy, so transient provider errors don't lose invoices.
- As an owner, I want issued invoices submitted to the tax authority and tracked, so I meet CQT obligations.
- As an owner, I want to adjust, replace, or cancel an issued invoice with a reason, so I can correct mistakes legally.
- As a cashier, I want to capture buyer info and issue the invoice at the counter, so a buyer who asks gets their invoice on the spot.
- As a buyer, I want to scan the receipt QR and submit my own info before a deadline, so I can claim my invoice myself.
- As an owner, I want every invoice event in an immutable audit trail, so the invoice history is verifiable.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | Queue an invoice for issuance automatically on a successful payment | URD-INV-001 |
| FR-2 | Issue the invoice via the provider and capture its number + tax-authority code | URD-INV-002 |
| FR-3 | Track invoice status pending → processing → success / failed / cancelled | URD-INV-003 |
| FR-4 | Retry a failed issuance per the configured retry policy | URD-INV-004 |
| FR-5 | Submit the issued invoice to the tax authority (CQT via T-VAN) when enabled, and track its status | URD-INV-005 |
| FR-6 | Record an immutable audit-trail entry for every invoice event | URD-INV-006 |
| FR-7 | Adjust an issued invoice (correction linked to the original); replace or cancel with a reason | URD-INV-007..008 |
| FR-8 | Process inbound provider webhooks with signature validation to update status | URD-INV-009 |
| FR-9 | Capture buyer info (name, tax code, address, email); cashier-direct issuance at the counter | URD-REQ-001..002 |
| FR-10 | Buyer self-service claim via receipt QR with a claim token + deadline; claim lifecycle pending → claimed / expired | URD-REQ-003..004 |
| FR-11 | Deliver the invoice / claim link by receipt QR, email, or SMS | URD-REQ-005 |
| FR-12 | Support four issuance modes: real-time, manual, scheduled batch, buyer self-service | URD-MOD-001..004 |
Full requirement text and acceptance criteria live in the Tax & Invoice URD. This PRD references them rather than restating them.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Data integrity | Invoice number + tax-authority code are recorded only on confirmed issuance; status transitions are coherent with the audit trail |
| Immutability | The audit trail is append-only; corrections happen via adjustment/replacement entries, never edits |
| Tenancy & authz | All operations scoped per merchant (x-merchant-id); issuance/config gated by invoice permissions; provider credentials owner-gated |
| Reliability | Issuance runs through a queue (Kafka + BullMQ) with idempotent processing and retry; webhooks validate signatures |
| Security | Provider credentials stored encrypted; claim tokens unique and bound to a single invoice request |
| i18n | User-facing labels/statuses are bilingual ({ en, vi }) |
8. UX & Flows
Key screens: invoice configuration / onboarding wizard, invoice list and detail, manual / batch issuance, and the buyer self-service claim page reached from the receipt QR (backed by @nx/invoice; provider webhooks update status asynchronously).
9. Data & Domain
| Entity | Role |
|---|---|
Invoice | The issued invoice - status, number, tax-authority code, links to order and buyer info |
InvoiceRequest | Buyer-info capture + claim token/deadline that produces an invoice |
InvoiceAuditTracing | Immutable per-event trail of every invoice state change |
MerchantInvoiceProfile / InvoiceProvider / InvoiceProviderConfig | Merchant invoicing setup, provider credentials, serial + retry policy |
InvoiceConfigMapping | Routes a sale channel to the provider config that issues its invoices |
Conceptual only - full schema and invariants in the invoice domain model and taxation domain model.
10. Dependencies & Assumptions
Depends on
- Invoice configuration (URD-CFG) - an invoice profile, connected provider, serial, and routing must exist.
- Seller tax identity (URD-TAX) - the seller (MST, name, address) printed on every invoice.
- Payment (
payment.success) - a successful payment is what triggers automatic issuance. - Orders - the sale order supplies the line items and totals that fill the invoice.
- Provider gateways (
@nx/iiapi,@nx/t-van) - issuance and tax-authority connectivity.
Assumptions
- The merchant has an active invoice profile with a connected provider and a routed channel.
- A retry policy and (where required) tax-authority submission are configured.
- Buyer tax info is available at issuance (collected at the counter or via claim) when a VAT invoice is requested.
11. Risks & Open Questions
| Risk / question | Mitigation / status |
|---|---|
| Provider outage / transient errors | Queued issuance with configured retry; terminal failure recorded in the audit trail |
| Duplicate issuance on event replay | Idempotent queue processing keyed per order/request |
| Webhook spoofing | Inbound provider webhooks validated by signature before status update |
| Reversing an issued invoice | Adjustment / replacement / cancellation carry a reason and link to the original; nothing is hard-deleted |
| Single provider set (iiapi / T-VAN) | Accepted for this increment; additional providers are a later phase |
12. Release Plan & Launch Criteria
| Aspect | Plan |
|---|---|
| Phase | P1 (foundation) - see URD feature catalog |
| Rollout | All merchants with an active invoice profile; gated by invoice configuration |
| Migration | None (new entities; invoice config and provider credentials set up via onboarding) |
| Launch criteria | payment-success → queue → issue → number + tax-authority code captured verified end-to-end; retry on failure verified; CQT submission tracked; audit-trail entry per event; buyer self-service claim verified |
| Monitoring | Issuance success/failure rate, retry counts, CQT acceptance rate, queue lag, webhook errors, claim conversion |
13. FAQ
When does an invoice get issued? By default in real time on payment.success. It can also be issued manually by a cashier, in a scheduled batch, or by the buyer self-claiming via QR.
What happens if the provider rejects issuance? The invoice is retried per the configured policy; once retries are exhausted the status is failed and the failure is recorded in the audit trail.
Can an issued invoice be changed? Not in place - it can be adjusted (a correction linked to the original), replaced, or cancelled with a reason. The audit trail stays immutable.
Who renders the PDF? The provider, not the app. Rendering is provider-side.
How does a buyer claim their own invoice? They scan the receipt QR, open the claim link before the deadline, and submit their buyer info; the invoice is then issued with those details. After the deadline the claim is expired.
Does this calculate tax rates? No - tax-rate calculation at sale time is owned by the pricing engine; this increment issues the invoice from the order data it receives.
References
- URD: Tax & Invoice - Invoice Lifecycle · Invoice Request & Buyer Claim · Issuance Modes
- Depends on: Invoice Configuration · Tax Identity
- Related PRD: Tax Identity & Groups · E-Invoice Provider Integration
- Module: Tax & Invoice - overview + traceability
- Developer: @nx/invoice · @nx/taxation · iiapi · t-van