PRD: Invoice lifecycle & issuance
| Module | Tax & Invoice | PRD ID | PRD-INV-001 |
| Status | Ready to dev | FEAT | INV |
| Epic | — | Plane | BANA-1524 |
| Date | 2026-04-13 | Version | v1.0 |
| Packages | @nx/invoice | URD | INV |
| Surface | Backend | ||
| Owner | Phát Nguyễn | ||
TL;DR
Turns a completed payment into a legal Vietnamese e-invoice - auto-queued and issued through an authorized provider, tracked with retry, submitted to the tax authority, and written to an immutable audit trail. Every paid order becomes a numbered, compliant invoice (and buyers can self-claim theirs from a receipt QR), so a paid order is no longer legally incomplete.
1. Context & Problem
In Vietnam a completed payment isn't legally complete until a numbered e-invoice is issued through an authorized provider and (when required) submitted to the tax authority. The module already records the seller tax identity, tax groups, and invoice configuration - but nothing connects a paid order to an issued invoice: nothing to consume payment success, drive issuance, track status, retry failures, or keep an audit trail.
This increment builds that path on the existing configuration: an invoicing capability that turns payment success into a queued issuance, issues through the provider, tracks the invoice across its lifecycle, and records every event immutably. Issuance runs through one provider (VNPAY) as the single gateway, with buyer-info capture and a self-service claim 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) when enabled and track the submitted status.
- Record an immutable audit-trail entry for every invoice event.
- Support invoice adjustment and cancellation (with reason) against an issued invoice; replacement is modeled as an invoice origin but issuing a replacement is not delivered in this increment.
- 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 provider set.
- PDF rendering of the invoice - produced provider-side, not in-house.
- Tax-rate calculation at sale time - owned by pricing.
- 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 / 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 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) 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) or cancel with a reason; replacement is modeled but not delivered in this increment | 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 (functional); email and SMS channels are scaffolded but not yet wired to a sender | 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 entries, never edits |
| Tenancy & authz | All operations confined to the user's own merchant; issuance/config gated by invoice permissions; provider credentials owner-gated |
| Reliability | Issuance runs through an event-driven queue 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 (English / Vietnamese) |
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 (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 |
| Invoice request | Buyer-info capture + claim token/deadline that produces an invoice |
| Invoice audit trail | Immutable per-event trail of every invoice state change |
| Merchant invoice profile / provider connection / provider configuration | Merchant invoicing setup, provider credentials, serial + retry policy |
| Channel routing | Routes a sale channel to the provider configuration 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 (tax code, name, address) printed on every invoice.
- Payment - a successful payment is what triggers automatic issuance.
- Orders - the sale order supplies the line items and totals that fill the invoice.
- Provider gateways (e-invoice provider, tax-authority transmission network) - 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 / cancellation carry a reason and link to the original; nothing is hard-deleted |
| Single provider set | 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) or cancelled with a reason. Replacement is recognized as an invoice origin but issuing a replacement is not delivered in this increment. 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 pricing; 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 - URD
- Developer: @nx/invoice · @nx/taxation · iiapi · t-van