diff --git a/.env.example b/.env.example index a2f35d357..0722505cd 100644 --- a/.env.example +++ b/.env.example @@ -53,6 +53,7 @@ JWT_SECRET=Jrsweag3Mf0srOqDizRkhjWm5CEFcrBy PADDLE_VENDOR_ID= PADDLE_VENDOR_AUTH_CODE= PADDLE_ENV=sandbox +PADDLE_PUBLIC_KEY= WAVE_DOCS=true WAVE_DEMO=false diff --git a/config/wave.php b/config/wave.php index 85757967e..4a5689893 100644 --- a/config/wave.php +++ b/config/wave.php @@ -23,7 +23,8 @@ 'paddle' => [ 'vendor' => env('PADDLE_VENDOR_ID', ''), 'auth_code' => env('PADDLE_VENDOR_AUTH_CODE', ''), - 'env' => env('PADDLE_ENV', 'sandbox') + 'env' => env('PADDLE_ENV', 'sandbox'), + 'public_key' => env('PADDLE_PUBLIC_KEY', ''), ] ]; diff --git a/wave/routes/web.php b/wave/routes/web.php index 7a3de015d..3497ec2d6 100644 --- a/wave/routes/web.php +++ b/wave/routes/web.php @@ -28,7 +28,7 @@ Route::view('pricing', 'theme::pricing')->name('wave.pricing'); /***** Billing Routes *****/ -Route::post('paddle/webhook', '\Wave\Http\Controllers\SubscriptionController@webhook'); +Route::post('paddle/webhook', '\Wave\Http\Controllers\WebhookController'); Route::post('checkout', '\Wave\Http\Controllers\SubscriptionController@checkout')->name('checkout'); Route::get('test', '\Wave\Http\Controllers\SubscriptionController@test'); diff --git a/wave/src/Http/Controllers/SubscriptionController.php b/wave/src/Http/Controllers/SubscriptionController.php index 604cf9e25..1e26f1edc 100644 --- a/wave/src/Http/Controllers/SubscriptionController.php +++ b/wave/src/Http/Controllers/SubscriptionController.php @@ -31,36 +31,6 @@ public function __construct(){ $this->paddle_vendors_url = (config('wave.paddle.env') == 'sandbox') ? 'https://sandbox-vendors.paddle.com/api' : 'https://vendors.paddle.com/api'; } - - public function webhook(Request $request){ - - // Which alert/event is this request for? - $alert_name = $request->alert_name; - $subscription_id = $request->subscription_id; - $status = $request->status; - - - // Respond appropriately to this request. - switch($alert_name) { - - case 'subscription_created': - break; - case 'subscription_updated': - break; - case 'subscription_cancelled': - $this->cancelSubscription($subscription_id); - return response()->json(['status' => 1]); - break; - case 'subscription_payment_succeeded': - break; - case 'subscription_payment_failed': - $this->cancelSubscription($subscription_id); - return response()->json(['status' => 1]); - break; - } - - } - public function cancel(Request $request){ $this->cancelSubscription($request->id); return response()->json(['status' => 1]); diff --git a/wave/src/Http/Controllers/WebhookController.php b/wave/src/Http/Controllers/WebhookController.php new file mode 100644 index 000000000..4c93fa45e --- /dev/null +++ b/wave/src/Http/Controllers/WebhookController.php @@ -0,0 +1,51 @@ +middleware(VerifyWebhook::class); + } + } + + public function __invoke(Request $request) + { + $method = match ($request->get('alert_name', null)) { + 'subscription_cancelled', + 'subscription_payment_failed' => 'subscriptionCancelled', + default => null, + }; + + if (method_exists($this, $method)) { + try { + $this->{$method}($request); + } catch (\Exception $e) { + return response('Webhook failed'); + } + } + + return response('Webhook handled'); + } + + protected function subscriptionCancelled(Request $request) + { + $subscription = PaddleSubscription::where('subscription_id', $request->subscription_id)->firstOrFail(); + $subscription->cancelled_at = Carbon::now(); + $subscription->status = 'cancelled'; + $subscription->save(); + $user = config('wave.user_model')::find($subscription->user_id); + $cancelledRole = Role::where('name', '=', 'cancelled')->first(); + $user->role_id = $cancelledRole->id; + $user->save(); + } +} diff --git a/wave/src/Http/Middleware/VerifyWebhook.php b/wave/src/Http/Middleware/VerifyWebhook.php new file mode 100644 index 000000000..164ce8c79 --- /dev/null +++ b/wave/src/Http/Middleware/VerifyWebhook.php @@ -0,0 +1,43 @@ +get('p_signature'); + $fields = $request->except('p_signature'); + + ksort($fields); + + foreach ($fields as $k => $v) { + if (!in_array(gettype($v), array('object', 'array'))) { + $fields[$k] = "$v"; + } + } + + if (openssl_verify( + serialize($fields), + base64_decode($signature), + openssl_get_publickey(config('wave.paddle.public_key')), + OPENSSL_ALGO_SHA1 + ) !== 1) { + throw new InvalidArgumentException('Webhook signature is invalid.'); + } + + return $next($request); + } +}