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.
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.
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.
The main finance plugin ships twelve internal modules, each owning a clear domain and independently loadable:
| Module | Purpose |
|---|---|
| churchvault-partners | Referral partner programme · 30% commission · 1st & 15th payouts · per-row rate overrides |
| churchvault-trials | 14-day Premium Pro free trial · auto-downgrade to Starter on expiry · data preserved |
| churchvault-renewals | Subscription renewal system · 7/3/1-day email reminders · 7-day grace · suspend-on-failure |
| churchvault-signup-system | Paystack inline checkout · webhook handler · church provisioning pipeline |
| modules/budget-planning | Budget Planning v4.0 · 6 sub-tools (see below) |
| modules/tax-calculator | 8-country tax engine · Nigeria, UK, USA, South Africa, Ghana, Kenya + more |
| modules/bank-import | CSV/Excel bank statement import · auto column mapper · 4-step wizard |
| modules/csv-import | Bulk transaction import from spreadsheet |
| modules/volunteer-form | Finance volunteer data-entry shortcode · income/expense tabs |
| churchvault-demo-seeder | Demo data generator for sales demos |
| churchvault-health | Table diagnostics · schema version checks · orphan-record audit |
| churchvault-showcase | Public church directory · SEO-friendly slugs · denomination filtering |
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:
// 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.
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.
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:
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.
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.
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 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.
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.
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 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:
| Tool | What it does |
|---|---|
| Annual Budget | Set monthly income, expense, and savings targets by category. KPI cards show budget vs actuals live. |
| Charts | Monthly net chart, expense breakdown pie, income sources breakdown. Pure PHP/JS — no charting library. |
| Budget vs Actual | Side-by-side comparison of planned vs recorded figures. Highlights over/underspend per category. |
| Giving Forecast | Projects the next 6 months of income based on the trailing 12 months of recorded deposits. |
| Building Fund Tracker | Tracks a church's progress toward a named building project goal. Progress ring, current contributions, gap. |
| Ministry ROI | Calculates cost-per-impact for each ministry department. Premium Pro only. |
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.
| Country | Tax types covered |
|---|---|
| Nigeria | PAYE (progressive), WHT 5%, VAT 7.5% — CITA s.23 CIT exemption noted |
| United Kingdom | PAYE, Employer NI 13.8%, VAT 20%, Gift Aid (charity tax relief) |
| United States | FICA Social Security 6.2%, Medicare 1.45%, FUTA/SUTA — 501(c)(3) exemption noted |
| South Africa | PAYE, UIF 1%, VAT 15% — PBO SARS exemption noted |
| Ghana | PAYE 25%, SSNIT 13%, VAT 15%, NHIL 2.5% |
| Kenya | PAYE 25%, NHIF, NSSF 6%, VAT 16% |
| + 2 more | Additional African countries included in the framework |
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.
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.
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.
| 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 |
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 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.
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:
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.
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.
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.
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.
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.
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.
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.
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.