Skip to content
Invoke Technologies
All work

Case study · Nonprofits & fundraising

A multi-brand fundraising platform, built from the ground up

A multi-tenant, white-label SaaS platform where organizations run campaigns, every participant gets a personal fundraising page, and donations, sales, tickets, and auction bids flow through one Stripe Connect payment pipeline.

Client
Peer-to-peer fundraising SaaS
Services
SaaS product development · Payments & marketplace integration · APIs & systems integration · Marketing sites & SEO · Testing, quality & DevOps

Highlights

Multi-tenant and white-label

Brands, partners, and organizations, each with the right access and nothing more. Every brand gets its own domains, logo, colors, and email sender from a single build.

Payments that pay out to the right people

Stripe Connect sends each gift to the right connected account - the organization’s, a partner’s, or the brand’s - so the platform never holds their money.

Webhook-driven, concurrency-safe totals

Totals only move when verified Stripe webhooks confirm the money, and stay right when events retry, arrive out of order, or land at the same moment.

One pipeline, many ways to raise money

Donations, product sales, holiday shops, event tickets, raffles, and auctions all share one payment, crediting, and ledger pipeline.

Refunds, voids, and chargebacks

Money moving backwards is split pro-rata across the gift, the fee, and any partner share, with every step recorded and safe to retry.

A configurable fee engine

Fees resolve from campaign, organization, partner, and brand settings, with optional partner revenue sharing and a donor tip that can offset the organization’s fee.

A real financial ledger

Every gift, fee, refund, and chargeback writes an auditable ledger entry - the source for reports, payout statements, and performance dashboards.

An audit trail you can follow

Every request gets a correlation id that ties together its logs, the commands it ran, and the field-level changes it made.

Automated, monitored operations

GitHub Actions builds and deploys to AWS with health checks and automatic rollback, and logs and server metrics flow to CloudWatch.

The challenge

The client wanted to give schools, youth sports teams, nonprofits, and churches a way to run peer-to-peer fundraisers themselves - without a sales call, setup fees, or a vendor taking weeks to configure a campaign. An organization runs a central campaign; each supporter gets their own personal fundraising page; every gift rolls up to their team and the campaign total in real time.

That sounds simple. The hard parts were underneath:

  • Money has to go to the right place. Donations belong to each organization, not the platform.
  • Totals have to be right. A leaderboard that’s wrong - or double-counts a retried payment - erodes trust immediately.
  • Most users are on a phone. Organizers, participants, and donors mostly arrive from a text message or social post.
  • It has to grow. Donations were only the first fundraising type; raffles, auctions, and product sales were on the roadmap - and fundraising companies wanted to offer the platform under their own brands.

Our approach

We built the platform as deployable pieces sharing one repository: a .NET 10 Web API, a Vue 3 + TypeScript dashboard, and static Astro marketing sites, with brand design tokens shared between the frontends so they always look like one product.

A backend structured to grow

The API follows Clean Architecture - Domain, Application, Infrastructure, and API layers with dependencies pointing inward. Features are expressed as MediatR commands and queries, with FluentValidation running as a pipeline behavior so every request is validated the same way. Controllers stay thin; business rules live where they can be unit tested.

We made several data-model decisions early, before real data existed, because they would have been painful to retrofit later:

  • Separating an organization from the people who administer it, so an organization can have many admins.
  • Separating a person’s identity from their participation in a specific campaign, so the same supporter returning next year links back to their existing profile.
  • Separating a generic payment record from what it paid for, so new fundraising types reuse the same payment pipeline instead of duplicating it.

That last decision paid off exactly as planned: when product sales, raffles, and auctions arrived, each one plugged into the existing payment, crediting, and ledger code instead of growing its own.

Payments done properly

Online payments use Stripe’s Payment Element and Stripe Connect Express, so each organization onboards once and is paid directly. Payment success is confirmed by signature-verified webhooks, which update participant, team, and campaign totals in a single transaction. Organizers can also record cash and check gifts by hand, and they count toward the same totals.

Because webhooks can arrive several at once while an admin acts on the same donation, every money change follows strict rules: row locks taken in a fixed order, atomic increments instead of read-then-write totals, and status checks only while holding the lock. Integration tests force the race conditions that would break those rules.

