Skip to content

SOGTheFirst/VeriBridge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VeriBridge Oversight Services

Commission independent, ground-level business and welfare verification from anywhere in the world — and get evidence-backed reports back within days.

VeriBridge is a full-stack oversight platform built for diaspora clients who need eyes on the ground in Africa. Whether you're a UK-based investor funding a Lagos construction project, a family member arranging welfare visits for an elderly relative, or a business owner who needs supplier legitimacy confirmed before wiring money — VeriBridge connects you with a vetted local field team, handles scheduling and reporting, and gives you a real-time oversight dashboard backed by AI-assisted analysis.

The platform covers two verticals: Business Oversight (verification visits, operational monitoring, investor-confidence reporting) and ElderCare Oversight (wellbeing visits, welfare monitoring, appointment accompaniment). Both flow through the same backend: subscription plans with feature permissions, one-off bookable orders, Stripe payments with webhook activation, and a staff assignment + reporting layer.


⚠️ About this repository

This is a curated portfolio excerpt, not the full application. VeriBridge is an active commercial product, so the complete codebase — all Blade views and CSS, admin tooling, full test suite, deployment config, credentials, and internal business logic — stays private.

What's published here is a representative cross-section of the backend: the authentication architecture, payment orchestration, middleware chain, key models and services, route definitions, and the database schema history. Files in this repo reference controllers, models, and helpers (the temp() view helper, main() settings helper, the full bootstrap/app.php wiring) that exist in the full application but aren't included here.

This repo isn't runnable standalone. It's here to show how the system is structured, the decisions behind it, and what the code actually looks like.


Screenshots

Homepage Packages
Homepage Packages
Pricing Dashboard
Pricing Dashboard
Bridge AI Contact
Bridge AI Contact

The problem it solves

Investing money or placing trust in a business or caregiver you can't physically visit is a high-stakes leap of faith. Bank transfers are sent to contractors who may not exist. Elderly relatives are left with carers whose visits are never actually confirmed. Supplier relationships are built on WhatsApp profiles and nothing more.

VeriBridge closes that gap with human verification: a local field agent visits the location, documents what they find with photos and video, files a structured report, and delivers it to the client's dashboard — with an AI analysis layer (Bridge AI) that cross-references findings against stated claims and flags anomalies.


Core features

For clients (diaspora users)

  • Project-scoped dashboard: every active oversight project has its own data view, health score, and report history
  • Plan-gated feature access: site visit history, live oversight view, analytics dashboard, Bridge AI insights — each unlocked at a different subscription tier
  • One-off bookable visits: pay per visit, no subscription required, still get full portal access
  • KYC verification gate: optional — admin can toggle whether KYC must be approved before accessing project features
  • Stripe-powered subscription checkout and one-time order payments, with Stripe webhook activation and a reliable success-redirect fallback
  • PDF reports, evidence attachments, and real-time order status tracking
  • Two-factor authentication, single-session enforcement, and email verification

For field staff

  • Assigned order queue with client and service detail
  • Report submission with structured findings, recommendations, and evidence uploads
  • Client notification triggered automatically when a report is submitted or approved

For admin (control panel)

  • Full user, subscription, and order management
  • Manual plan assignment, subscription extension, cancellation, and reactivation
  • Subscription plan builder with JSON permission keys that gate individual portal features
  • Email template management: slug-based, shortcode-driven, DB-overridable with hardcoded fallbacks

Tech stack

Layer Technology
Backend PHP 8.2, Laravel 11
Authentication Multi-guard (user / staff / control), custom middleware, Laravel Sanctum-style session guards
Database MySQL, Eloquent ORM
Payments Stripe Checkout (subscriptions + one-time), webhooks with signature verification
Email Laravel Mail, DynamicMail mailable, slug-based EmailTemplate model with DB + hardcoded fallbacks
Push notifications Firebase Cloud Messaging via PushNotificationService
Two-factor auth TOTP + recovery codes via TwoFactorAuthService, challenge middleware
Frontend Blade templating, custom CSS design system (no Tailwind), vanilla JavaScript
Languages PHP 8.2, Blade, JavaScript
File storage Laravel Storage (local/S3 compatible)
Queue Laravel Queues (database driver)

Architecture

Multi-guard authentication

