Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ yarn-error.log
/.idea
/.vscode
/.zed

/.github
12 changes: 12 additions & 0 deletions app/DonationStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace App;

enum DonationStatus: string
{
case CANCELLED = 'cancelled';
case COMPLETED = 'completed';
case FAILED = 'failed';
case PENDING = 'pending';
case REFUNDED = 'refunded';
}
54 changes: 54 additions & 0 deletions app/Http/Controllers/DonationController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace App\Http\Controllers;

use App\DonationStatus;
use App\Http\Requests\StoreDonationRequest;
use App\Http\Requests\UpdateDonationRequest;
use App\Http\Resources\DonationResource;
use App\Models\Donation;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

class DonationController extends Controller
{
public function store(StoreDonationRequest $request): JsonResponse
{
$validated = $request->validated();

$donation = Donation::create([
'user_id' => $request->user()->id,
'amount' => $validated['amount'],
'currency' => strtoupper($validated['currency']),
'donated_at' => now(),
])->refresh();

return response()->json([
'message' => 'Donation created successfully',
'donation' => $donation,
], Response::HTTP_CREATED);
}

public function update(Donation $donation, UpdateDonationRequest $request): JsonResponse
{
$validated = $request->validated();
$donation->update($validated);

return response()->json([
'message' => 'Donation updated successfully',
'donation' => $donation,
], Response::HTTP_OK);
}

public function historyByUser(string $user_id): JsonResponse
{
$donations = Donation::where('user_id', $user_id)
->orderBy('donated_at', 'desc')
->where('status', DonationStatus::COMPLETED->value)
->get();

return response()->json([
'donations' => DonationResource::collection($donations),
], Response::HTTP_OK);
}
}
10 changes: 10 additions & 0 deletions app/Http/Controllers/RecurringDonationController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class RecurringDonationController extends Controller
{
//
}
46 changes: 46 additions & 0 deletions app/Http/Requests/StoreDonationRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreDonationRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'amount' => ['required', 'numeric', 'min:1'],
'currency' => ['required', 'string', 'size:3'],
// 'status' => ['sometimes', 'string', 'in:cancelled,completed,failed,pending,refunded'],
];
}

/**
* Get custom error messages for validation rules.
*
* @return array<string, string>
*/
public function messages(): array
{
return [
'amount.required' => 'Donation amount is required.',
'amount.numeric' => 'Donation amount must be a number.',
'amount.min' => 'Donation amount must be at least 1.',
'currency.required' => 'Currency is required.',
'currency.size' => 'Currency must be a 3-letter code.',
// 'status.in' => 'Status must be one of: cancelled, completed, failed, pending, refunded.',
];
}
}
39 changes: 39 additions & 0 deletions app/Http/Requests/UpdateDonationRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UpdateDonationRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'status' => ['required', 'string', 'in:cancelled,completed,failed,refunded'],
];
}

/**
* Get custom error messages for validation rules.
*
* @return array<string, string>
*/
public function messages(): array
{
return [
'status.in' => 'Status must be one of: cancelled, completed, failed, refunded.',
];
}
}
19 changes: 19 additions & 0 deletions app/Http/Resources/DonationCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;

class DonationCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* @return array<int|string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}
29 changes: 29 additions & 0 deletions app/Http/Resources/DonationResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class DonationResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'user_id' => $this->user_id,
'amount' => $this->amount,
'currency' => $this->currency,
'status' => $this->status,
'is_recurring' => $this->recurring_donation_id ? 'true' : 'false',
'donated_at' => $this->donated_at,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}
39 changes: 39 additions & 0 deletions app/Models/Donation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace App\Models;

use App\DonationStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Donation extends Model
{
/** @use HasFactory<\Database\Factories\DonationFactory> */
use HasFactory;

/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'user_id',
'amount',
'currency',
'donated_at',
'status',
'recurring_donation_id',
];

/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'status' => DonationStatus::class,
];
}
}
40 changes: 40 additions & 0 deletions app/Models/RecurringDonation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Models;

use App\RecurringDonationStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class RecurringDonation extends Model
{
/** @use HasFactory<\Database\Factories\RecurringDonationFactory> */
use HasFactory;

/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'user_id',
'amount',
'currency',
'schedule',
'start_date',
'end_date',
'status',
];

/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'status' => RecurringDonationStatus::class,
];
}
}
3 changes: 2 additions & 1 deletion app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
use HasApiTokens, HasFactory, Notifiable;

/**
* The attributes that are mass assignable.
Expand Down
10 changes: 10 additions & 0 deletions app/RecurringDonationStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App;

enum RecurringDonationStatus: string
{
case ACTIVE = 'active';
case CANCELLED = 'cancelled';
case PAUSED = 'paused';
}
1 change: 1 addition & 0 deletions bootstrap/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
Expand Down
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
"require": {
"php": "^8.2",
"laravel/framework": "^11.9",
"laravel/sanctum": "^4.0",
"laravel/tinker": "^2.9"
},
"require-dev": {
"fakerphp/faker": "^1.23",
"laravel/boost": "^1.0",
"laravel/pint": "^1.13",
"laravel/sail": "^1.26",
"mockery/mockery": "^1.6",
Expand Down
Loading