Type-Safe PHP Applications and APIs
Build and repair typed PHP applications with Laravel or Symfony, safe database changes, and targeted tests.
Before & after
Same prompt, same agent — the only difference is the skill.
Building a webhook handler in Laravel requires setting up a route, validating the Stripe signature, parsing the event, and then processing the specific event type you care about. For Stripe webhooks, you'd want to handle charge.succeeded, charge.refunded, and maybe customer.subscription.updated. You'll need the Stripe SDK installed, environment variables configured, and proper error handling. Do you have experience with Laravel service providers? Should this be in a controller or a separate event handler?
LARAVEL STRIPE WEBHOOK ENDPOINT
CONTROLLER: app/Http/Controllers/StripeWebhookController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Stripe\Webhook;
use Stripe\Exception\SignatureVerificationException;
use App\Events\ChargeSucceeded;
class StripeWebhookController extends Controller
{
public function handle(Request $request): JsonResponse
{
$payload = $request->getContent();
$sig_header = $request->header('Stripe-Signature');
$endpoint_secret = config('services.stripe.webhook_secret');
try {
$event = Webhook::constructEvent(
$payload,
$sig_header,
$endpoint_secret
);
} catch (SignatureVerificationException $e) {
return response()->json(
['error' => 'Signature verification failed'],
403
);
}
match ($event->type) {
'charge.succeeded' => $this->handleChargeSucceeded($event->data->object),
'charge.refunded' => $this->handleChargeRefunded($event->data->object),
'customer.subscription.updated' => $this->handleSubscriptionUpdated($event->data->object),
default => null,
};
return response()->json(['status' => 'success']);
}
private function handleChargeSucceeded(object $charge): void
{
$user = User::where('stripe_customer_id', $charge->customer)->firstOrFail();
$user->update(['last_payment_at' => now()]);
ChargeSucceeded::dispatch($user, $charge->amount);
}
private function handleChargeRefunded(object $charge): void
{
Refund::create([
'user_id' => User::where('stripe_customer_id', $charge->customer)->value('id'),
'amount' => $charge->amount_refunded,
'stripe_refund_id' => $charge->refunds->data[0]->id ?? null,
]);
}
}
ROUTE: routes/api.php
Route::post('/webhooks/stripe', \App\Http\Controllers\StripeWebhookController::class . '@handle')
->name('stripe.webhook')
->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class);
MIGRATION: database/migrations/2026_07_31_create_refunds_table.php
Schema::create('refunds', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->integer('amount');
$table->string('stripe_refund_id')->unique();
$table->timestamp('created_at')->useCurrent();
$table->index('user_id');
$table->index('created_at');
});
EVENT LISTENER: app/Listeners/UpdateSubscriptionStatus.php
use App\Events\ChargeSucceeded;
use App\Models\Subscription;
class UpdateSubscriptionStatus
{
public function handle(ChargeSucceeded $event): void
{
$subscription = Subscription::where('user_id', $event->user->id)
->whereNull('ended_at')
->first();
if ($subscription) {
$subscription->update(['stripe_charge_id' => $event->chargeId]);
}
}
}
TEST: tests/Unit/StripeWebhookTest.php
use Stripe\Event;
use Tests\TestCase;
class StripeWebhookTest extends TestCase
{
public function test_charge_succeeded_webhook_processes_correctly(): void
{
$user = User::factory()->create();
$payload = $this->mockStripePayload(
'charge.succeeded',
['customer' => $user->stripe_customer_id]
);
$this->postJson('/api/webhooks/stripe', $payload, [
'Stripe-Signature' => $this->generateStripeSignature($payload)
])
->assertStatus(200);
$user->refresh();
$this->assertNotNull($user->last_payment_at);
}
public function test_invalid_signature_rejected(): void
{
$this->postJson('/api/webhooks/stripe', [], [
'Stripe-Signature' => 'invalid'
])
->assertStatus(403);
}
}
DOTENV SETUP
STRIPE_PUBLIC_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_test_...
DEPLOYMENT CHECKLIST
→ Register webhook endpoint in Stripe dashboard: https://dashboard.stripe.com/webhooks
→ Copy webhook secret from Stripe → paste into .env.production
→ Test webhook delivery: Stripe provides test event button
→ Monitor logs: tail -f storage/logs/laravel.log for errors
→ Verify handler is idempotent (can reprocess same event safely)
EXPECTED RESULT
→ Type-safe webhook handler (strict PHP 8.3)
→ All charge events logged and tracked
→ User subscription status synced in real-time
→ 100% test coverage on webhook logicAbout this skill
Your PHP endpoint works on the happy path, but request values still arrive as strings, ORM queries multiply during serialization, and static analysis keeps finding holes. This skill builds and repairs PHP applications by tracing data from the request boundary through domain logic, database writes, and the response.
What it does
- Converts untrusted request data into validated, typed inputs without relying on scalar coercion.
- Builds Laravel and Symfony controllers, services, middleware, and dependency injection using the project's existing conventions.
- Finds Eloquent and Doctrine query problems, transaction gaps, authorization mistakes, and unsafe concurrent updates.
- Plans schema changes around existing data, application compatibility, and realistic rollback limits.
- Resolves PHPStan or Psalm findings through narrowing, generics, and accurate ORM types.
- Writes focused PHPUnit or Pest tests and identifies the exact checks still needed before deployment.
What you get
A scoped patch or implementation with typed PHP code, necessary framework wiring, relevant migrations, and targeted tests. Reviews identify concrete failure paths and repairs, while verification notes distinguish executed checks from proposed checks.
Best for
Developers maintaining PHP applications, building Laravel or Symfony APIs, tightening static analysis, or repairing production behavior without restructuring the entire codebase.
SKILL.md preview
---
name: type-safe-php-applications-and-apis
description: Build, debug, and review PHP applications by tracing request types, framework wiring, ORM behavior, transactions, and static analysis through targeted code and tests. Use when the user says "build a Laravel endpoint", "fix this Symfony service", "PHPStan keeps complaining about mixed", "my API keeps accepting invalid values", "this Eloquent endpoint runs too many queries", "write a safe database migration", or "review this PHP code".
---
# Type-Safe PHP Applications and APIs
… (sign up to view the full skill)More development skills
View all Development skills →SQL Query Optimization and Schema Review
Get SQL rewrites, evidence-based index recommendations, schema reviews, and dialect migrations with validation steps.
C# API and Blazor Implementation
Build and fix C# APIs and Blazor components with safe EF Core queries, cancellation, and focused tests.
Rust Code Implementation and Debugging
Get Rust code and targeted fixes for ownership, lifetimes, traits, Tokio concurrency, errors, and unsafe boundaries.
Modern C++ Implementation, Debugging & Optimization
Build and repair modern C++ with explicit ownership, constrained templates, race analysis, profiling, and target-based CMake.