Laravel 11 ships without Http/Kernel.php. All guards, middleware aliases, and route group wiring live in a single bootstrap/app.php. Three completely isolated portals share the same codebase without touching each other's session or middleware:

  Client browser         Staff browser          Admin browser
       │                      │                      │
  /user/*                /staff/*              /control/*
       │                      │                      │
  guard:user            guard:staff           guard:control
  check.email           middleware:staff      middleware:control
  single.session:user   middleware:active     middleware:active
  check.kyc
  check.plan:feature
       │                      │                      │
       └──────────────────────┴──────────────────────┘
                              │
                    bootstrap/app.php
                    (one place — all wiring)
                              │
                    ┌─────────┴─────────┐
                    │    Controllers    │
                    │  User\  Staff\    │
                    │  Control\         │
                    └─────────┬─────────┘

Each guard has its own controller namespace, route file, URL prefix, and named-route prefix (user.*, staff.*, control.*). No guard can accidentally resolve another guard's authenticated user because every middleware explicitly names its guard: auth('user')->user(), Auth::guard('staff')->check(), etc.

Subscription and payment lifecycle

Every Stripe payment — whether a recurring subscription or a one-time booking — follows the same two-path activation design to handle the inherent race between the Stripe-hosted checkout page and the webhook:

  Client selects plan / order
         │
  Controller creates DB record (status: PENDING / payment_status: UNPAID)
         │
  Stripe Checkout session created, stripe_session_id saved to record
         │
  Client redirected to Stripe ──────────────────────────────────────┐
         │                                                           │
         │ (payment succeeds)                                        │
         │                                                           │
  Path A: Stripe redirect to success_url                   Path B: Stripe webhook
  (immediate, always fires)                                (may be delayed or blocked
         │                                                  on localhost)
         │                                                           │
  success() / paymentSuccess()                    handleCheckoutCompleted()
  – find record by stripe_session_id              – find record by stripe_session_id
  – fallback: Stripe API retrieve + metadata      – atomic update (WHERE status=PENDING)
  – atomic status update (WHERE PENDING)          – create Payment + Invoice records
  – create Payment + Invoice records              – activate project
    (idempotency: check transaction_ref first)    – send activation email
  – activate project
  – send activation email
         │                                                           │
         └──────────────────────────┬────────────────────────────────┘
                                    │
                             Both paths safe to run in any order.
                             Idempotency guards prevent double records.

See StripeWebhookController.php and SubscriptionController.php.

Oversight health score

Each project has a project_data table of key–value data points (ProjectData records) with a status field: excellent, good, average, issue, confusing, critical. The dashboard aggregates these into a 0–100 health score (weighted average of per-status scores), which drives the donut chart and per-metric progress bars. No data → score is 0, not a fake default. See MainController.php.


Design decisions worth highlighting

Feature gating is data-driven, not code-driven. Every subscription plan stores a JSON features column in the shape {"permissions": ["site-visits", "analytics", "bridge-ai"], "items": ["display label 1", ...]}. The check.plan:bridge-ai middleware reads the permissions array at request time — adding a new gated feature to a plan is a database update, not a deployment. See CheckPlanPermission.php.

The success-redirect is the primary activation path, not the webhook. On localhost (during development) Stripe webhooks never arrive. Rather than require Stripe CLI forwarding for basic testing, the success() and paymentSuccess() handlers are fully self-contained: they try the stripe_session_id DB lookup, fall back to a Stripe API session retrieve using the URL parameter, use session metadata to locate the correct DB record, and create Payment + Invoice records with a transaction_ref idempotency guard. The webhook runs the same logic with an affected === 0 early-return so whichever path wins, the other is a no-op.

One main() helper, one settings row, cached. All site-wide settings (contact email, phone, social links, feature toggles like kyc_verification and two_factor_auth) live in a single settings table row fetched once per process via Cache::remember('site_settings', 300, ...). The main($key) global helper reads from that cache. Changing a setting takes effect within five minutes without touching code or config files.

Email templates are DB-first with hardcoded fallbacks. EmailTemplate::getBySlug() checks the database first (so non-technical admins can edit copy from the control panel), then falls back to a $defaults array in the model for slugs that haven't been customised yet. This means new slugs work immediately on deploy without a DB record, and the fallback is never null. See EmailTemplate.php and EmailTemplateService.php.

Report emails fire from a model observer, not a controller. VerificationReport::booted() registers an updated observer that watches for status changing to SUBMITTED or APPROVED and emails the client. This means the notification fires regardless of which controller or admin tool changes the status — one place, no duplication. See VerificationReport.php.

KYC is an opt-in gate, not always-on. The check.kyc middleware reads main('kyc_verification') — a boolean toggle in the settings row. When KYC enforcement is off (default), the middleware is a no-op. When on, unapproved users are redirected to the KYC flow but can still access their profile, KYC submission, and dashboard. This prevents a misconfigured toggle from locking users out of their accounts entirely. See CheckKYC.php.

Single-session enforcement is guard-aware. SingleSession middleware stores a session token in the DB (users.session_id) and compares it against the current session on every authenticated request. If another device logs in, the old session is invalidated immediately on the next request — no polling, no cron, no WebSockets. The guard name is passed as a parameter so the same middleware works for user, staff, and control guards independently.

Slug-based routing with optional {uuid?}. The entire user portal is prefixed with project/{uuid?}. If a UUID is present, the Guard middleware switches the authenticated user's active project to the one referenced. If absent, it resolves to the current active project. This means a user can deep-link directly to a specific project's dashboard or orders from an email, and the URL is also shareable between browser tabs.


Database schema

The full application has 32 database tables across three verticals (Business Oversight, ElderCare Oversight, and platform administration). The database/migrations folder contains the major structural migrations; additive/incremental migrations are omitted from this excerpt. Key tables:

Table Purpose
users Client accounts — active_project_id FK, preferences JSON, session_id for single-session enforcement
projects Client oversight projects — uuid, slug, status (DRAFT → ACTIVE), service_id
project_data Key–value data points per project — variable_key, value_text, value_json, data_type, status, sort_order
services Service catalogue — service_type (ONE_OFF / SUBSCRIPTION), base_price
service_categories Category groupings — slug used for plan filtering (e.g. eldercare-oversight)
subscription_plans Plan definitions — features JSON with permissions + items arrays, price_gbp, billing_cycle
subscriptions Client subscriptions — status enum (PENDING / ACTIVE / SUSPENDED / CANCELLED), stripe_session_id, stripe_subscription_id
verification_orders One-off bookings — payment_status (UNPAID / PAID), status (PENDING → IN_PROGRESS → COMPLETED), stripe_session_id
verification_reports Field team reports — findings, recommendations, status (DRAFT → SUBMITTED → APPROVED), evidence attachments
payments Append-only payment ledger — transaction_ref for idempotency, gateway_reference
invoices Invoice records — linked to subscription or order
kyc_submissions KYC document submissions — status (PENDING / APPROVED / REJECTED)
email_templates Overridable email copy — slug, subject, body with {{shortcode}} placeholders
settings Single-row site configuration — all toggles and contact details
staff Staff member accounts (separate from users)
controls Admin accounts (separate guard from both users and staff)

Repository structure

├── artisan                                       # Laravel CLI entry point
├── composer.json                                 # dependencies (laravel/framework 11, stripe/stripe-php, …)
├── .env.example                                  # environment variable template
│
├── bootstrap/
│   └── app.php                                   # single-file wiring: guards, routes, middleware aliases (Laravel 11)
│
├── config/
│   └── auth.php                                  # three-guard definition: user / staff / control
│
├── public/
│   └── index.php                                 # HTTP entry point — boots the application
│
├── resources/
│   └── views/
│       ├── layout/
│       │   └── app.blade.php                     # public site layout (home, pricing, contact)
│       └── main/
│           ├── layout/
│           │   └── master.blade.php              # authenticated portal layout (sidebar, header, notifications)
│           └── user/
│               └── dashboard.blade.php           # client dashboard — health score, KPI row, activity feed
│
├── app/
│   ├── Http/
│   │   ├── Controllers/
│   │   │   ├── Ipn/
│   │   │   │   └── StripeWebhookController.php      # checkout.completed, invoice.paid, subscription events
│   │   │   ├── User/
│   │   │   │   ├── SubscriptionController.php        # plan checkout, success redirect, cancel
│   │   │   │   ├── VerificationOrderController.php   # one-off order booking + payment
│   │   │   │   ├── ProjectController.php             # project CRUD, active project switching
│   │   │   │   └── MainController.php                # dashboard, oversight health score
│   │   │   ├── Staff/
│   │   │   │   └── StaffController.php               # assigned orders, report submission
│   │   │   └── Control/
│   │   │       ├── ManagementController.php          # admin list views (users, orders, invoices)
│   │   │       └── SubscriptionManagementController.php  # plan assign, extend, cancel, reactivate
│   │   └── Middleware/
│   │       ├── Guard.php                             # multi-guard auth + active project resolution
│   │       ├── CheckPlanPermission.php               # feature gate: plan.features.permissions
│   │       ├── CheckKYC.php                          # optional KYC gate, toggled via settings
│   │       ├── RequireTwoFactor.php                  # TOTP challenge enforcement
│   │       ├── SingleSession.php                     # one-device-at-a-time enforcement
│   │       └── CheckEmailVerification.php            # blocks unverified accounts
│   ├── Models/
│   │   ├── User.php                                  # portal access, planHasFeature(), relationships
│   │   ├── ClientSubscription.php                    # subscription lifecycle + Stripe fields
│   │   ├── VerificationOrder.php                     # order lifecycle, payment relationship
│   │   ├── VerificationReport.php                    # booted() observer → email on status change
│   │   ├── Project.php                               # HasMany customStats, reports, orders
│   │   ├── SubscriptionPlan.php                      # features JSON, billing cycle
│   │   ├── Payment.php                               # transaction_ref idempotency key
│   │   ├── Invoice.php                               # billing invoice records
│   │   └── EmailTemplate.php                         # getBySlug(), $defaults fallback
│   └── Services/
│       ├── EmailTemplateService.php                  # send() via slug + shortcode substitution
│       ├── TwoFactorAuthService.php                  # TOTP generation, verification, recovery codes
│       └── PushNotificationService.php               # FCM push notifications
│
├── database/
│   ├── migrations/                                   # 9 core structural migrations (32 tables total)
│   └── seeders/
│       ├── VeriBridgeServicesSeeder.php              # business oversight plans with permission keys
│       └── ElderCareSeeder.php                       # eldercare plans (Silver / Gold / Platinum)
│
├── routes/
│   ├── user.php                                      # client portal route tree (plan-gated)
│   ├── staff.php                                     # staff portal routes
│   └── control.php                                   # admin panel routes
│
└── screenshots/                                      # portfolio screenshots

Getting oriented

This repo isn't runnable standalone — views, config, the bootstrap/app.php guard wiring, helpers, and several referenced models aren't included. The best way to read it:

  1. Start with routes/user.php to understand the full portal surface: what's public, what requires authentication, what's gated by plan permission, and how project/{uuid?} scoping works.

  2. Read Guard.php next. It resolves which project is active, switches it when the UUID in the URL changes, and redirects new users to project creation. Everything in the authenticated portal flows through it.

  3. Follow a subscription payment: SubscriptionController::checkout() → Stripe hosted page → SubscriptionController::success() (success redirect path) and StripeWebhookController::handleSubscriptionCheckoutCompleted() (webhook path). Both activate the same DB record, both create Payment and Invoice records, both send an email — with idempotency guards so only one actually writes.

  4. Look at CheckPlanPermission.php alongside VeriBridgeServicesSeeder.php to see how features.permissions on a plan maps to middleware keys on routes.

  5. Open VerificationReport.php for the model observer pattern: booted() registers an updated hook that emails the client when a report status changes — no controller coupling.

  6. Read EmailTemplate.php and EmailTemplateService.php together to see the DB-first / hardcoded-fallback email system.


Service categories and plan permission keys

Business Oversight (monthly-business-plans / one-time-verification)

Plan Price Permission keys unlocked
Business Verify £79/mo site-visits
Business Monitoring £199/mo site-visits, analytics, oversight
Business Oversight Premium £499/mo site-visits, analytics, oversight, bridge-ai, multi-project
Investor Confidence £750+/mo + priority-support
Business Verification Visit £149 one-off portal access (no plan features)
Supplier/Partner Verification £199 one-off portal access (no plan features)

ElderCare Oversight (eldercare-oversight)

Plan Price Permission keys unlocked
Standard Wellbeing Visit £75 one-off portal access
Silver Package £199/mo wellbeing-visits, family-reports, welfare-monitoring
Gold Package £399/mo + appointment-support, priority-comms
Platinum Package Custom + dedicated-liaison (no Stripe checkout — contact form)

License

The code in this repository is shared for portfolio and reference purposes under the MIT License. This is a curated excerpt of a larger closed-source production application; the licence covers what's shown here, not the complete VeriBridge platform.


Built by THE SOGGitHub

About

VeriBridge gives diaspora clients worldwide professional, verified oversight of their businesses, properties, and projects. From Lagos to Nairobi, Accra to Karachi. No more stories. Just proof.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors