Fundraising plugin
plugins/Fundraising is a tenant-owned business plugin for projects, donation channels, manual donation submissions, private proofs, review decisions, supporters, and reports.
Package structure
plugins/Fundraising/
├── FundraisingPlugin.php
├── FundraisingSeeder.php
├── plugin.json
├── Enums/{ProjectStatus,ProjectUpdateStatus,ProjectUpdateType,DonationStatus}.php
├── Models/{Project,ProjectUpdate,ProjectMedia,DonationOption,Donation,DonationChannel}.php
├── Policies/{FundraisingPolicy,ProjectPolicy,ProjectUpdatePolicy,DonationPolicy,DonationChannelPolicy}.php
├── Http/Controllers/FundraisingController.php
├── Http/Controllers/Admin/{Overview,Project,Donation}Controller.php
├── Http/Controllers/Dashboard/{Overview,Project,ProjectUpdate,Donation,DonationChannel,Supporter,Report}Controller.php
├── Http/Requests/{StoreProjectRequest,UpdateProjectRequest,StoreProjectUpdateRequest}.php
├── database/migrations/
├── resources/views/dashboard/{overview,projects,donations,channels,supporters,reports}/
├── resources/views/{admin,site,widgets}/
└── routes/web.php
The five package migrations create the Fundraising tables, add project images, add private proof/review fields, enforce transfer-reference uniqueness, create project updates, and add public media plus donation options. Plugin installation runs them and then invokes the local/testing-only FundraisingSeeder.
Exact PHP and lifecycle paths:
plugins/Fundraising/FundraisingPlugin.php
plugins/Fundraising/FundraisingSeeder.php
plugins/Fundraising/Enums/ProjectStatus.php
plugins/Fundraising/Enums/ProjectUpdateStatus.php
plugins/Fundraising/Enums/ProjectUpdateType.php
plugins/Fundraising/Enums/DonationStatus.php
plugins/Fundraising/Models/Project.php
plugins/Fundraising/Models/ProjectUpdate.php
plugins/Fundraising/Models/ProjectMedia.php
plugins/Fundraising/Models/DonationOption.php
plugins/Fundraising/Models/Donation.php
plugins/Fundraising/Models/DonationChannel.php
plugins/Fundraising/Policies/FundraisingPolicy.php
plugins/Fundraising/Policies/ProjectPolicy.php
plugins/Fundraising/Policies/ProjectUpdatePolicy.php
plugins/Fundraising/Policies/DonationPolicy.php
plugins/Fundraising/Policies/DonationChannelPolicy.php
plugins/Fundraising/Http/Controllers/FundraisingController.php
plugins/Fundraising/Http/Controllers/Admin/OverviewController.php
plugins/Fundraising/Http/Controllers/Admin/ProjectController.php
plugins/Fundraising/Http/Controllers/Admin/DonationController.php
plugins/Fundraising/Http/Controllers/Dashboard/OverviewController.php
plugins/Fundraising/Http/Controllers/Dashboard/ProjectController.php
plugins/Fundraising/Http/Controllers/Dashboard/ProjectUpdateController.php
plugins/Fundraising/Http/Controllers/Dashboard/DonationController.php
plugins/Fundraising/Http/Controllers/Dashboard/DonationChannelController.php
plugins/Fundraising/Http/Controllers/Dashboard/SupporterController.php
plugins/Fundraising/Http/Controllers/Dashboard/ReportController.php
plugins/Fundraising/Http/Requests/StoreProjectRequest.php
plugins/Fundraising/Http/Requests/UpdateProjectRequest.php
plugins/Fundraising/Http/Requests/StoreProjectUpdateRequest.php
plugins/Fundraising/Support/ProjectMediaManager.php
plugins/Fundraising/database/migrations/2026_08_09_010000_create_fundraising_tables.php
plugins/Fundraising/database/migrations/2026_08_09_020000_add_image_to_fundraising_projects.php
plugins/Fundraising/database/migrations/2026_08_09_030000_add_review_fields_to_fundraising_donations.php
plugins/Fundraising/database/migrations/2026_08_10_000100_harden_donations_and_create_project_updates.php
plugins/Fundraising/database/migrations/2026_08_10_000200_add_project_media_and_donation_options.php
plugins/Fundraising/routes/web.php
Data ownership
fundraising_projects: tenant project content, image path, goal/currency, featured flag, publication/end timestamps, and status.fundraising_project_updates: progress, milestone, financial, and impact entries with optional image, amount spent, beneficiary count, and publication state.fundraising_project_media: public project-gallery and update attachments, including images, videos, and downloadable documents.fundraising_project_donation_options: up to three named impact amounts per project, preserved as inactive records when removed so historical donations retain context.fundraising_donation_channels: tenant transfer account/wallet instructions and active state.fundraising_donations: tenant donation, project/channel FKs, optional global donor FK, normalized phone, amount/reference, private proof path, status, reviewer, timestamps, and notes.
All tenant-owned Fundraising models use BelongsToOrganization; ownership is injected from CurrentOrganization, guarded against reassignment, and filtered by the tenant global scope. Donor is intentionally global and optional.
Project and update images and donation proofs intentionally use different storage boundaries. Featured project images are public files under fundraising/projects, update cover images are under fundraising/updates, and gallery/update attachments are under fundraising/media. These use the public disk because donors can view them. Proofs remain private files on local.
Manifest and public surfaces
Fundraising requires slots.home_after_hero.v1, plugin_pages.v1, and site_navigation.v1. It contributes:
- navigable full-width
projects; - non-navigable full-width
projects/{project}; - featured published projects in
home_after_heroat priority20; - public
POST /donate/{organizationSlug}/projects/{project}with CSRF andthrottle:20,1.
Settings are heading (required, normalized, maximum 80 characters) and featured_limit (integer 1–6). Public GET pages resolve through the site catch-all. Preview can show non-published projects, but the donation POST independently requires published.
Organization dashboard
The dashboard prefix is:
/charity/{organization}/plugins/fundraising/manage
The plugin contributes these submenu destinations:
plugin.fundraising.dashboard.overview: summary metrics and recent activity.plugin.fundraising.dashboard.projects.index: project list. Create, show, edit, status, and delete routes are contextual actions and are not sidebar items.plugin.fundraising.dashboard.donations.index: all tenant donations.plugin.fundraising.dashboard.donations.review: pending review queue.plugin.fundraising.dashboard.channels.index: transfer accounts and wallets.plugin.fundraising.dashboard.supporters.index: donors who interacted with the current organization only.plugin.fundraising.dashboard.reports.index: twelve-month and per-project performance.
Plugin display settings remain at /charity/{organization}/plugins/fundraising; management routes must use /manage to avoid colliding with that settings URL.
FundraisingPlugin::dashboards() contributes only these seven top-level destinations. Create, show, edit, status, proof, and delete routes are contextual and must not appear in the sidebar. PluginDashboardResolver::groupsForOrganization() renders them beneath one “المشاريع والتبرعات” submenu.
Super Admin oversight
Fundraising implements ContributesSuperAdminPages and ContributesSuperAdminWidgets. The global widget summarizes verified value/count, pending count, project count, and organizations with projects. Its submenu contains:
plugin.fundraising.admin.overview: platform metrics, recent donations, and leading organizations.plugin.fundraising.admin.projects.index: filtered projects across all organizations.plugin.fundraising.admin.donations.index: filtered donations across all organizations.
Routes live under /admin/plugins/fundraising/manage with auth, super.admin, and installed-mode availability. They are read-only: organization staff retain responsibility for project mutations, donation decisions, and proof access. Every global row identifies its organization.
Project workflow
ProjectStatus supports draft, published, paused, completed, and archived. Only published accepts new donations. published and completed remain visible publicly; paused, draft, and archived projects are excluded from published resolution.
Every project is tenant-scoped with BelongsToOrganization. Verified progress is calculated only from donations whose status is verified.
Published resolution also requires non-null published_at <= now(). First publication sets this timestamp; later status changes preserve it. Project validation requires a tenant-unique kebab-case slug, title up to 160, summary up to 500, body up to 20,000, goal of at least 1,000, optional non-past end date, and an optional JPG/PNG/WebP image up to 5 MB. Currency is currently forced to SDG.
Project body editing uses Quill 2 through the Vite bundle. Its semantic HTML is allow-list sanitized on write and read before public rendering. Published projects stop accepting donations automatically after ends_at. Deleting a project that has donations archives it instead, preserving its financial records; only projects without donations are permanently deleted.
Project updates support draft/published state and four types: progress, milestone, financial, and impact. Published entries appear as a public timeline and can optionally disclose an image, amount spent, and beneficiary count.
The project editor provides a drag-and-drop featured-image picker with immediate preview and a multiple-image gallery picker. Update entries accept up to ten image, MP4/WebM/MOV, PDF, Office-document, text, or ZIP attachments. Public project pages render images and videos inline and expose documents as named download links.
Each project may define up to three active donation options containing a label, exact amount, and short impact description. The public form also keeps a custom-amount path. When a named option is selected, the server ignores a tampered client amount and persists the option's configured amount and ID on the donation.
Donation workflow
DonationStatus supports pending, verified, and rejected.
Public submissions require a donor name, normalized phone, amount, active channel, transfer reference, and proof file. Transfer references are unique per organization and channel at validation and database levels. Proofs are stored on the private local disk under fundraising/proofs; they are never exposed through a public storage URL. The authenticated proof download route enforces organization membership and the donation policy.
Organization roles
owner: full project, update, donation-review, and channel mutation access.editor: creates and edits projects and project updates, but cannot approve or reject donations.finance: manages transfer channels and reviews donations, but cannot mutate project content.reviewer: approves or rejects donations, but cannot mutate project content or transfer channels.
All organization members may read the package dashboards. Mutation controls are hidden in Blade and independently enforced by model policies.
The minimum amount is 100. Accepted proof types are JPG, PNG, WebP, and PDF with a 5 MB maximum. New records default to pending. If donation persistence fails after storing a proof, the controller removes the orphan file. A submission links an existing donor only when its normalized phone is already verified; later retroactive claiming is not implemented.
Reviewing a donation records:
statusreviewed_by_user_idreviewed_atreview_notesverified_atfor verified donations
Only verified donations count toward project progress, metrics, supporters' verified totals, and reports. A rejected decision clears verified_at. Channels linked to donations cannot be deleted; administrators disable them instead.
Review actions accept only verified or rejected; they do not return a record to pending. Donation listing supports status, project, and donor-name/phone/reference search and paginates 20 rows.
Privacy
The supporters page groups records from the current organization's donations. A charity cannot query a donor's activity at another organization. A verified global donor profile is shown only as an identity indicator; unrelated donation history remains inaccessible.
Dashboard widgets
FundraisingPlugin implements both organization and Super Admin widget contracts. Organization widget fundraising-overview has order 10 and shows verified totals, pending totals, the count of publicly visible published/completed projects, a six-month verified trend, and the top three public projects by verified amount. Its queries execute inside the resolved tenant context. Super Admin widget fundraising-platform-overview is global and runs only after EnsureSuperAdmin establishes bypass.
The package report page shows twelve-month verified totals, average donation, verification rate, and per-project performance. Reports are currently on-screen aggregates; CSV/PDF export is not implemented.
Lifecycle
Install runs all five migrations and the local/testing-only idempotent seeder for every organization under explicit tenant context. Tenant disable and normal global uninstall preserve Fundraising rows and files. awn:plugin:uninstall fundraising --purge removes assignments and reverses package tables; back up production data before purge.
Verification
Run:
php artisan awn:plugin:install fundraising
php artisan awn:extensions:validate
php artisan test --filter=FundraisingDashboardTest