spatie/laravel-github-webhooks
Handle GitHub webhooks in Laravel with signature verification, automatic logging to the database, and easy job/event dispatching per webhook type. Configure handlers, queue processing, and access the webhook payload via a stored call model.
Installation:
composer require spatie/laravel-github-webhooks
php artisan vendor:publish --provider="Spatie\GitHubWebhooks\GitHubWebhooksServiceProvider"
Publish the config and migration files.
Configure GitHub Webhook:
Settings > Webhooks > Add webhook).POST /github-webhook).First Use Case:
push, issues).php artisan make:job HandlePushWebhookJob
config/github-webhooks.php under webhook_events.Test Locally:
spatie_github_webhook_calls table.Event Handling:
config/github-webhooks.php:
'webhook_events' => [
'push' => \App\Jobs\GitHubWebhooks\HandlePushWebhookJob::class,
'issues' => \App\Jobs\GitHubWebhooks\HandleIssueOpenedWebhookJob::class,
],
GitHubWebhookCall model to access payload and metadata.Job Structure:
GitHubWebhookCall into jobs to access:
public function handle(GitHubWebhookCall $call) {
$payload = $call->payload; // Decoded JSON payload
$event = $call->event; // Event name (e.g., 'push')
}
HandleWebhook middleware (auto-applied to /github-webhook).Queue Integration:
config/queue.php.Payload Validation:
Spatie\GitHubWebhooks\Webhook to validate payloads:
use Spatie\GitHubWebhooks\Webhook;
$webhook = new Webhook($payload, $signature, $secret);
if ($webhook->isValid()) {
// Process
}
Logging:
spatie_github_webhook_calls.config/github-webhooks.php:
'log_webhook_calls' => true,
'log_webhook_call_model' => \App\Models\CustomWebhookLog::class,
Dynamic Event Handling:
public function handle($request, Closure $next) {
if ($request->input('event') === 'custom_event') {
return $next($request);
}
return response('Unauthorized', 403);
}
Webhook Secrets:
.env:
GITHUB_WEBHOOK_SECRET=your_secret_here
.env.Testing:
Spatie\GitHubWebhooks\Testing\WebhookTestCase:
use Spatie\GitHubWebhooks\Testing\WebhookTestCase;
class PushWebhookTest extends WebhookTestCase {
public function test_push_webhook() {
$this->assertDispatches(
HandlePushWebhookJob::class,
'push'
);
}
}
Custom Payload Parsing:
GitHubWebhookCall or use accessors:
public function getRepoName(): string {
return $this->payload['repository']['full_name'];
}
Rate Limiting:
/github-webhook with Laravel’s throttle middleware:
Route::middleware(['throttle:100,1'])->post('/github-webhook', ...);
Signature Mismatch:
GITHUB_WEBHOOK_SECRET matches GitHub’s webhook secret.\Log::debug('Expected signature:', $webhook->expectedSignature());
\Log::debug('Received signature:', $request->header('X-Hub-Signature-256'));
Payload Size:
push with many files).max_input_vars or use input() instead of json() in middleware.Queue Failures:
ShouldQueue with backoff:
public function retryUntil() {
return now()->addMinutes(5);
}
Event Naming:
pull_request.opened) differ from Laravel’s job keys.config/github-webhooks.php:
'webhook_events' => [
'pull_request.opened' => HandlePROpenedJob::class,
],
Testing Locally:
ngrok to expose localhost to GitHub:
ngrok http 8000
https://your-ngrok-url.ngrok.io/github-webhook.Log Payloads:
config/github-webhooks.php:
'log_webhook_calls' => true,
'log_level' => 'debug',
Validate Payloads Manually:
Check Middleware:
Spatie\GitHubWebhooks\HandleWebhook is registered in app/Http/Kernel.php:
protected $middleware = [
// ...
\Spatie\GitHubWebhooks\HandleWebhook::class,
];
Database Logs:
spatie_github_webhook_calls for failed calls:
SELECT * FROM spatie_github_webhook_calls WHERE failed_at IS NOT NULL;
Custom Models:
Spatie\GitHubWebhooks\Models\GitHubWebhookCall:
class CustomWebhookCall extends GitHubWebhookCall {
public function getAuthorName() {
return $this->payload['sender']['login'];
}
}
config/github-webhooks.php:
'webhook_call_model' => \App\Models\CustomWebhookCall::class,
Custom Verification:
Spatie\GitHubWebhooks\Webhook for custom signature logic:
class CustomWebhook extends Webhook {
public function isValid(): bool {
// Custom validation
return true;
}
}
Webhook Routes:
routes/web.php:
Route::post('/custom-webhook', \Spatie\GitHubWebhooks\WebhookController::class)
->middleware(['github.webhook']);
Event Filtering:
public function handle($request, Closure $next) {
if (!in_array($request->input('event'), ['push', 'issues'])) {
return response('Forbidden', 403);
}
return $next($request);
}
Webhook Retries:
public function retryUntil() {
return now()->addSeconds(2 ** $this->attempts());
}
How can I help you explore Laravel packages today?