A donation platform built with Laravel 12 + Filament 4. This application allows users to browse donation campaigns, make contributions, and track their donation history. Features include campaign management, real-time payment processing with Midtrans, and a comprehensive admin panel.
Designed for the Indonesian market (IDR currency, Asia/Jakarta timezone).
π Learning Project: This is an experimental project built while learning fullstack Laravel development. The codebase started from a tutorial boilerplate and has been significantly modified and expanded. This is a work in progress as I continue learning!
This project was initially based on the tutorial and boilerplate from Mas Asdita (codingtengahmalam) β an Indonesian Laravel educator who provides practical tutorials for building real-world applications.
The original boilerplate provided the foundation for this project. Since then, the codebase has been heavily modified with many custom features, UI redesigns, and architectural improvements as part of my Laravel learning journey.
Browse available donation campaigns with filtering by category, search functionality, and featured campaigns highlighted.
Clean donation form allowing users to select preset amounts or enter custom amounts, leave messages, and choose anonymous donation options.
Personal dashboard showing donation statistics, recent donations with status tracking, and campaign contribution history.
- Features
- System Architecture
- Project Structure
- Prerequisites
- Getting Started
- Available Scripts
- Application Flow
- Architecture Patterns
- Testing
- Troubleshooting
- Campaign Management - Browse and search donation campaigns by category
- Donation Processing - Online donations with Midtrans payment gateway
- Real-time Payment Status - Webhook integration for automatic status updates
- User Dashboard - View donation history and manage contributions
- Filament Admin Panel - Full CRUD for campaigns, categories, donations, and users
- Role-Based Access Control - Super admin and donor roles using Spatie Permissions
- Anonymous Donations - Option to hide donor identity
- Campaign Updates - Article system for campaign progress updates
- Responsive Design - Tailwind CSS with custom components
- Authentication System - Built-in auth flow via Livewire starter stack (Volt + session auth)
- Custom Admin Login - Styled Filament login page
graph TD
subgraph Public
D[Donor] -->|Browse| F[Frontend /]
F -->|Search| CM[Campaign Model]
end
subgraph Donor_Area
D -->|Donate| DF[DonationForm]
D -->|View| DD[DonationDashboard]
end
subgraph Admin_Panel
A[Admin] -->|/admin| FP[Filament Panel]
FP --> CR[CampaignResource]
FP --> DR[DonationResource]
FP --> UR[UserResource]
end
DF --> DS[DonationService]
DS --> MS[MidtransService]
CM --> DB[(MySQL)]
MS --> MID[Midtrans API]
sequenceDiagram
participant D as Donor
participant F as Frontend
participant DS as DonationService
participant DB as MySQL
participant MID as Midtrans
D->>F: Select Campaign β Click Donate
F->>F: Enter Amount & Message
D->>F: Submit Donation
F->>DS: recordDonation()
DS->>DB: Create Donation (pending)
DS->>DB: Generate Order ID
DS->>MID: Create Snap Token
MID-->>DS: Snap Token
DS-->>F: Payment URL
F-->>D: Show Midtrans Payment UI
MID->>DS: Webhook Notification
DS->>DB: Update Status to Paid
DS->>DB: Set Paid At Timestamp
donasikita-project/
βββ app/
β βββ Filament/
β β βββ Resources/ # Admin panel resources
β β βββ Campaigns/
β β βββ Donations/
β β βββ Users/
β βββ Http/
β β βββ Controllers/
β β β βββ MidtransController.php # Payment webhook callback
β β β βββ Auth/ # Auth controllers (email verification, etc.)
β β βββ Requests/ # Form requests
β βββ Livewire/
β β βββ Campaign/ # Campaign components
β β β βββ DonationForm.php
β β β βββ ShowCampaign.php
β β βββ Dashboard/ # Dashboard components
β β β βββ Donations.php
β β βββ LandingPage.php # Landing page component
β βββ Models/
β β βββ User.php
β β βββ Campaign.php
β β βββ CampaignCategory.php
β β βββ CampaignArticle.php
β β βββ Donation.php
β β βββ Attachment.php
β βββ Services/
β β βββ DonationService.php # Donation processing
β βββ Providers/
β βββ Filament/
β βββ AdminPanelProvider.php
βββ config/
β βββ payment.php # Midtrans configuration
βββ database/
β βββ migrations/ # Database migrations
β βββ seeders/
β βββ ShieldSeeder.php # Roles & permissions
β βββ UserSeeder.php # Default users
βββ resources/
β βββ views/
β β βββ livewire/ # Livewire components
β β β βββ campaign/
β β β βββ dashboard/
β β β βββ landing/
β β βββ components/ # Blade components
β β βββ layouts/ # Master layouts
β βββ css/
β βββ app.css # Tailwind entry
βββ routes/
β βββ web.php # Web routes
β βββ api.php # API routes (webhooks)
βββ tests/ # Feature & Unit tests
βββ .env.example # Environment template
βββ composer.json # PHP dependencies
βββ package.json # Node dependencies
βββ phpunit.xml # Test configuration
βββ vite.config.js # Vite + Tailwind CSS v4 plugin config
Before you begin, ensure you have the following installed:
-
PHP (8.4 or later) with extensions:
pdo_mysql,mbstring,openssl,json,fileinfo
-
Composer (PHP package manager)
curl -sS https://getcomposer.org/installer | php mv composer.phar /usr/local/bin/composer -
MySQL (8.0 or later) or MariaDB
-
Node.js (18+ or 20+) and NPM
-
Git
Follow these steps to get the application running locally.
git clone <your-repo-url>
cd donasikita-project
# Install PHP dependencies
composer install
# Install Node.js dependencies
npm installCopy the example environment file and configure it:
cp .env.example .env
# Generate application key
php artisan key:generateCritical Variables:
| Variable | Description | Default (Local) |
|---|---|---|
APP_NAME |
Application name | DonasiKita |
APP_URL |
Base URL | http://localhost:8000 |
DB_CONNECTION |
Database driver | mysql |
DB_HOST |
Database host | 127.0.0.1 |
DB_DATABASE |
Database name | donasikita |
DB_USERNAME |
Database user | root |
DB_PASSWORD |
Database password | (empty) |
MIDTRANS_SERVER_KEY |
Midtrans server key | (from Midtrans) |
MIDTRANS_CLIENT_KEY |
Midtrans client key | (from Midtrans) |
MIDTRANS_IS_PRODUCTION |
Production mode | false |
Create the database:
# Via MySQL CLI
mysql -u root -p -e "CREATE DATABASE donasikita;"Run migrations:
php artisan migrateSeed the database:
# Seed roles and permissions
php artisan db:seed --class=ShieldSeeder
# Seed default users
php artisan db:seed --class=UserSeederDefault credentials:
- Super Admin:
superadmin@example.com/example - Donor:
ahmad.rizki@example.com/password
Start the development server:
# Terminal 1: Laravel dev server
php artisan serve
# Terminal 2: Vite dev server (for assets)
npm run devThe application will be available at:
- Frontend:
http://localhost:8000 - Admin Panel:
http://localhost:8000/admin
Build for production:
npm run build| Script | Description |
|---|---|
composer install |
Install PHP dependencies |
composer dev |
Run full dev environment (Laravel + Queue + Logs + Vite) |
composer test |
Run test suite (Pest via php artisan test) |
composer test:coverage |
Run tests with coverage report |
composer test:coverage-html |
Generate HTML coverage report |
composer test:coverage-min |
Run tests with coverage (min 80%) |
npm install |
Install Node.js dependencies |
npm run dev |
Start Vite development server |
npm run build |
Build assets for production |
php artisan serve |
Start Laravel development server |
php artisan migrate |
Run database migrations |
php artisan migrate:fresh --seed |
Reset DB and seed |
php artisan db:seed --class=UserSeeder |
Seed users |
php artisan storage:link |
Create storage symlink |
php artisan route:list |
List all routes |
php artisan pail |
Monitor application logs |
vendor/bin/pint --dirty |
Format changed PHP files |
| Route | Controller/Component | Description |
|---|---|---|
GET / |
LandingPage |
Homepage with campaigns |
GET /campaign/{slug} |
Campaign\ShowCampaign |
Campaign detail page |
GET /campaign/{slug}/donate |
Campaign\DonationForm |
Donation form (auth + verified) |
| Route | Component/Livewire | Description |
|---|---|---|
GET /dashboard |
Dashboard\Dashboard |
User dashboard |
GET /dashboard/donations |
Dashboard\Donations |
Donation history |
| Route | Controller | Description |
|---|---|---|
POST /api/webhook/midtrans |
MidtransController@callback |
Midtrans payment webhook |
Controllers are kept thin - business logic lives in Services:
class MidtransController extends Controller
{
public function callback(Request $request)
{
$donation = app(DonationService::class)->handleCallback($request->all());
if (!$donation) {
return response()->json(['status' => 'error'], 404);
}
return response()->json(['status' => 'success']);
}
}class DonationService
{
public function handleCallback(array $data)
{
$donation = Donation::where('order_id', $data['order_id'])->first();
if (!$donation) {
return ['success' => false, 'message' => 'Donation not found'];
}
// Update status based on transaction_status
switch ($data['transaction_status']) {
case 'settlement':
case 'capture':
$donation->status = Donation::STATUS_PAID;
$donation->paid_at = now();
break;
// ... other cases
}
$donation->save();
return ['success' => true];
}
}// β
Good: Eager load relationships
$campaign = Campaign::with(['category', 'donations.user'])
->findOrFail($id);
// β Bad: N+1 queries
$campaign = Campaign::find($id);
foreach ($campaign->donations as $donation) {
echo $donation->user->name; // Extra query!
}Run the test suite:
# Run all tests
composer test
# Run tests with coverage (terminal output)
composer test:coverage
# Generate HTML coverage report
composer test:coverage-html
# Run tests with minimum 80% coverage requirement
composer test:coverage-min
# Or directly with Artisan
php artisan test
php artisan test --coverage
php artisan test --coverage-html=coverage-report
# Run specific test file
php artisan test --filter=CampaignTest
# Run specific test class
php artisan test tests/Unit/Services/DonationServiceTest.phptests/
βββ Feature/ # HTTP/Integration tests
β βββ CampaignPageTest.php
β βββ Auth/
βββ Unit/ # Unit tests
β βββ Models/
β β βββ CampaignTest.php
β β βββ DonationTest.php
β βββ Services/
β βββ DonationServiceTest.php
βββ TestCase.php # Base test class
This project aims for 80%+ code coverage. Key areas to test:
- Services: Business logic in DonationService
- Models: Relationships, scopes, accessors
- Feature Tests: HTTP endpoints, Livewire components
Example unit test for a Service:
class DonationServiceTest extends TestCase
{
use RefreshDatabase;
public function test_handle_callback_updates_donation_status(): void
{
$campaign = Campaign::factory()->create();
$donation = Donation::factory()->create([
'order_id' => 'TEST-123',
'status' => Donation::STATUS_PENDING,
]);
$service = new DonationService;
$result = $service->handleCallback([
'order_id' => 'TEST-123',
'transaction_status' => 'settlement',
]);
$this->assertTrue($result['success']);
$this->assertEquals(Donation::STATUS_PAID, $donation->fresh()->status);
}
}| Issue | Possible Cause | Solution |
|---|---|---|
| Database connection failed | MySQL not running / wrong credentials | Start MySQL and check .env DB_* variables |
| Class not found | Autoload not updated | Run composer dump-autoload |
| Storage images not loading | Symlink not created | Run php artisan storage:link |
| 419 Page Expired | CSRF token missing | Add @csrf to forms |
| Filament panel 403 | User doesn't have admin role | Check user has super_admin role |
| Admin login not working | Wrong credentials or role | Use superadmin@example.com / example |
| Midtrans payment fails | Missing/wrong API keys | Add correct keys to .env |
| Migration error | Schema mismatch | Run php artisan migrate:fresh --seed ( |
| Permission denied | File permissions | Run chmod -R 775 storage/ |
| CSS not loading | Vite not running | Run npm run dev in separate terminal |
| Blank page / 500 error | Check logs | Read storage/logs/laravel.log |
# Check Laravel version
php artisan --version
# List all routes
php artisan route:list
# Check config in Tinker
php artisan tinker
>>> config('payment.midtrans.server_key')
# Clear all caches
php artisan optimize:clear
# Check migration status
php artisan migrate:status
# Monitor logs in real-time
php artisan pailThis project is actively being developed. The following features are planned for upcoming releases:
| Feature | Description | Priority |
|---|---|---|
| Test Coverage | Comprehensive unit and feature tests for Services, Controllers, and Models (Pest + PHPUnit engine). Aim for 70%+ code coverage. | High |
| Reward System | Point-based loyalty program for donors who make regular contributions. Users can earn points redeemable for badges or recognition. | Medium |
| Campaign Analytics | Detailed analytics dashboard for campaign owners showing donation trends, donor demographics, and engagement metrics. | Medium |
| Recurring Donations | Subscription-based donation system allowing users to set up monthly/weekly automatic contributions. | High |
| Social Sharing | Enhanced social media integration with share buttons, campaign embeds, and viral tracking. | Low |
| Multi-language Support | Full localization support for English and Indonesian with easy expansion to other languages. | Medium |
| Advanced Search | Full-text search with filters for category, location, donation target, and campaign status. | Medium |
| Email Templates | Customizable email notification templates for different events (donation received, campaign updates, etc.). | Low |
| Export Reports | PDF/Excel export functionality for campaign owners to download donation reports. | Low |
Personal notes for future development on this experimental project:
- Test Coverage: Currently at 22% coverage - aiming for 70%+. Following TDD approach with mocked external services (Midtrans). Priority on Services and Livewire components.
- Recurring Donations: Planning to use Laravel's scheduling features with a
subscriptiontable tracking billing cycles and Midtrans subscription API. - Campaign Analytics: Thinking of using Laravel's caching for aggregating stats and possibly integrating Chart.js for visualizations.
- Reward System: Could implement with a simple pivot table between
usersand a newrewardstable with point calculations based on donation amounts. - Multi-language: Plan to use Laravel's localization features with
__()helper and language files inresources/lang/. - Search: Considering Laravel Scout with database driver for simple full-text search without external dependencies.
This is a learning/experimental project - features and priorities may change as I continue exploring Laravel and PHP best practices.
This is a learning project - features and implementation may change as I continue exploring Laravel and PHP best practices. The codebase has evolved significantly from the original tutorial boilerplate with custom modifications to:
- UI/UX design and styling
- Database schema and relationships
- Business logic in services
- Frontend components and layouts
- Admin panel customization
- Payment flow and webhook handling
This project is licensed under the MIT License.
Built for learning Laravel through practical donation platform development with Laravel 12 + Filament 4.


