Back to all work
Case Study · 02 Multi-tenant SaaS · Finance Live · Paying customers

SeeMyChurch + Pastor OS

What started as a church finance SaaS evolved into a full Ministry Operating System — two interlocking products on one WordPress install. ChurchVault handles finance across HQ-and-branch denomination networks. Pastor OS Discovery Engine powers a public pastor directory, gospel music archive, AI sermon builder, free Bible engine, and a sponsored ministry ad rail. Built solo. Over 20 plugins and modules. ~50,000 lines of PHP. Live with paying customers.

Stack
WordPress 6.xAstra · Hostinger LiteSpeed · PHP 8
Role
Solo buildConcept · code · launch
Products
ChurchVault + Pastor OSFinance · Discovery · AI · Bible
The brief

Church finance, done properly.

Most church finance tools fall into one of two camps. Either they're consumer accounting software like QuickBooks, retrofitted with a "Donations" category and called good — never engineered for the structural reality of how churches actually run their money. Or they're enterprise denominational tools that cost $300+ per month and require a finance director to operate.

Nigerian denominations sit awkwardly between both. A typical RCCG, Anglican, or Methodist network has a national HQ with oversight, regional districts, and dozens or hundreds of branches — each with their own accounts, their own income streams, their own approval cultures. Money moves in Naira locally, Dollars from international missions, Pounds from diaspora partners. Tax reporting straddles Nigerian PAYE, CIT, VAT and UK Gift Aid for branches abroad. None of the international tools speak this geography.

SeeMyChurch was built to fit that shape exactly. Multi-tenant from day one — HQ admins see aggregated views across all their branches, branch managers see only their own. Multi-currency native — every bank account stores its own currency and symbol, so a Lagos church with both ₦ and $ accounts gets accurate per-currency totals, never a confusing converted aggregate. Paystack-first billing — recurring subscriptions in Naira, retry logic, kill switches. Built on WordPress, hosted on Hostinger — because the alternative is asking Nigerian churches to learn AWS, and that's not the job.

The platform is live at seemychurch.org, has been onboarding paying customers since Q2 2026, and has grown from a four-plugin finance tool to a two-product ministry operating system — ChurchVault for church finance and Pastor OS Discovery Engine for public ministry discovery, AI tools, and a free Bible engine. Over 20 custom plugins and modules, totalling ~50,000 lines of PHP, with zero JavaScript build pipeline.

Architecture

Two products, one install. Five standalone plugins.

The platform is built as five standalone custom WordPress plugins — plus twelve internal sub-modules inside the main finance plugin. Every plugin is independently activatable and upgradable. Every table shares the cv_ or pos_ prefix. No third-party library dependencies beyond WordPress core.

/01
churchvault-hq
Main finance engine · v0.6.0 · ~6,900 lines · multi-tenant queries · Budget Planning v4 · Tax Calculator · Bank Import
/02
pastor-os-discovery-engine
Public ministry OS · v0.6.6 · pastor directory · gospel music · sermons · Bible engine · AI Vault · ad rail
/03
churchvault-admin-pro
Modern platform overview dashboard · sleek admin shell · replaced default WP admin aesthetics
/04
smc-church-pages
Free church micro-pages at /church/{slug} · subscriber benefit · YouTube latest video auto-pull
/05
churchvault-faqbot
Floating FAQ chat widget · admin-editable Q&A bank · click analytics · no external dependencies

Internal sub-modules inside churchvault-hq

The main finance plugin ships twelve internal modules, each owning a clear domain and independently loadable:

ModulePurpose
churchvault-partnersReferral partner programme · 30% commission · 1st & 15th payouts · per-row rate overrides
churchvault-trials14-day Premium Pro free trial · auto-downgrade to Starter on expiry · data preserved
churchvault-renewalsSubscription renewal system · 7/3/1-day email reminders · 7-day grace · suspend-on-failure
churchvault-signup-systemPaystack inline checkout · webhook handler · church provisioning pipeline
modules/budget-planningBudget Planning v4.0 · 6 sub-tools (see below)
modules/tax-calculator8-country tax engine · Nigeria, UK, USA, South Africa, Ghana, Kenya + more
modules/bank-importCSV/Excel bank statement import · auto column mapper · 4-step wizard
modules/csv-importBulk transaction import from spreadsheet
modules/volunteer-formFinance volunteer data-entry shortcode · income/expense tabs
churchvault-demo-seederDemo data generator for sales demos
churchvault-healthTable diagnostics · schema version checks · orphan-record audit
churchvault-showcasePublic church directory · SEO-friendly slugs · denomination filtering
The standout

Multi-tenancy lives in the WHERE clause.

The hardest part of multi-tenant SaaS isn't the schema. It's discipline — every single query that touches financial data must be filtered by which church is allowed to see it. One missed WHERE clause anywhere in the codebase, and a Lagos church's deposits show up on a Calabar branch's report. That's not a bug. That's an incident.

SeeMyChurch enforces this discipline by building tenancy into the query layer itself, not the application code. A small helper, cv_get_user_church_ids($user_id), returns the array of church IDs the requesting user is allowed to read — one ID for branch managers, many for HQ admins, every ID for super admins. Every financial query then filters against that array using a prepared IN clause:

churchvault-hq · payments listing PHP
// Resolve the church IDs this user is allowed to read.
// HQ admin → all branches; branch manager → just one.
$church_ids = cv_get_user_church_ids( get_current_user_id() );
if ( empty( $church_ids ) ) {
  return []; // no access, no rows
}

// Build a safe placeholder list for the IN clause.
$placeholders = implode( ',', array_fill( 0, count( $church_ids ), '%d' ) );

$rows = $wpdb->get_results(
  $wpdb->prepare(
    "SELECT * FROM {$wpdb->prefix}cv_payments
       WHERE church_id IN ($placeholders)
       ORDER BY paid_at DESC",
    ...$church_ids
  )
);

The pattern is repeated across every read in the platform. cv_deposits, cv_payments, cv_subscriptions, cv_budgets, cv_tax_records — all gated by the same helper, all using $wpdb->prepare(), all passing church IDs through prepared statements rather than string-concatenated SQL. A non-admin user cannot return another church's data. The database is what enforces that, not the controller.

Multi-tenancy isn't a feature you build. It's a discipline applied to every query. — design principle, SeeMyChurch

HQ admins get aggregation, not magic

The same helper that returns one church ID for branch managers returns many for HQ admins — every branch in their network, computed via parent_hq_id walks on the cv_churches table. The query layer doesn't know or care; it just receives an array. Aggregated reports across an entire denomination network use exactly the same code path as a single branch's report. One pattern. Zero special cases.

Multi-currency accounting

Three currencies, never mixed.

A Lagos church might hold a Naira account at GTBank, a Dollar account for missionary support, and a Pounds account for diaspora partners. Most accounting tools either force a single base currency (and convert everything to it daily, generating phantom FX swings) or pretend the multi-currency case doesn't exist.

SeeMyChurch took a third path. Each bank account stores its own account_currency and account_symbol at row level. The dashboard never converts. Reports aggregate per currency, presenting three independent totals side by side rather than one fictitious blended number:

cv_accounts schema · currency at row level SQL
CREATE TABLE wp_cv_accounts (
  account_id      INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  church_id       INT UNSIGNED NOT NULL,
  account_name    VARCHAR(120) NOT NULL,
  account_currency CHAR(3)  NOT NULL DEFAULT 'NGN',
  account_symbol   VARCHAR(8) NOT NULL DEFAULT '₦',
  opening_balance  DECIMAL(14,2) NOT NULL DEFAULT 0,
  INDEX (church_id, account_currency)
);

-- Aggregated report stays per-currency. No blending.
SELECT a.account_currency, a.account_symbol,
       SUM(p.amount) AS total
  FROM wp_cv_payments p
  JOIN wp_cv_accounts a ON a.account_id = p.account_id
 WHERE a.church_id IN (...)
 GROUP BY a.account_currency, a.account_symbol;

The dashboard auto-displays the correct symbol per account — a ₦ next to Naira balances, a $ next to USD, a £ next to GBP. Tax calculators read the currency of each transaction and apply the right country's rules to the right rows. A Nigerian payroll line gets PAYE; a UK Gift Aid donation gets the UK calculation. No conversion tables, no FX feeds, no phantom volatility. Three currencies, three totals, side by side.

Pastor OS Discovery Engine

Beyond finance — a ministry operating system.

As ChurchVault matured, a second product opportunity became clear. The same WordPress install already hosted church profiles and payment data. The question was whether the platform could become a discovery layer for the broader African church ecosystem — not just a finance back-office tool, but the place people go to find pastors, sermons, and gospel music.

Pastor OS Discovery Engine was built as a standalone plugin (pastor-os-discovery-engine, v0.6.6) that sits alongside ChurchVault on the same installation. It adds a public-facing layer while ChurchVault remains the authenticated finance engine. The two products share Paystack billing infrastructure but own entirely separate namespaces, post types, and routes.

