diff --git a/config/paystack.php b/config/paystack.php index ffe1dcd..7141130 100644 --- a/config/paystack.php +++ b/config/paystack.php @@ -7,8 +7,8 @@ "merchant_email" => env("PAYSTACK_MERCHANT_EMAIL"), "route" => [ - "middleware" => ['paystack_route_disabled', 'api'], // For injecting middleware to the package's routes - "prefix" => 'api', // For injecting middleware to the package's routes - 'hook_middleware' => ['paystack_route_disabled', 'api'] + "middleware" => ["paystack_route_disabled", "api"], // For injecting middleware to the package's routes + "prefix" => "api", // For injecting middleware to the package's routes + "hook_middleware" => ["validate_paystack_hook", "api"] ], ]; diff --git a/readme.md b/readme.md index 21dc2ae..8bd6ff8 100644 --- a/readme.md +++ b/readme.md @@ -38,7 +38,7 @@ return [ "route" => [ "middleware" => ["paystack_route_disabled", "api"], // For injecting middleware to the package's routes "prefix" => "api", // For injecting middleware to the package's routes - "hook_middleware" => ["paystack_route_disabled", "api"] + "hook_middleware" => ["validate_paystack_hook", "api"] ], ]; ``` @@ -408,6 +408,7 @@ Miscellaneous::listStates($params); ### Using WebHook route Laravel paystack provides you a predefined endpoint that listens to and validates incoming paystack's webhook events. It emits `Myckhel\Paystack\Events\Hook` on every incoming hooks which could be listened to. +The hook request is validated with `validate_paystack_hook` middleware by using the paystack's config `secret_key` against the incoming request. ## Setup Paystack Webhook [Check official page to read more about paystack webhook](https://paystack.com/docs/payments/webhooks/#introduction) diff --git a/src/Http/Controllers/HookController.php b/src/Http/Controllers/HookController.php index 41fdd67..247e034 100644 --- a/src/Http/Controllers/HookController.php +++ b/src/Http/Controllers/HookController.php @@ -4,29 +4,11 @@ use Myckhel\Paystack\Events\Hook; use Illuminate\Http\Request; -use Myckhel\Paystack\Traits\PaystackConfig; class HookController extends Controller { - use PaystackConfig; - public function hook(Request $request) { - $signature = $request->header('x-paystack-signature'); - if (!$signature) { - abort(403); - } - - $signingSecret = $this->config('secret_key'); - - if (empty($signingSecret)) { - abort(403, 'Signing Secret Not Set'); - } - - $computedSignature = hash_hmac('sha512', $request->getContent(), $signingSecret); - - if (!hash_equals($signature, $computedSignature)) return abort(403); - event(new Hook($request->all())); return ['status' => true]; diff --git a/src/Http/Middleware/ValidatePaystackHook.php b/src/Http/Middleware/ValidatePaystackHook.php new file mode 100644 index 0000000..80b6d66 --- /dev/null +++ b/src/Http/Middleware/ValidatePaystackHook.php @@ -0,0 +1,38 @@ +header('x-paystack-signature'); + if (!$signature) { + abort(403, 'Signature header not found'); + } + + $signingSecret = $this->config('secret_key'); + + if (empty($signingSecret)) { + abort(403, 'Signing Secret Not Set'); + } + + $computedSignature = hash_hmac('sha512', $request->getContent(), $signingSecret); + + if (!hash_equals($signature, $computedSignature)) return abort(403, "Invalid Secret Signature"); + + return $next($request); + } +} diff --git a/src/PaystackServiceProvider.php b/src/PaystackServiceProvider.php index 7c5c959..b5e16e3 100644 --- a/src/PaystackServiceProvider.php +++ b/src/PaystackServiceProvider.php @@ -3,6 +3,7 @@ namespace Myckhel\Paystack; use Illuminate\Support\ServiceProvider; +use Myckhel\Paystack\Http\Middleware\ValidatePaystackHook; use Myckhel\Paystack\Http\Middleware\DisabledRoute; class PaystackServiceProvider extends ServiceProvider @@ -19,6 +20,7 @@ public function register() $this->mergeConfigFrom(__DIR__ . '/../config/paystack.php', 'paystack'); $this->app['router']->aliasMiddleware('paystack_route_disabled', DisabledRoute::class); + $this->app['router']->aliasMiddleware('validate_paystack_hook', ValidatePaystackHook::class); // Register the service the package provides. $this->app->singleton(