Softellar

Cloud-Native SaaS Modernization for a U.S. Accounting and Payroll Software Provider

From desktop-era licensing to self-serve subscription SaaS on Azure: reverse-engineered domain rules, ASP.NET Core services, Stripe-backed billing, high-volume audit storage, and a React experience accountants can run in the browser.

Client

Independent software vendor (accounting and payroll suite, United States)

Location

United States

Platform

Azure SaaS (.NET 8 APIs and workers, React SPA, Azure SQL and Blob Storage)

Engagement Model

Dedicated Team

Team Size

7 specialists

Duration

18 months

Industries

Software Products
Professional Services

Technologies

.NET
C#
ASP.NET Core
Entity Framework Core
React
TypeScript
Azure
Azure SQL
Azure Blob Storage
Azure Service Bus
Azure Key Vault
Entra ID
Stripe
Application Insights
OpenTelemetry
GitHub Actions
Playwright
xUnit

About The Customer

The Customer is a United States software company that sells accounting and payroll packages to tens of thousands of small and mid-sized organizations. A large share of revenue still arrives through accountant and IT partner recommendations, which raised the bar for accuracy, explainability, and predictable upgrades. For years the flagship suite lived as a desktop product with a thin companion portal; customers expected the same depth of ledgers, tax worksheets, and payroll outcomes - now available anywhere, on a subscription they can manage themselves.

Key Highlights

  • Reverse-engineered business rules and calculation paths from legacy binaries and sparse notes into a governed domain model linked to delivery work items
  • Cloud-hosted SaaS on Azure with self-serve onboarding, stateless ASP.NET Core APIs, EF Core, and strict per-customer data isolation on a shared platform
  • Self-serve subscription lifecycle replacing manual license keys and phone-based activation
  • Customer-facing document generation from versioned templates, including lightweight in-browser edits for common letterhead and statement layouts
  • Bank-feed ingestion and outbound payment initiation through vetted provider APIs with idempotent posting
  • High-volume, tamper-evident audit logging stored in low-cost object storage instead of overloading the primary relational database

The Challenge

Leadership committed to a full move from installable clients to a browser-first SaaS experience on Azure. The product surface was enormous: hundreds of workflows, registers, and payroll wizards that had accreted over more than a decade. Formal specifications were incomplete, and many accounting rules lived only in code paths or field trainer anecdotes. Re-implementing that behavior for the web without silently changing outcomes was the core risk - especially for tax and wage calculations where a rounding difference becomes a support storm.

Commercially, the legacy license model could not support rapid onboarding or usage-based add-ons. Operations still depended on manual billing touches and one-off key issuance. The Customer needed centralized updates, elastic capacity during payroll windows, and a subscription backbone that finance could reconcile without custom spreadsheets - while keeping partner trust high in a regulated-adjacent category.

Pain Points

  • Undocumented or divergent rules across modules made parity testing slow and anxiety-inducing
  • Desktop release trains blocked security patching and delayed customer-visible improvements
  • Provisioning and billing friction capped growth in the accountant-led channel
  • Audit expectations were rising, but storing every field-level change in SQL threatened cost and performance
  • Integrations (banks, payments, government filing hand-offs) were brittle and hard to observe in production

Challenges We Addressed

  • Truth for calculations: We separated immutable calculation contracts from UI chrome so payroll and tax logic could be unit-tested independently and compared against golden outputs from the legacy system.
  • Multi-tenancy: Every API boundary carried an explicit tenant_id and organization scope; EF Core global filters and row-level conventions prevented cross-tenant reads during refactors.
  • Long-running payroll batches: Windowed runs and statement generation could not block HTTP threads; durable messaging on Azure Service Bus coordinated workers with retries and dead-letter inspection.
  • Compliance-friendly automation: Bank and payment calls used provider webhooks, stored idempotency keys, and emitted structured audit events without persisting full payloads in the primary OLTP database.

Project Team Composition

  • 2 Senior .NET engineers (core APIs, payroll orchestration, integration adapters)
  • 1 Frontend engineer (React SPA, template editor surfaces, accessibility passes)
  • 1 Business analyst / subject-matter liaison (rule discovery, parity matrices, UAT with partner firms)
  • 1 QA engineer (Playwright journeys, payroll scenario packs, regression harnesses)
  • 1 Azure engineer (networking, Key Vault, environments, cost guardrails)
  • 1 Product manager (roadmap sequencing, cutover planning, stakeholder comms)

Our Solution

Softellar delivered a modular SaaS architecture. A set of stateless .NET ASP.NET Core APIs owns ledgers, payroll batches, tax configuration, and integration edges. A React and TypeScript SPA provides the day-to-day operator experience, while background workers drain queues for statements, filings packaging, and webhook processing. Entra ID handles workforce sign-in and partner organizations; Stripe backs plans, trials, and seat-based add-ons with webhooks reconciled in .NET.

Payroll management web UI with pay run list, status filters, and period summary
Payroll operations: pay runs, statuses, and period context in the browser-first experience that replaced desktop-only workflows.

Domain rediscovery and traceability

Analysts and engineers reconstructed behavior screen by screen, then encoded it as a navigable domain graph: entities, calculations, validation gates, and persistence mappings. Each graph node linked to Azure DevOps work items so testers could trace a discrepancy from a UI symptom to the underlying rule, golden file, and deployment that touched it.

Parity suites compared legacy outputs to the new services for representative tenants across wage bases, benefit deductions, and state configurations. Where intentional improvements were approved - such as clearer rounding policy - they were documented as explicit deltas rather than silent drift.

Customer management UI listing organizations with search, status, and subscription context
Customer and organization management: searchable firm records, lifecycle state, and subscription context for support and onboarding teams.