Dir
Pastor Directory · /pastors/
Public profiles for Nigerian and African pastors. Claim and verification system. Admins assign profile ownership; false claims blocked at the application layer.
Med
Sermon Archive · /sermons/
YouTube/RSS auto-sync. Channel ID resolver with health scoring. Source Manager admin — flags missing channels, bad handles, empty profiles. Bulk sync available.
Mus
Gospel Music · /gospel-music/
Gospel artist profiles and media archive. Same YouTube sync infrastructure as sermon archive. Topic and region taxonomies shared across all CPTs.
Live
Live Now · /live-now/
Live stream listing and watch pages. Separate CPT with dedicated video watch template. Custom rewrite rules for clean URLs.
PV
PastorVault · /pastor-vault/
Resource library tied to pastor profiles. Sermons, teachings, notes. Users access their own vault via /pastor-os-vault/ after login.
Ads
Sponsored Ad Rail
Ministry placement ads on content and home pages. 7/15/30/90-day placement tiers. Context-aware: desktop rail, mobile in-feed. Self-serve via /advertise-on-pastor-os/ request page.

Engagement system

Likes, comments, and reactions are handled by a custom engagement module — no plugin dependency. Comments store against a context key (post ID or URL), are approval-gated by admins via a comments manager page, and never touch WordPress's native comment system. Engagement counts are keyed per-object and stored separately from post meta to avoid bloat.

Reading plans and universal search

Reading plans live at /pastor-os-reading-plans/. Universal search is a custom route at /pastor-os-search/ that searches across all registered Pastor OS CPTs — pastors, gospel artists, sermons, vault items — in a single query. Neither uses a third-party search library.

AI Vault + Bible Engine

Sermon builder, word study, credit-gated AI.

The AI Vault module adds three credit-gated AI tools to the platform: an AI sermon builder, a Bible word study tool, and a Greek/Hebrew study assistant. Pastor OS itself remains free; AI tools require credits. The credit system is admin-configurable — monthly limits per plan, manual credit grants for testing or support, full usage logs per user.

The AI backend is provider-agnostic: an OpenAI-compatible API endpoint is stored in admin settings, and the module works with OpenAI, DeepSeek, OpenRouter, or any compatible provider. When no API key is configured, the module falls back to local structured drafts — useful for development and testing without incurring API costs.

AI Sermon Builder · /pastor-os-ai/
Topic, scripture reference, audience, and notes. Builds a structured sermon draft via OpenAI-compatible API. Credit cost configurable per tool.
📖
AI Bible Word Study
Accepts any biblical word or phrase. Returns contextual study notes, usage across scripture, and original language connections. Credit-gated.
📚
Free Bible Engine · /bible/
Full KJV Bible reader with book/chapter/verse routing. Word search across all verses. Local storage table — no external Bible API. CSV importer for additional translations. Fully free, no login required.

The Bible engine is architecturally separate from the AI Vault: it's entirely free, requires no credits, and works without a login. The local Bible verse table was seeded from public-domain CSVs via an admin importer, meaning the platform has zero dependency on external Bible APIs and works even on restricted hosting environments. Bible search and verse lookup are pure MySQL queries against the local table.

Budget Planning v4.0 + Tax Calculator

Six planning tools. Eight countries.

Budget Planning v4.0 ships as a multi-tab module inside ChurchVault. Rather than one monolithic budget page, it is split into six focused tools — each useful independently, each drawing on the same underlying transaction data:

ToolWhat it does
Annual BudgetSet monthly income, expense, and savings targets by category. KPI cards show budget vs actuals live.
ChartsMonthly net chart, expense breakdown pie, income sources breakdown. Pure PHP/JS — no charting library.
Budget vs ActualSide-by-side comparison of planned vs recorded figures. Highlights over/underspend per category.
Giving ForecastProjects the next 6 months of income based on the trailing 12 months of recorded deposits.
Building Fund TrackerTracks a church's progress toward a named building project goal. Progress ring, current contributions, gap.
Ministry ROICalculates cost-per-impact for each ministry department. Premium Pro only.

Tax Calculator — 8 countries

The Tax Calculator module covers church-specific tax situations across eight African and diaspora countries. Each country's framework is hardcoded in a rate table — no third-party tax API, no annual subscription. When FIRS updates Nigeria's VAT rate, one array value changes. Works offline, deploys in seconds.

