diff --git a/resources/views/livewire/dashboard.blade.php b/resources/views/livewire/dashboard.blade.php index 64a861653..022a58ff2 100644 --- a/resources/views/livewire/dashboard.blade.php +++ b/resources/views/livewire/dashboard.blade.php @@ -5,13 +5,13 @@

Dashboard

Your self-hosted environment
@if (request()->query->get('success')) -
+
- Your subscription has been activated! Welcome onboard! + Your subscription has been activated! Welcome onboard!
It could take a few seconds before your subscription is activated.
Please be patient.
@endif @if ($projects->count() === 0 && $servers->count() === 0) diff --git a/routes/webhooks.php b/routes/webhooks.php index 1fb0bce4e..777789940 100644 --- a/routes/webhooks.php +++ b/routes/webhooks.php @@ -1,5 +1,6 @@ true, ]); break; + case 'invoice.payment_failed': + $customerId = data_get($data, 'customer'); + $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); + $team = data_get($subscription, 'team'); + if (!$team) { + throw new Exception('No team found for subscription: ' . $subscription->id); + } + SubscriptionInvoiceFailedJob::dispatch($team); + send_internal_notification('Invoice payment failed: ' . $subscription->team->id); + break; case 'payment_intent.payment_failed': $customerId = data_get($data, 'customer'); $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); @@ -610,7 +621,11 @@ send_internal_notification('Subscription excluded.'); break; } - $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); + $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); + if (!$subscription) { + Sleep::for(5)->seconds(); + $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); + } $trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended'); $cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end'); $alreadyCancelAtPeriodEnd = data_get($subscription, 'stripe_cancel_at_period_end'); @@ -703,129 +718,129 @@ return response($e->getMessage(), 400); } }); -Route::post('/payments/paddle/events', function () { - try { - $payload = request()->all(); - $signature = request()->header('Paddle-Signature'); - $ts = Str::of($signature)->after('ts=')->before(';'); - $h1 = Str::of($signature)->after('h1='); - $signedPayload = $ts->value . ':' . request()->getContent(); - $verify = hash_hmac('sha256', $signedPayload, config('subscription.paddle_webhook_secret')); - if (!hash_equals($verify, $h1->value)) { - return response('Invalid signature.', 400); - } - $eventType = data_get($payload, 'event_type'); - $webhook = Webhook::create([ - 'type' => 'paddle', - 'payload' => $payload, - ]); - // TODO - Handle events - switch ($eventType) { - case 'subscription.activated': - } - ray('Subscription event: ' . $eventType); - $webhook->update([ - 'status' => 'success', - ]); - } catch (Exception $e) { - ray($e->getMessage()); - send_internal_notification('Subscription webhook failed: ' . $e->getMessage()); - $webhook->update([ - 'status' => 'failed', - 'failure_reason' => $e->getMessage(), - ]); - } finally { - return response('OK'); - } -}); -Route::post('/payments/lemon/events', function () { - try { - $secret = config('subscription.lemon_squeezy_webhook_secret'); - $payload = request()->collect(); - $hash = hash_hmac('sha256', $payload, $secret); - $signature = request()->header('X-Signature'); +// Route::post('/payments/paddle/events', function () { +// try { +// $payload = request()->all(); +// $signature = request()->header('Paddle-Signature'); +// $ts = Str::of($signature)->after('ts=')->before(';'); +// $h1 = Str::of($signature)->after('h1='); +// $signedPayload = $ts->value . ':' . request()->getContent(); +// $verify = hash_hmac('sha256', $signedPayload, config('subscription.paddle_webhook_secret')); +// if (!hash_equals($verify, $h1->value)) { +// return response('Invalid signature.', 400); +// } +// $eventType = data_get($payload, 'event_type'); +// $webhook = Webhook::create([ +// 'type' => 'paddle', +// 'payload' => $payload, +// ]); +// // TODO - Handle events +// switch ($eventType) { +// case 'subscription.activated': +// } +// ray('Subscription event: ' . $eventType); +// $webhook->update([ +// 'status' => 'success', +// ]); +// } catch (Exception $e) { +// ray($e->getMessage()); +// send_internal_notification('Subscription webhook failed: ' . $e->getMessage()); +// $webhook->update([ +// 'status' => 'failed', +// 'failure_reason' => $e->getMessage(), +// ]); +// } finally { +// return response('OK'); +// } +// }); +// Route::post('/payments/lemon/events', function () { +// try { +// $secret = config('subscription.lemon_squeezy_webhook_secret'); +// $payload = request()->collect(); +// $hash = hash_hmac('sha256', $payload, $secret); +// $signature = request()->header('X-Signature'); - if (!hash_equals($hash, $signature)) { - return response('Invalid signature.', 400); - } +// if (!hash_equals($hash, $signature)) { +// return response('Invalid signature.', 400); +// } - $webhook = Webhook::create([ - 'type' => 'lemonsqueezy', - 'payload' => $payload, - ]); - $event = data_get($payload, 'meta.event_name'); - ray('Subscription event: ' . $event); - $email = data_get($payload, 'data.attributes.user_email'); - $team_id = data_get($payload, 'meta.custom_data.team_id'); - if (is_null($team_id) || empty($team_id)) { - throw new Exception('No team_id found in webhook payload.'); - } - $subscription_id = data_get($payload, 'data.id'); - $order_id = data_get($payload, 'data.attributes.order_id'); - $product_id = data_get($payload, 'data.attributes.product_id'); - $variant_id = data_get($payload, 'data.attributes.variant_id'); - $variant_name = data_get($payload, 'data.attributes.variant_name'); - $customer_id = data_get($payload, 'data.attributes.customer_id'); - $status = data_get($payload, 'data.attributes.status'); - $trial_ends_at = data_get($payload, 'data.attributes.trial_ends_at'); - $renews_at = data_get($payload, 'data.attributes.renews_at'); - $ends_at = data_get($payload, 'data.attributes.ends_at'); - $update_payment_method = data_get($payload, 'data.attributes.urls.update_payment_method'); - $team = Team::find($team_id); - $found = $team->members->where('email', $email)->first(); - if (!$found->isAdmin()) { - throw new Exception("User {$email} is not an admin or owner of team {$team->id}."); - } - switch ($event) { - case 'subscription_created': - case 'subscription_updated': - case 'subscription_resumed': - case 'subscription_unpaused': - send_internal_notification("LemonSqueezy Event (`$event`): `" . $email . '` with status `' . $status . '`, tier: `' . $variant_name . '`'); - $subscription = Subscription::updateOrCreate([ - 'team_id' => $team_id, - ], [ - 'lemon_subscription_id' => $subscription_id, - 'lemon_customer_id' => $customer_id, - 'lemon_order_id' => $order_id, - 'lemon_product_id' => $product_id, - 'lemon_variant_id' => $variant_id, - 'lemon_status' => $status, - 'lemon_variant_name' => $variant_name, - 'lemon_trial_ends_at' => $trial_ends_at, - 'lemon_renews_at' => $renews_at, - 'lemon_ends_at' => $ends_at, - 'lemon_update_payment_menthod_url' => $update_payment_method, - ]); - break; - case 'subscription_cancelled': - case 'subscription_paused': - case 'subscription_expired': - $subscription = Subscription::where('team_id', $team_id)->where('lemon_order_id', $order_id)->first(); - if ($subscription) { - send_internal_notification("LemonSqueezy Event (`$event`): " . $subscription_id . ' for team ' . $team_id . ' with status ' . $status); - $subscription->update([ - 'lemon_status' => $status, - 'lemon_trial_ends_at' => $trial_ends_at, - 'lemon_renews_at' => $renews_at, - 'lemon_ends_at' => $ends_at, - 'lemon_update_payment_menthod_url' => $update_payment_method, - ]); - } - break; - } +// $webhook = Webhook::create([ +// 'type' => 'lemonsqueezy', +// 'payload' => $payload, +// ]); +// $event = data_get($payload, 'meta.event_name'); +// ray('Subscription event: ' . $event); +// $email = data_get($payload, 'data.attributes.user_email'); +// $team_id = data_get($payload, 'meta.custom_data.team_id'); +// if (is_null($team_id) || empty($team_id)) { +// throw new Exception('No team_id found in webhook payload.'); +// } +// $subscription_id = data_get($payload, 'data.id'); +// $order_id = data_get($payload, 'data.attributes.order_id'); +// $product_id = data_get($payload, 'data.attributes.product_id'); +// $variant_id = data_get($payload, 'data.attributes.variant_id'); +// $variant_name = data_get($payload, 'data.attributes.variant_name'); +// $customer_id = data_get($payload, 'data.attributes.customer_id'); +// $status = data_get($payload, 'data.attributes.status'); +// $trial_ends_at = data_get($payload, 'data.attributes.trial_ends_at'); +// $renews_at = data_get($payload, 'data.attributes.renews_at'); +// $ends_at = data_get($payload, 'data.attributes.ends_at'); +// $update_payment_method = data_get($payload, 'data.attributes.urls.update_payment_method'); +// $team = Team::find($team_id); +// $found = $team->members->where('email', $email)->first(); +// if (!$found->isAdmin()) { +// throw new Exception("User {$email} is not an admin or owner of team {$team->id}."); +// } +// switch ($event) { +// case 'subscription_created': +// case 'subscription_updated': +// case 'subscription_resumed': +// case 'subscription_unpaused': +// send_internal_notification("LemonSqueezy Event (`$event`): `" . $email . '` with status `' . $status . '`, tier: `' . $variant_name . '`'); +// $subscription = Subscription::updateOrCreate([ +// 'team_id' => $team_id, +// ], [ +// 'lemon_subscription_id' => $subscription_id, +// 'lemon_customer_id' => $customer_id, +// 'lemon_order_id' => $order_id, +// 'lemon_product_id' => $product_id, +// 'lemon_variant_id' => $variant_id, +// 'lemon_status' => $status, +// 'lemon_variant_name' => $variant_name, +// 'lemon_trial_ends_at' => $trial_ends_at, +// 'lemon_renews_at' => $renews_at, +// 'lemon_ends_at' => $ends_at, +// 'lemon_update_payment_menthod_url' => $update_payment_method, +// ]); +// break; +// case 'subscription_cancelled': +// case 'subscription_paused': +// case 'subscription_expired': +// $subscription = Subscription::where('team_id', $team_id)->where('lemon_order_id', $order_id)->first(); +// if ($subscription) { +// send_internal_notification("LemonSqueezy Event (`$event`): " . $subscription_id . ' for team ' . $team_id . ' with status ' . $status); +// $subscription->update([ +// 'lemon_status' => $status, +// 'lemon_trial_ends_at' => $trial_ends_at, +// 'lemon_renews_at' => $renews_at, +// 'lemon_ends_at' => $ends_at, +// 'lemon_update_payment_menthod_url' => $update_payment_method, +// ]); +// } +// break; +// } - $webhook->update([ - 'status' => 'success', - ]); - } catch (Exception $e) { - ray($e->getMessage()); - send_internal_notification('Subscription webhook failed: ' . $e->getMessage()); - $webhook->update([ - 'status' => 'failed', - 'failure_reason' => $e->getMessage(), - ]); - } finally { - return response('OK'); - } -}); +// $webhook->update([ +// 'status' => 'success', +// ]); +// } catch (Exception $e) { +// ray($e->getMessage()); +// send_internal_notification('Subscription webhook failed: ' . $e->getMessage()); +// $webhook->update([ +// 'status' => 'failed', +// 'failure_reason' => $e->getMessage(), +// ]); +// } finally { +// return response('OK'); +// } +// });