Fees resolve through a small, well-tested fee engine - campaign, organization, partner, and brand overrides, then the platform default. An optional donor tip offsets the organization’s fee, and partners can keep a configured revenue share of their organizations’ gifts. Every gift, fee, and share writes an entry to an organization ledger.

When money moves backwards

Refunds, voids, and chargebacks are where many payment systems quietly go wrong. Here, each one is split pro-rata across the gift, the platform fee, and any partner share, with cumulative rounding so several partial refunds add up exactly to a full one. Refunds are recorded before Stripe is called and finished by webhook if anything fails midway. Chargebacks are mirrored from Stripe, lock the donation while open, and restore exactly the same shares if the dispute is won.

More than donations

On the same pipeline, organizations can now run:

  • Product sales - a catalog, online checkout from each participant’s page, paper order entry, and fulfillment reports.
  • Holiday shops - vouchers parents buy online and students spend at an in-person shop.
  • Event tickets - seats and tables, guest questions for the caterer, QR tickets, check-in, and receipts that show the deductible amount.
  • Raffles - licensed, numbered tickets online and on paper, with verifiable drawings.
  • Auctions - online timed, silent, live, and fund-a-need, with proxy bidding, soft close, buy-it-now, and pay links for winners.
  • Volunteer sign-ups - shifts, roles, and items to bring, with a waitlist, reminders, and check-in.

Each new fundraising type ships behind its own switch, so it can be built and tested end to end while questions like sales tax and raffle licensing are settled before it goes live.

Many brands, one platform

The platform is white-label: a brand has its own domains, logo, colors, email sender, and default fee, and fundraising companies manage their own organizations as partners under it. The brand is resolved from the host on every request, the dashboard is themed at runtime from one build, and Stripe routing can send money to each organization, each partner, or the brand. Partners get their own performance snapshot and an organization payouts report.

Email that runs itself

Campaign emails come from a library of templates and schedules maintained by platform staff, which protects the platform’s sending reputation while organizers choose recipients and timing. A background worker delivers them through SendGrid, and a signed event webhook records deliveries, clicks, bounces, and unsubscribes so organizers can see engagement - down to each email’s delivery history.

An audit trail you can follow

Every request gets a correlation id that appears on its log lines, in a log of every command it ran (with secrets stripped), and on a field-level change log written in the same save. Raw Stripe events are kept before they’re handled, so staff can see exactly what happened to a donation and safely replay an event if needed.

Mobile-first everywhere

The dashboard was built for a ~375px screen first: stacked forms, full-width tap targets, card lists in place of wide tables, bottom-sheet dialogs, and form controls sized so phones don’t zoom on focus. Rosters are built for a thousand participants, with fast in-browser filtering.

Marketing sites that rank and load instantly

Each brand’s public site is built with Astro and ships as plain, pre-rendered HTML - with structured data, a sitemap, social share cards, Markdown-driven content pages, cookieless analytics, and small interactive islands only where needed, like a fee calculator.

Quality and operations

The solution has three test projects mirroring the layers they test. Domain and application logic are covered by xUnit unit tests; the API is covered by integration tests that spin up a real PostgreSQL database with Testcontainers for every run, with payment and email providers swapped for fakes so tests stay fast and deterministic.

GitHub Actions builds one release and deploys it to AWS - database migrations, a health check, and automatic rollback if the new version doesn’t come up. A separate demo deployment, reset with realistic sample fundraisers, runs on Stripe test mode so production never mixes with demo data. Logs and disk, memory, and swap metrics go to CloudWatch, with alarms.

The result

A complete, end-to-end platform - from organizer signup and bank onboarding to donation, ticket, or winning bid, ledger entry, and follow-up email - running under multiple brands, and designed so organizers can use it without training. The early architecture decisions held up: each new way to raise money had an obvious place to go.

Architecture at a glance

Dashboard SPA

Vue 3 · TypeScript · Tailwind

Marketing site

Astro · static HTML

.NET 10 Web API

API

ASP.NET Core controllers · JWT auth · versioning

Application

MediatR commands & queries · FluentValidation

Domain

Entities & business rules · no framework deps

Infrastructure

Implements Application interfaces · background workers

PostgreSQL

EF Core · migrations

Stripe Connect

Payments · webhooks

SendGrid

Email · event webhooks

Clean Architecture: dependencies point inward, so business rules never depend on frameworks or vendors.

Let’s build yours

Tell us what you’re building. We’ll reply within one business day with honest next steps - even if that means pointing you somewhere else.