CountryTax types covered
NigeriaPAYE (progressive), WHT 5%, VAT 7.5% — CITA s.23 CIT exemption noted
United KingdomPAYE, Employer NI 13.8%, VAT 20%, Gift Aid (charity tax relief)
United StatesFICA Social Security 6.2%, Medicare 1.45%, FUTA/SUTA — 501(c)(3) exemption noted
South AfricaPAYE, UIF 1%, VAT 15% — PBO SARS exemption noted
GhanaPAYE 25%, SSNIT 13%, VAT 15%, NHIL 2.5%
KenyaPAYE 25%, NHIF, NSSF 6%, VAT 16%
+ 2 moreAdditional African countries included in the framework

Bank Statement Import

Churches manually recording transactions one-by-one was the most common friction point in onboarding. The Bank Import module solves it with a 4-step wizard: upload a CSV or Excel export from any Nigerian bank, the module auto-detects headers (date, debit, credit, description, reference), prompts for manual column mapping where auto-detection fails, previews the mapped rows, and bulk-inserts them into the correct currency account. One import can move months of transactions in minutes.

Free trials + subscription lifecycle

14-day trial. Seven-day grace. Zero silent terminations.

The trials system gives new signups 14 days of full Premium Pro access — the highest plan — before asking for payment. On expiry, the account auto-downgrades to the Starter (free) plan: data is fully preserved, and an upgrade banner prompts the user to subscribe. Existing paying customers are protected by a launch date guard — accounts created before 2 May 2026 are not touched by the trial logic.

The renewal system runs parallel to trials and governs paying customers. A WP-Cron job fires daily, walks every active subscription, and fires reminder emails at 7, 3, and 1 day before expiry. After expiry, a 7-day grace period holds the account active before suspension. Failed Paystack charges get three retry attempts spread across that grace window. No silent terminations. No surprise reactivations. Every state transition writes a log entry.

Above the renewal system sits the same kill switch from the original architecture — a single Admin Tools toggle that pauses the entire renewal cron globally. If something looks wrong with billing at any level, one click halts every recurring charge across the platform.

Payments & auto-renewal

Recurring billing with a kill switch.

The signup flow is deliberately quiet. A visitor lands on /pricing/, picks a plan, fills their church details on /signup/, and Paystack's inline checkout pops over the page — no redirect to an external domain, no broken back-button experience. On success, a webhook lands, a cv_subscriptions row is written with the Paystack subscription_code for future recurring charges, the church record is provisioned with status pending_setup, and an email pings me to do manual onboarding before flipping the church to active.

Subscription tiers

Tier Monthly Annual
Essential · single church ₦9,900 ₦99,000
Professional · multi-branch ₦19,900 ₦199,000
Premium Pro · denomination scale ₦39,900 ₦399,000

Auto-renewal cron, with retry logic and a kill switch

Recurring billing is the part of any SaaS most likely to silently misbehave. SeeMyChurch runs a dedicated WP-Cron job, cv_auto_renewal_cron, that fires once per day. It walks every active subscription, identifies those whose next_billing_date falls today, and charges them via Paystack's subscription codes.

The retry path matters as much as the happy path. A failed charge — declined card, network blip, Paystack outage — triggers up to three retry attempts spread across seven days. After the third failure, the subscription is marked suspended, the church status flips, and the customer receives a notification email with a one-click reactivation link. No silent terminations. No surprise reactivations.

Above all of that sits a kill switch: a single Admin Tools toggle that pauses the entire renewal cron globally. If something looks off — duplicate charges, webhook flapping, an API change — one click halts every recurring charge across the platform until I've diagnosed it. This is the single most important administrative control on the system, and it's saved at least one Saturday already.

The partner programme

Custom commissions, lifetime attribution.

The Partners plugin powers SeeMyChurch's growth strategy: regional church consultants, denomination-level liaison officers, and commercial referrers who introduce new churches to the platform. Two architectural decisions made it work.

Per-partner commission rate overrides

The default partner commission is 30% of every subscription payment, paid for the lifetime of the referred subscription. But strategic partners — those bringing entire denomination networks rather than individual churches — negotiate higher rates. Hardcoding tiers wouldn't scale. So commission rate lives on the partner row itself:

cv_partners · per-row override SQL
ALTER TABLE wp_cv_partners
  ADD COLUMN commission_rate DECIMAL(5,2)
  NOT NULL DEFAULT 30.00;

