PHP code example of vherbaut / laravel-inbound-webhooks
1. Go to this page and download the library: Download vherbaut/laravel-inbound-webhooks library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
vherbaut / laravel-inbound-webhooks example snippets
use Vherbaut\InboundWebhooks\Events\WebhookReceived;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
WebhookReceived::class => [
HandleStripeWebhook::class,
],
];
}
// app/Listeners/HandleStripeWebhook.php
class HandleStripeWebhook
{
public function handle(WebhookReceived $event): void
{
if ($event->provider() !== 'stripe') {
return;
}
match ($event->eventType()) {
'payment_intent.succeeded' => $this->handlePaymentSucceeded($event),
'customer.subscription.deleted' => $this->handleSubscriptionCanceled($event),
default => null,
};
}
protected function handlePaymentSucceeded(WebhookReceived $event): void
{
$paymentIntentId = $event->get('data.object.id');
$amount = $event->get('data.object.amount');
// Your logic here...
}
}
// app/Events/PaymentReceived.php
class PaymentReceived
{
public function __construct(
public InboundWebhook $webhook
) {}
}
use Vherbaut\InboundWebhooks\Events\WebhookReceived;
class HandleStripeWebhook
{
public function handle(WebhookReceived $event): void
{
// Access webhook data via helper methods
$provider = $event->provider(); // "stripe"
$eventType = $event->eventType(); // "payment_intent.succeeded"
$payload = $event->payload(); // Full payload array
$value = $event->get('data.object.id'); // Dot notation access
}
}
use Vherbaut\InboundWebhooks\Events\WebhookProcessed;
class UpdateMetrics
{
public function handle(WebhookProcessed $event): void
{
Metrics::increment("webhooks.{$event->webhook->provider}.success");
}
}
use Vherbaut\InboundWebhooks\Events\WebhookFailed;
class NotifyOnFailure
{
public function handle(WebhookFailed $event): void
{
Log::error('Webhook failed', [
'provider' => $event->webhook->provider,
'event_type' => $event->webhook->event_type,
'error' => $event->exception->getMessage(),
]);
// Send notification to Slack, email, etc.
}
}
use Vherbaut\InboundWebhooks\Events\WebhookReceived;
use Vherbaut\InboundWebhooks\Events\WebhookProcessed;
use Vherbaut\InboundWebhooks\Events\WebhookFailed;
protected $listen = [
WebhookReceived::class => [
HandleStripeWebhook::class,
HandleGitHubWebhook::class,
],
WebhookProcessed::class => [
UpdateWebhookMetrics::class,
],
WebhookFailed::class => [
NotifyOnWebhookFailure::class,
RetryFailedWebhook::class,
],
];
'storage' => [
'store_payload' => true, // Store full payload (recommended for replay)
'retention_days' => 30, // Auto-prune after 30 days (null = forever)
],
$schedule->command('webhooks:prune')->daily();
// app/Providers/AppServiceProvider.php
use Vherbaut\InboundWebhooks\Facades\InboundWebhooks;
use App\Webhooks\Drivers\PayPalDriver;
public function boot(): void
{
InboundWebhooks::extend('paypal', function (array $config) {
return new PayPalDriver($config);
});
}
'providers' => [
'paypal' => [
'driver' => 'paypal', // Must match the name used in extend()
'webhook_id' => env('PAYPAL_WEBHOOK_ID'), // Custom config keys
'client_id' => env('PAYPAL_CLIENT_ID'),
'client_secret' => env('PAYPAL_CLIENT_SECRET'),
'sandbox' => env('PAYPAL_SANDBOX', true),
],
],
// app/Webhooks/Drivers/PayPalDriver.php
namespace App\Webhooks\Drivers;
use Illuminate\Http\Request;
use Vherbaut\InboundWebhooks\Drivers\AbstractDriver;
use Vherbaut\InboundWebhooks\Exceptions\InvalidSignatureException;
class PayPalDriver extends AbstractDriver
{
/**
* Validate the webhook signature.
*
* @throws InvalidSignatureException
*/
public function validateSignature(Request $request): void
{
$transmissionId = $request->header('Paypal-Transmission-Id');
$timestamp = $request->header('Paypal-Transmission-Time');
$signature = $request->header('Paypal-Transmission-Sig');
$certUrl = $request->header('Paypal-Cert-Url');
if (! $transmissionId || ! $signature) {
throw new InvalidSignatureException('Missing PayPal signature headers');
}
// Build the expected signature string
$webhookId = $this->config['webhook_id'];
$payload = $request->getContent();
$crc32 = crc32($payload);
$expectedSignature = "{$transmissionId}|{$timestamp}|{$webhookId}|{$crc32}";
// Verify with PayPal certificate (simplified example)
if (! $this->verifyWithCertificate($expectedSignature, $signature, $certUrl)) {
throw new InvalidSignatureException('Invalid PayPal webhook signature');
}
}
/**
* Extract the event type from the webhook payload.
*/
public function getEventType(Request $request): ?string
{
return $request->input('event_type');
}
/**
* Extract the external ID (PayPal's webhook event ID).
*/
public function getExternalId(Request $request): ?string
{
return $request->input('id');
}
/**
* Define provider-specific headers to store for auditing.
*
* @return array<int, string>
*/
protected function getRelevantHeaders(): array
{
return [
'Content-Type',
'Paypal-Transmission-Id',
'Paypal-Transmission-Time',
'Paypal-Transmission-Sig',
'Paypal-Cert-Url',
];
}
private function verifyWithCertificate(
string $data,
string $signature,
?string $certUrl
): bool {
// Your verification logic here
return true;
}
}
class AcmeDriver extends AbstractDriver
{
public function validateSignature(Request $request): void
{
$signature = $request->header('X-Acme-Signature');
$payload = $request->getContent();
$secret = $this->config['secret'];
if (! $signature) {
throw new InvalidSignatureException('Missing signature header');
}
// Use built-in HMAC computation
$expected = $this->computeHmac($payload, $secret, 'sha256');
// Use timing-safe comparison to prevent timing attacks
if (! $this->compareSignatures($expected, $signature)) {
throw new InvalidSignatureException('Invalid signature');
}
}
// ...
}
public function getPayload(Request $request): array
{
// Handle form-encoded webhooks (e.g., Twilio)
if ($request->isJson()) {
return $request->json()->all();
}
return $request->all();
}
interface DriverInterface
{
/**
* Validate the webhook signature.
*
* @throws InvalidSignatureException
*/
public function validateSignature(Request $request): void;
/**
* Extract the event type from the webhook payload.
*/
public function getEventType(Request $request): ?string;
/**
* Extract the external ID (provider's webhook/event ID).
*/
public function getExternalId(Request $request): ?string;
/**
* Get the parsed payload from the request.
*/
public function getPayload(Request $request): array;
/**
* Get headers that should be stored with the webhook.
*/
public function getStorableHeaders(Request $request): array;
}
use App\Webhooks\Drivers\PayPalDriver;
use Illuminate\Http\Request;
use Vherbaut\InboundWebhooks\Exceptions\InvalidSignatureException;
it('validates paypal webhook signature', function () {
$driver = new PayPalDriver([
'webhook_id' => 'WH-123',
'client_id' => 'test',
'client_secret' => 'secret',
]);
$request = Request::create('/webhooks/paypal', 'POST', [], [], [], [
'HTTP_PAYPAL_TRANSMISSION_ID' => 'abc123',
'HTTP_PAYPAL_TRANSMISSION_SIG' => 'valid-signature',
'HTTP_PAYPAL_TRANSMISSION_TIME' => '2024-01-15T10:00:00Z',
], json_encode(['event_type' => 'PAYMENT.CAPTURE.COMPLETED']));
// Should not throw
$driver->validateSignature($request);
});
it('rejects invalid signature', function () {
$driver = new PayPalDriver(['webhook_id' => 'WH-123']);
$request = Request::create('/webhooks/paypal', 'POST');
$driver->validateSignature($request);
})->throws(InvalidSignatureException::class);
use Vherbaut\InboundWebhooks\Models\InboundWebhook;
// Get all failed Stripe webhooks
$failed = InboundWebhook::provider('stripe')
->failed()
->latest()
->get();
// Get recent payment webhooks
$payments = InboundWebhook::provider('stripe')
->eventType('payment_intent.succeeded')
->where('created_at', '>', now()->subDay())
->get();
// Retry all failed webhooks
InboundWebhook::failed()->each(function ($webhook) {
$webhook->resetForRetry();
ProcessWebhook::dispatch($webhook);
});
use Vherbaut\InboundWebhooks\Models\InboundWebhook;
// Create a webhook directly for testing
$webhook = InboundWebhook::create([
'provider' => 'stripe',
'event_type' => 'payment_intent.succeeded',
'payload' => [
'type' => 'payment_intent.succeeded',
'data' => [
'object' => [
'id' => 'pi_123',
'amount' => 1000,
],
],
],
]);
// Process it
ProcessWebhook::dispatchSync($webhook);