diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..f88c4f6 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,43 @@ +name: Deploy GitHub Pages + +on: + push: + branches: + - main + paths: + - 'docs/**' + - '.github/workflows/pages.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Pages + uses: actions/configure-pages@v5 + with: + enablement: true + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..39f1adc --- /dev/null +++ b/docs/index.html @@ -0,0 +1,2415 @@ + + +
+ + ++ High-velocity business primitives: Indian localized fintech (GSTIN, PAN, IFSC, Aadhaar), transactional outbox with SKIP LOCKED, multi-channel notifications, PDF tax invoices, partitioned audit trails, and streaming CSV export. +
+ + +go get github.com/umesh0492/go-app-kit@v0.3.2
+
+ How modern enterprise microservices build upon go-app-kit and go-libs for zero-compromise reliability.
+Six hardened packages architected for zero data races, high concurrency, and statutory precision.
++ Zero-dependency statutory Indian validation algorithms, fiscal calendar arithmetic, and exact integer paise currency calculations. +
++ Guarantees at-least-once message delivery without dual-write race conditions using PostgreSQL row-level lease fencing. +
+SELECT ... FOR UPDATE SKIP LOCKED allows $N$ worker replicas to poll without lock contention.DEAD_LETTER.go-libs/workerpool for bounded background concurrency.+ Unified multi-channel alert broker supporting synchronous and asynchronous delivery powered by bounded workerpools. +
+X-Signature-SHA256 tamper-proofing, timestamp binding, and constant-time signature verification.+ Zero-disk in-memory HTML-to-PDF compilation engine with bundled statutory GST tax invoice and payment receipt templates. +
+*bytes.Buffer, eliminating temporary file leaks in containerized workloads.Renderer interface allows mocking in unit tests or alternate render backends.+ Enterprise compliance audit logging with partitioned PostgreSQL persistence, append-only triggers, and JSON state diffing. +
+created_at with trigger-enforced append-only constraints.ComputeDiff automatically calculates field-level before-and-after property deltas.
+ High-throughput, low-memory streaming CSV export directly to io.Writer or HTTP response streams.
+
\xEF\xBB\xBF) for flawless Excel rendering.=, +, -, @) preventing CSV injection attacks.Test statutory algorithms offline: GSTIN Mod-36 checksum, Aadhaar Verhoeff D5, PAN classification, and INR Paise calculation.
+Complete production invoice processing pipeline combining all 6 modules located in examples/invoice_service.
+ Demonstrates zero-compromise architectural decoupling: statutory checks run before database calls, documents render in memory without container disk I/O, events commit transactionally with PostgreSQL, and notifications queue asynchronously. +
+ +ValidateGSTIN and ValidateIFSC run in sub-microsecond time before any expensive DB or PDF processing.GenerateFromTemplate compiles tax invoice directly into a *bytes.Buffer with zero disk leaks.outboxStore.Insert commits the domain event in the same Postgres TX. No split-brain states.audit.RecordAsync writes partitioned, append-only logs with before/after state diffs.broker.SendAsync delivers customer email with PDF attachment and Slack ops alert via workerpool.Production-ready idiomatic code patterns designed for clean integration and zero boilerplate.
+package main
+
+import (
+ "fmt"
+ "github.com/umesh0492/go-app-kit/india"
+)
+
+func main() {
+ // 1. Exact Paise Integer Arithmetic (Zero floating-point inaccuracies)
+ taxable := india.NewMoneyFromFloat(15000.50) // 1500050 paise
+ cgst := taxable.Percentage(9.0) // 9% CGST
+ sgst := taxable.Percentage(9.0) // 9% SGST
+ total := taxable.Add(cgst).Add(sgst)
+
+ fmt.Printf("Taxable: โน%s\n", taxable.Format()) // "15,000.50"
+ fmt.Printf("Total: โน%s\n", total.Format()) // "17,700.59"
+
+ // 2. Amount to Words (INR)
+ words := india.AmountToWordsINR(total.Paise())
+ fmt.Printf("In Words: %s\n", words)
+
+ // 3. GSTIN Statutory Mod-36 Checksum Validation
+ gstin := "27AAPFU0939F1ZV"
+ if err := india.ValidateGSTIN(gstin); err != nil {
+ fmt.Printf("Invalid GSTIN: %v\n", err)
+ } else {
+ parsed, _ := india.ParseGSTIN(gstin)
+ fmt.Printf("State: %s (%s), Entity: %s\n",
+ parsed.StateCode, parsed.StateName, parsed.PAN.EntityType())
+ }
+
+ // 4. Aadhaar Verhoeff D5 Dihedral Checksum & Masking
+ aadhaar := "234567890128"
+ if india.IsValidAadhaar(aadhaar) {
+ fmt.Printf("Masked Aadhaar: %s\n", india.MaskAadhaar(aadhaar)) // "XXXX-XXXX-0128"
+ }
+}
+ package main
+
+import (
+ "context"
+ "time"
+
+ "github.com/jackc/pgx/v5/pgxpool"
+ "github.com/umesh0492/go-app-kit/outbox"
+)
+
+func main() {
+ ctx := context.Background()
+ pool, _ := pgxpool.New(ctx, "postgres://user:pass@localhost:5432/app_db")
+ defer pool.Close()
+
+ // 1. Initialize PostgreSQL Outbox Store with lease fencing
+ store := outbox.NewPGStore(pool, outbox.WithLeaseDuration(30*time.Second))
+
+ // 2. Transactionally commit domain entity and outbox event in same DB transaction
+ tx, _ := pool.Begin(ctx)
+ defer tx.Rollback(ctx)
+
+ // ... execute business mutations on orders/invoices table ...
+
+ evt, _ := outbox.NewEvent("Invoice", "INV-2026-001", "InvoiceCreated", map[string]any{
+ "amount_paise": 1770059,
+ "customer_id": "CUST-8821",
+ })
+ if err := store.Insert(ctx, tx, *evt); err != nil {
+ return
+ }
+ _ = tx.Commit(ctx) // Atomic commit: zero phantom records
+
+ // 3. Autonomous background relay using SELECT ... FOR UPDATE SKIP LOCKED
+ relay, _ := outbox.NewRelay(outbox.RelayConfig{
+ Store: store,
+ Publisher: kafkaPublisher,
+ PollInterval: 500 * time.Millisecond,
+ BatchSize: 100,
+ })
+ go relay.Start(ctx)
+}
+ package main
+
+import (
+ "context"
+ "github.com/umesh0492/go-app-kit/notifications"
+)
+
+func main() {
+ ctx := context.Background()
+
+ // 1. Initialize multi-channel broker with bounded workerpool
+ broker := notifications.NewBroker(notifications.DefaultConfig())
+ defer broker.Close()
+
+ // 2. Register senders: Email (SMTP), Slack, and HMAC-Signed Webhook
+ broker.RegisterSender(notifications.NewEmailSender(notifications.EmailConfig{
+ Host: "smtp.mail.com",
+ Port: 587,
+ User: "alerts@domain.com",
+ Pass: "secret",
+ From: "Billing ",
+ }))
+ broker.RegisterSender(notifications.NewSlackSender(notifications.SlackConfig{
+ WebhookURL: "https://hooks.slack.com/services/T00/B00/X00",
+ Channel: "#finance-alerts",
+ }))
+
+ // 3. Non-blocking asynchronous dispatch
+ _ = broker.SendAsync(ctx, notifications.Message{
+ Title: "Statutory Tax Invoice Ready: INV-2026",
+ Body: "Your invoice has been generated with GST breakdown.",
+ Priority: notifications.PriorityHigh,
+ Recipients: []string{"client-finance@corp.com"},
+ Channels: []notifications.Channel{notifications.ChannelEmail, notifications.ChannelSlack},
+ Attachments: []notifications.Attachment{
+ {Filename: "INV-2026.pdf", ContentType: "application/pdf", Data: pdfBytes},
+ },
+ })
+}
+ package main
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/umesh0492/go-app-kit/pdf"
+)
+
+func main() {
+ // Statutory B2B GST Invoice Data
+ invoiceData := pdf.InvoiceData{
+ InvoiceNumber: "INV-2026-0042",
+ InvoiceDate: "2026-09-20",
+ SupplierName: "Acme Cloud Technologies Pvt Ltd",
+ SupplierGSTIN: "27AAPFU0939F1ZV",
+ BuyerName: "Enterprise Retailers Ltd",
+ BuyerGSTIN: "29AAACI1681G1ZM",
+ Items: []pdf.InvoiceItem{
+ {
+ Description: "Cloud Infrastructure - September",
+ HSNSAC: "998313",
+ Quantity: 1,
+ UnitPrice: 15000.50,
+ CGSTRate: 9.0,
+ SGSTRate: 9.0,
+ },
+ },
+ }
+
+ // In-memory HTML-to-PDF compilation (Zero disk I/O)
+ pdfBuf, err := pdf.GenerateFromTemplate(pdf.GSTInvoiceTemplate, invoiceData,
+ pdf.WithPageSize("A4"),
+ pdf.WithOrientation("Portrait"),
+ pdf.WithMargins(10, 10, 10, 10),
+ )
+ if err != nil {
+ fmt.Printf("PDF generation failed: %v\n", err)
+ return
+ }
+
+ fmt.Printf("Generated %d bytes in-memory PDF buffer!\n", pdfBuf.Len())
+}
+ package main
+
+import (
+ "context"
+ "github.com/jackc/pgx/v5/pgxpool"
+ "github.com/umesh0492/go-app-kit/audit"
+)
+
+func main() {
+ ctx := context.Background()
+ pool, _ := pgxpool.New(ctx, "postgres://user:pass@localhost:5432/audit_db")
+ defer pool.Close()
+
+ // 1. Initialize asynchronous partitioned audit recorder
+ recorder, _ := audit.NewPGRecorder(audit.Config{
+ DB: pool,
+ Workers: 4,
+ QueueSize: 500,
+ })
+ defer recorder.Close()
+
+ // 2. Calculate field-level state diff (Old vs New)
+ oldState := map[string]any{"status": "DRAFT", "amount": 10000}
+ newState := map[string]any{"status": "ISSUED", "amount": 10000, "gstin": "27AAPFU0939F1ZV"}
+
+ // 3. Construct event with context-propagated actor & IP
+ ctx = audit.WithActor(ctx, "admin@corp.com")
+ ctx = audit.WithIP(ctx, "192.168.1.50")
+
+ event := audit.NewEvent(ctx, "INVOICE_STATE_CHANGE", "Invoice", "INV-100", oldState, newState)
+ recorder.RecordAsync(event)
+}
+ package main
+
+import (
+ "net/http"
+ "github.com/umesh0492/go-app-kit/export"
+ "github.com/umesh0492/go-app-kit/india"
+)
+
+type InvoiceExportRow struct {
+ ID string
+ BuyerGSTIN string
+ AmountPaise int64
+}
+
+func exportHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/csv; charset=utf-8")
+ w.Header().Set("Content-Disposition", "attachment; filename=\"invoices.csv\"")
+
+ columns := []export.Column[InvoiceExportRow]{
+ {Header: "Invoice ID", Extractor: func(r InvoiceExportRow) string { return r.ID }},
+ {Header: "Buyer GSTIN", Extractor: func(r InvoiceExportRow) string { return r.BuyerGSTIN }},
+ {Header: "Amount (INR)", Extractor: func(r InvoiceExportRow) string {
+ return india.FormatINRPaise(r.AmountPaise)
+ }},
+ }
+
+ // Streams directly to ResponseWriter with Excel UTF-8 BOM and formula sanitization
+ streamer := export.NewCSVStreamer(w, columns, export.WithBOM(true))
+ defer streamer.Flush()
+
+ // Iteratively stream millions of rows without loading all into memory
+ for rows.Next() {
+ streamer.WriteRow(fetchNextInvoiceRow())
+ }
+}
+ package invoice_service
+
+import (
+ "context"
+ "github.com/umesh0492/go-app-kit/audit"
+ "github.com/umesh0492/go-app-kit/india"
+ "github.com/umesh0492/go-app-kit/notifications"
+ "github.com/umesh0492/go-app-kit/outbox"
+ "github.com/umesh0492/go-app-kit/pdf"
+)
+
+// ProcessInvoice combines validation, PDF generation, outbox, audit, and notifications.
+func (s *InvoiceService) ProcessInvoice(ctx context.Context, req InvoiceRequest) ([]byte, error) {
+ // Step 1: Statutory Validation (Fail-Fast)
+ if err := india.ValidateGSTIN(req.SupplierGSTIN); err != nil {
+ return nil, err
+ }
+ if err := india.ValidateGSTIN(req.BuyerGSTIN); err != nil {
+ return nil, err
+ }
+
+ // Step 2: Exact Money & Total Calculation
+ taxable := india.NewMoneyFromFloat(req.Amount)
+ cgst := taxable.Percentage(9.0)
+ sgst := taxable.Percentage(9.0)
+ total := taxable.Add(cgst).Add(sgst)
+
+ // Step 3: In-Memory PDF Document Compilation
+ pdfBuf, err := pdf.GenerateFromTemplate(pdf.GSTInvoiceTemplate, req.ToInvoiceData(total))
+ if err != nil {
+ return nil, err
+ }
+
+ // Step 4: Transactional Outbox (Postgres DB Transaction)
+ tx, err := s.pool.Begin(ctx)
+ if err != nil {
+ return nil, err
+ }
+ defer tx.Rollback(ctx)
+
+ evt, _ := outbox.NewEvent("Invoice", req.ID, "InvoiceCreated", total)
+ if err := s.outboxStore.Insert(ctx, tx, *evt); err != nil {
+ return nil, err
+ }
+ if err := tx.Commit(ctx); err != nil {
+ return nil, err
+ }
+
+ // Step 5: Asynchronous Audit Logging
+ s.auditRecorder.RecordAsync(audit.NewEvent(ctx, "INVOICE_ISSUED", "Invoice", req.ID, nil, total))
+
+ // Step 6: Multi-Channel Alerts via Workerpool
+ _ = s.broker.SendAsync(ctx, notifications.Message{
+ Title: "Invoice Issued: " + req.ID,
+ Body: "Attached is your GST Tax Invoice.",
+ Recipients: []string{req.BuyerEmail},
+ Channels: []notifications.Channel{notifications.ChannelEmail, notifications.ChannelSlack},
+ Attachments: []notifications.Attachment{
+ {Filename: req.ID + ".pdf", ContentType: "application/pdf", Data: pdfBuf.Bytes()},
+ },
+ })
+
+ return pdfBuf.Bytes(), nil
+}
+ Continuous verification enforced by scripts/check_version.sh and scripts/check_coverage.sh.
| Package | +Purpose | +Dependencies | +Statement Coverage | +
|---|---|---|---|
india |
+ Statutory Indian validations (GSTIN mod-36, PAN, IFSC, Aadhaar Verhoeff D5, INR Money, Aging) | +Zero external | +95.5% | +
export |
+ Low-memory streaming CSV exporter with Excel UTF-8 BOM & formula injection protection | +Zero external | +93.8% | +
notifications |
+ Multi-channel notification broker (SMTP Email, Slack, Webhook HMAC-SHA256 & versioning) | +go-libs/workerpool |
+ 92.3% | +
outbox |
+ PostgreSQL transactional outbox engine with row-level locked poller & lease fencing | +jackc/pgx/v5 |
+ 90.0% | +
audit |
+ Partitioned PostgreSQL audit logging with automated JSON diffing & append-only triggers | +jackc/pgx/v5 |
+ 89.5% | +
pdf |
+ In-memory HTML-to-PDF compilation & embedded GST invoice templates | +Pluggable / wkhtmltopdf | +80.7% | +
examples/invoice_service |
+ Reference microservice with end-to-end integration test & exact paise math | +Internal integration | +82.9% | +
| Overall Repository | +Verified Statement Coverage across whole repository (Zero data races) | +Go 1.26.0 | +90.5% | +