-- A single UPDATE activates a custom rate.
-- The first strategic partner: 35% under bilingual agreement.
UPDATE wp_cv_partners
   SET commission_rate = 35.00
 WHERE partner_id = 7;

Every payout calculation reads commission_rate from the partner row at the moment of payout — never hardcoded, never inherited from a tier table. The first strategic partnership signed was activated this way, formalised in a 17-page bilingual agreement at a 35% rate, with a single SQL update flipping the bit.

Lifetime attribution that survives partnership termination

This was the harder design decision. A partner refers a church. Years later, the partnership terminates — for whatever reason. Should the referred church stop generating commissions? Almost every affiliate platform says yes. SeeMyChurch deliberately says no. Once a referral is attributed, the church continues earning the original partner commissions for the lifetime of its subscription, unless fraud is proven.

Implementing this required careful handling in the renewal cron — payouts continue to terminated partners' referrals — and explicit guard logic in the dashboard so terminated partners can still see their lifetime referral revenue without being able to refer new churches. It's a small detail with disproportionate impact on partner trust: people don't sign up for an affiliate programme that can revoke their work retroactively.

Other decisions worth noting

Small choices that compound.

Pastor OS — separated from ChurchVault deliberately

A key architectural call: Pastor OS Discovery Engine is a separate plugin, not a module inside ChurchVault. This means the public discovery layer can be deactivated, updated, or replaced without touching finance logic. The two plugins share Paystack keys and billing infrastructure through option reads — not function calls across plugin boundaries. If ChurchVault is ever extracted to a different install, it runs cleanly without the discovery engine.

Credit-gated AI without a SaaS billing layer

AI credits are stored as WordPress user meta — not in a separate billing system. Monthly limits are configured per plan in admin settings. The maybe_reset_ai_credits_for_user() function checks if the current month differs from the stored reset month and resets the count if so. No cron needed, no billing webhooks, no third-party credit API. Usage logs are stored as a custom post type (pos_ai_log) which makes them auditable and searchable without a separate table.

Approval layer for branch transactions

Denominations like RCCG, Anglican, and Methodist need oversight on branch-level expenditure. SeeMyChurch's approval layer lets branch managers mark transactions pending_approval; HQ admins see a queue, approve or reject, and the transaction either commits to the ledger or is bounced back with a reason. Fully optional — independent churches turn it off. Enterprise-grade where it's needed.

Health Monitor + System Audit

Two complementary read-only diagnostics surfaces. Health Monitor shows table row counts and schema status. System Audit hunts for orphan records, balance reconciliation drift, cron health, and Paystack key validity. Both surface issues before customers notice them. Both refuse to mutate anything; they only report.

The outcome

Two products. Solo. Live with paying customers.

From initial concept in Q4 2025 to a full ministry operating system by mid-2026 — a solo build covering church finance, multi-tenancy, multi-currency accounting, recurring Paystack billing, a public discovery layer, pastor directory, gospel music, YouTube/RSS sync, AI sermon builder, a free Bible engine, budget planning, an 8-country tax calculator, bank import, partner programme, 14-day free trials, subscription renewals, a sponsored ad rail, and a platform FAQ bot. All running live on a single Hostinger Cloud plan.

20+
Custom plugins
& modules
~50k
Lines of PHP
solo-authored
8
Countries in
tax calculator
2
Full products
one install

Build timeline

  • Q4 2025 Initial concept & design
  • JAN–FEB 2026 Core finance engine, multi-tenancy, multi-currency
  • MAR 2026 Auto-renewal V1, partner programme, first denomination signup
  • APR 2026 Showcase directory, strategic partnership infrastructure
  • MAY 2026 Pastor OS Discovery Engine v0.3 — pastor directory, gospel music, sponsored ad rail
  • MAY–JUN 2026 Budget Planning v4 · Tax Calculator (8 countries) · Bank Import · Free Trials · FAQ Bot
  • JUN 2026 AI Vault (sermon builder, word study) · Free Bible Engine · Pastor OS v0.6
  • CURRENT Live · paying customers · onboarding partners · active roadmap

What's next

Active roadmap: expanding the public pastor and church directory (denomination-level filtering, verified badges), a Premium Pro tier HQ network rollout, reading plan builder for church leaders, and ongoing AI tool expansion — AI devotional writer, church letter generator, and multilingual sermon translation support.

Next case study · 03

A four-module fintech platform built for the African market — Business, Family, Personal, Schools. Two-person expense approval enforced at the database level. 8-country tax engine. Five weeks of build.