Data platform: Azure SQL, EF Core, and Blob-backed artifacts

Azure SQL remains the system of record for tenants, fiscal periods, payroll runs, and configuration with EF Core migrations versioned alongside APIs. Large binary outputs - PDF bundles, generated forms, and append-only audit envelopes - land in Azure Blob Storage with metadata rows in SQL for retrieval and lifecycle policies. That split kept OLTP lean while preserving defensible history for partners and auditors.

Audit logs UI with filterable event list, actor, entity, and timestamp columns
Audit trail browsing: filterable events for who changed what, aligned with append-only storage in Blob and pointer rows in Azure SQL.

Templates, documents, and in-browser adjustments

Document generation moved to provider-backed rendering pipelines orchestrated from .NET workers. Customers can fork approved templates, adjust layouts in the browser for allowed fields, and publish versions with semantic versioning so support can see exactly which template produced a statement.

Reports UI with saved report definitions, parameters, and export actions
Reporting and exports: saved definitions and parameters for statements and operational reports generated from versioned templates.

Subscriptions, metering, and finance alignment

Stripe Checkout and Customer Portal flows replaced manual invoicing for standard SKUs, while internal admin APIs apply credits and partner-specific price books under RBAC. Webhook handlers in .NET normalize events into ledger-friendly records so finance systems did not need bespoke CSV drops.

Billing and subscriptions UI showing plans, renewal dates, and payment status
Self-serve billing: plans, renewals, and payment status surfaced for finance and customer success without manual license key workflows.

Bank feeds, payments, and observability

Ingestion adapters normalize provider-specific transactions into a canonical ledger event stream; outbound payments use signed requests, stored idempotency keys, and explicit state machines so retries never double post. Application Insights and OpenTelemetry traces span API, worker, and external calls with correlation IDs propagated to support tickets.

  • Secrets and keys: Azure Key Vault backs database, Stripe, and provider credentials with rotation playbooks.
  • Release safety: feature flags guard payroll engine changes; canary tenants exercise new tax tables before broad activation.
  • Testing: xUnit covers calculation contracts; Playwright covers end-to-end payroll and billing journeys on every mainline build via GitHub Actions.

Security posture for a regulated-adjacent product

Encryption in transit and at rest, least-privilege managed identities for app services and workers, and periodic access reviews for partner impersonation features were treated as product requirements - not late add-ons. Penetration findings fed back into the same work-item graph so remediation stayed traceable.

Why We Built It This Way

  • Stateless APIs: Payroll peaks scale horizontally without sticky session assumptions; all durable state lives in SQL, blobs, or queues.
  • Service Bus for orchestration: Decouples interactive users from batch-heavy work and gives operators a first-class dead-letter story when a provider misbehaves.
  • Blob storage for audit volume: Keeps relational costs predictable while retaining immutable artifacts with retention policies aligned to customer agreements.
  • Stripe for monetization: Faster experimentation with plans and add-ons compared to bespoke billing code in the first release wave.
  • React SPA: Rich client interactions for registers and templates without coupling UI releases to full desktop installers.
  • GitHub Actions: Repeatable build, test, and deploy pipelines with artifact promotion across dev, staging, and production Azure subscriptions.

Our Approach

Delivery unfolded in four coordinated phases:

  1. Discovery and domain capture
    Interviewed accountants and support leads, mined legacy binaries, and stood up the traceability graph with parity test seeds; selected Azure services, tenancy model, and integration providers.
  2. MVP: core ledger and authentication
    Shipped Entra ID–backed sign-in, tenant provisioning, foundational GL APIs, and initial React shells for chart of accounts and bank reconciliation with observability baselines.
  3. Payroll, documents, and commercialization
    Ported prioritized payroll engines with golden comparisons, added template-driven documents, Stripe subscriptions, and webhook reconciliation; expanded Playwright coverage for partner-led scenarios.
  4. Hardening and measured cutover
    Ran parallel operations with pilot firms, tuned autoscale and SQL tiers for pay-week spikes, completed security review items, and migrated segments in waves with rollback paths.

Results and Impact

The Customer now ships continuous improvements instead of annual media drops. Directional metrics improved across onboarding time, finance reconciliation effort, and support repeatability - especially where audit trails and subscription state became queryable for the first time.

Business outcomes

  • Faster self-serve activation for new firms through subscription flows versus manual keys
  • Clearer packaging of add-ons aligned to how accountants actually sell bundled services
  • Reduced seasonal firefighting for support thanks to correlated traces and batch dashboards
  • Stronger partner confidence from visible change history tied to approvals and releases

Technical outcomes

  • Modular .NET services with explicit contracts and automated regression for payroll math
  • Elastic Azure footprint that absorbs pay-cycle spikes without over-provisioning year-round
  • Durable messaging and idempotent integrations that survive provider timeouts
  • Cost-aware storage split between SQL and Blob for operational and audit data

Tools and Technologies

.NET 8, C#, ASP.NET Core, Entity Framework Core, React, TypeScript, Azure SQL, Azure Blob Storage, Azure Service Bus, Azure Key Vault, Entra ID, Stripe, Application Insights, OpenTelemetry, GitHub Actions, Playwright, xUnit.

For similar replatforming work, our legacy application modernization and cloud architecture practices connect product strategy, engineering, and reliable Azure operations end to end.

Modernize a Mature Product Without Losing the Rules

We help product companies move from licensed desktop software to secure, self-serve subscription SaaS on Azure while preserving the calculations customers trust.

Ready to Scale Your Development Team?

Let's discuss how our expert developers can help accelerate your project and achieve your business goals with cutting-edge technology solutions.