cloud: send notification email if payment
This commit is contained in:
parent
01f7b07fa3
commit
6ecb9c21ce
@ -5,13 +5,13 @@
|
|||||||
<h1>Dashboard</h1>
|
<h1>Dashboard</h1>
|
||||||
<div class="subtitle">Your self-hosted environment</div>
|
<div class="subtitle">Your self-hosted environment</div>
|
||||||
@if (request()->query->get('success'))
|
@if (request()->query->get('success'))
|
||||||
<div class="rounded alert alert-success">
|
<div class="text-white rounded alert alert-success">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
|
||||||
viewBox="0 0 24 24">
|
viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span>Your subscription has been activated! Welcome onboard!</span>
|
<span>Your subscription has been activated! Welcome onboard! <br>It could take a few seconds before your subscription is activated.<br> Please be patient.</span>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@if ($projects->count() === 0 && $servers->count() === 0)
|
@if ($projects->count() === 0 && $servers->count() === 0)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Jobs\SubscriptionInvoiceFailedJob;
|
||||||
use App\Jobs\SubscriptionTrialEndedJob;
|
use App\Jobs\SubscriptionTrialEndedJob;
|
||||||
use App\Jobs\SubscriptionTrialEndsSoonJob;
|
use App\Jobs\SubscriptionTrialEndsSoonJob;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
@ -593,6 +594,16 @@ Route::post('/payments/stripe/events', function () {
|
|||||||
'stripe_invoice_paid' => true,
|
'stripe_invoice_paid' => true,
|
||||||
]);
|
]);
|
||||||
break;
|
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':
|
case 'payment_intent.payment_failed':
|
||||||
$customerId = data_get($data, 'customer');
|
$customerId = data_get($data, 'customer');
|
||||||
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
|
||||||
@ -610,7 +621,11 @@ Route::post('/payments/stripe/events', function () {
|
|||||||
send_internal_notification('Subscription excluded.');
|
send_internal_notification('Subscription excluded.');
|
||||||
break;
|
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');
|
$trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended');
|
||||||
$cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end');
|
$cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end');
|
||||||
$alreadyCancelAtPeriodEnd = data_get($subscription, 'stripe_cancel_at_period_end');
|
$alreadyCancelAtPeriodEnd = data_get($subscription, 'stripe_cancel_at_period_end');
|
||||||
@ -703,129 +718,129 @@ Route::post('/payments/stripe/events', function () {
|
|||||||
return response($e->getMessage(), 400);
|
return response($e->getMessage(), 400);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Route::post('/payments/paddle/events', function () {
|
// Route::post('/payments/paddle/events', function () {
|
||||||
try {
|
// try {
|
||||||
$payload = request()->all();
|
// $payload = request()->all();
|
||||||
$signature = request()->header('Paddle-Signature');
|
// $signature = request()->header('Paddle-Signature');
|
||||||
$ts = Str::of($signature)->after('ts=')->before(';');
|
// $ts = Str::of($signature)->after('ts=')->before(';');
|
||||||
$h1 = Str::of($signature)->after('h1=');
|
// $h1 = Str::of($signature)->after('h1=');
|
||||||
$signedPayload = $ts->value . ':' . request()->getContent();
|
// $signedPayload = $ts->value . ':' . request()->getContent();
|
||||||
$verify = hash_hmac('sha256', $signedPayload, config('subscription.paddle_webhook_secret'));
|
// $verify = hash_hmac('sha256', $signedPayload, config('subscription.paddle_webhook_secret'));
|
||||||
if (!hash_equals($verify, $h1->value)) {
|
// if (!hash_equals($verify, $h1->value)) {
|
||||||
return response('Invalid signature.', 400);
|
// return response('Invalid signature.', 400);
|
||||||
}
|
// }
|
||||||
$eventType = data_get($payload, 'event_type');
|
// $eventType = data_get($payload, 'event_type');
|
||||||
$webhook = Webhook::create([
|
// $webhook = Webhook::create([
|
||||||
'type' => 'paddle',
|
// 'type' => 'paddle',
|
||||||
'payload' => $payload,
|
// 'payload' => $payload,
|
||||||
]);
|
// ]);
|
||||||
// TODO - Handle events
|
// // TODO - Handle events
|
||||||
switch ($eventType) {
|
// switch ($eventType) {
|
||||||
case 'subscription.activated':
|
// case 'subscription.activated':
|
||||||
}
|
// }
|
||||||
ray('Subscription event: ' . $eventType);
|
// ray('Subscription event: ' . $eventType);
|
||||||
$webhook->update([
|
// $webhook->update([
|
||||||
'status' => 'success',
|
// 'status' => 'success',
|
||||||
]);
|
// ]);
|
||||||
} catch (Exception $e) {
|
// } catch (Exception $e) {
|
||||||
ray($e->getMessage());
|
// ray($e->getMessage());
|
||||||
send_internal_notification('Subscription webhook failed: ' . $e->getMessage());
|
// send_internal_notification('Subscription webhook failed: ' . $e->getMessage());
|
||||||
$webhook->update([
|
// $webhook->update([
|
||||||
'status' => 'failed',
|
// 'status' => 'failed',
|
||||||
'failure_reason' => $e->getMessage(),
|
// 'failure_reason' => $e->getMessage(),
|
||||||
]);
|
// ]);
|
||||||
} finally {
|
// } finally {
|
||||||
return response('OK');
|
// return response('OK');
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
Route::post('/payments/lemon/events', function () {
|
// Route::post('/payments/lemon/events', function () {
|
||||||
try {
|
// try {
|
||||||
$secret = config('subscription.lemon_squeezy_webhook_secret');
|
// $secret = config('subscription.lemon_squeezy_webhook_secret');
|
||||||
$payload = request()->collect();
|
// $payload = request()->collect();
|
||||||
$hash = hash_hmac('sha256', $payload, $secret);
|
// $hash = hash_hmac('sha256', $payload, $secret);
|
||||||
$signature = request()->header('X-Signature');
|
// $signature = request()->header('X-Signature');
|
||||||
|
|
||||||
if (!hash_equals($hash, $signature)) {
|
// if (!hash_equals($hash, $signature)) {
|
||||||
return response('Invalid signature.', 400);
|
// return response('Invalid signature.', 400);
|
||||||
}
|
// }
|
||||||
|
|
||||||
$webhook = Webhook::create([
|
// $webhook = Webhook::create([
|
||||||
'type' => 'lemonsqueezy',
|
// 'type' => 'lemonsqueezy',
|
||||||
'payload' => $payload,
|
// 'payload' => $payload,
|
||||||
]);
|
// ]);
|
||||||
$event = data_get($payload, 'meta.event_name');
|
// $event = data_get($payload, 'meta.event_name');
|
||||||
ray('Subscription event: ' . $event);
|
// ray('Subscription event: ' . $event);
|
||||||
$email = data_get($payload, 'data.attributes.user_email');
|
// $email = data_get($payload, 'data.attributes.user_email');
|
||||||
$team_id = data_get($payload, 'meta.custom_data.team_id');
|
// $team_id = data_get($payload, 'meta.custom_data.team_id');
|
||||||
if (is_null($team_id) || empty($team_id)) {
|
// if (is_null($team_id) || empty($team_id)) {
|
||||||
throw new Exception('No team_id found in webhook payload.');
|
// throw new Exception('No team_id found in webhook payload.');
|
||||||
}
|
// }
|
||||||
$subscription_id = data_get($payload, 'data.id');
|
// $subscription_id = data_get($payload, 'data.id');
|
||||||
$order_id = data_get($payload, 'data.attributes.order_id');
|
// $order_id = data_get($payload, 'data.attributes.order_id');
|
||||||
$product_id = data_get($payload, 'data.attributes.product_id');
|
// $product_id = data_get($payload, 'data.attributes.product_id');
|
||||||
$variant_id = data_get($payload, 'data.attributes.variant_id');
|
// $variant_id = data_get($payload, 'data.attributes.variant_id');
|
||||||
$variant_name = data_get($payload, 'data.attributes.variant_name');
|
// $variant_name = data_get($payload, 'data.attributes.variant_name');
|
||||||
$customer_id = data_get($payload, 'data.attributes.customer_id');
|
// $customer_id = data_get($payload, 'data.attributes.customer_id');
|
||||||
$status = data_get($payload, 'data.attributes.status');
|
// $status = data_get($payload, 'data.attributes.status');
|
||||||
$trial_ends_at = data_get($payload, 'data.attributes.trial_ends_at');
|
// $trial_ends_at = data_get($payload, 'data.attributes.trial_ends_at');
|
||||||
$renews_at = data_get($payload, 'data.attributes.renews_at');
|
// $renews_at = data_get($payload, 'data.attributes.renews_at');
|
||||||
$ends_at = data_get($payload, 'data.attributes.ends_at');
|
// $ends_at = data_get($payload, 'data.attributes.ends_at');
|
||||||
$update_payment_method = data_get($payload, 'data.attributes.urls.update_payment_method');
|
// $update_payment_method = data_get($payload, 'data.attributes.urls.update_payment_method');
|
||||||
$team = Team::find($team_id);
|
// $team = Team::find($team_id);
|
||||||
$found = $team->members->where('email', $email)->first();
|
// $found = $team->members->where('email', $email)->first();
|
||||||
if (!$found->isAdmin()) {
|
// if (!$found->isAdmin()) {
|
||||||
throw new Exception("User {$email} is not an admin or owner of team {$team->id}.");
|
// throw new Exception("User {$email} is not an admin or owner of team {$team->id}.");
|
||||||
}
|
// }
|
||||||
switch ($event) {
|
// switch ($event) {
|
||||||
case 'subscription_created':
|
// case 'subscription_created':
|
||||||
case 'subscription_updated':
|
// case 'subscription_updated':
|
||||||
case 'subscription_resumed':
|
// case 'subscription_resumed':
|
||||||
case 'subscription_unpaused':
|
// case 'subscription_unpaused':
|
||||||
send_internal_notification("LemonSqueezy Event (`$event`): `" . $email . '` with status `' . $status . '`, tier: `' . $variant_name . '`');
|
// send_internal_notification("LemonSqueezy Event (`$event`): `" . $email . '` with status `' . $status . '`, tier: `' . $variant_name . '`');
|
||||||
$subscription = Subscription::updateOrCreate([
|
// $subscription = Subscription::updateOrCreate([
|
||||||
'team_id' => $team_id,
|
// 'team_id' => $team_id,
|
||||||
], [
|
// ], [
|
||||||
'lemon_subscription_id' => $subscription_id,
|
// 'lemon_subscription_id' => $subscription_id,
|
||||||
'lemon_customer_id' => $customer_id,
|
// 'lemon_customer_id' => $customer_id,
|
||||||
'lemon_order_id' => $order_id,
|
// 'lemon_order_id' => $order_id,
|
||||||
'lemon_product_id' => $product_id,
|
// 'lemon_product_id' => $product_id,
|
||||||
'lemon_variant_id' => $variant_id,
|
// 'lemon_variant_id' => $variant_id,
|
||||||
'lemon_status' => $status,
|
// 'lemon_status' => $status,
|
||||||
'lemon_variant_name' => $variant_name,
|
// 'lemon_variant_name' => $variant_name,
|
||||||
'lemon_trial_ends_at' => $trial_ends_at,
|
// 'lemon_trial_ends_at' => $trial_ends_at,
|
||||||
'lemon_renews_at' => $renews_at,
|
// 'lemon_renews_at' => $renews_at,
|
||||||
'lemon_ends_at' => $ends_at,
|
// 'lemon_ends_at' => $ends_at,
|
||||||
'lemon_update_payment_menthod_url' => $update_payment_method,
|
// 'lemon_update_payment_menthod_url' => $update_payment_method,
|
||||||
]);
|
// ]);
|
||||||
break;
|
// break;
|
||||||
case 'subscription_cancelled':
|
// case 'subscription_cancelled':
|
||||||
case 'subscription_paused':
|
// case 'subscription_paused':
|
||||||
case 'subscription_expired':
|
// case 'subscription_expired':
|
||||||
$subscription = Subscription::where('team_id', $team_id)->where('lemon_order_id', $order_id)->first();
|
// $subscription = Subscription::where('team_id', $team_id)->where('lemon_order_id', $order_id)->first();
|
||||||
if ($subscription) {
|
// if ($subscription) {
|
||||||
send_internal_notification("LemonSqueezy Event (`$event`): " . $subscription_id . ' for team ' . $team_id . ' with status ' . $status);
|
// send_internal_notification("LemonSqueezy Event (`$event`): " . $subscription_id . ' for team ' . $team_id . ' with status ' . $status);
|
||||||
$subscription->update([
|
// $subscription->update([
|
||||||
'lemon_status' => $status,
|
// 'lemon_status' => $status,
|
||||||
'lemon_trial_ends_at' => $trial_ends_at,
|
// 'lemon_trial_ends_at' => $trial_ends_at,
|
||||||
'lemon_renews_at' => $renews_at,
|
// 'lemon_renews_at' => $renews_at,
|
||||||
'lemon_ends_at' => $ends_at,
|
// 'lemon_ends_at' => $ends_at,
|
||||||
'lemon_update_payment_menthod_url' => $update_payment_method,
|
// 'lemon_update_payment_menthod_url' => $update_payment_method,
|
||||||
]);
|
// ]);
|
||||||
}
|
// }
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
$webhook->update([
|
// $webhook->update([
|
||||||
'status' => 'success',
|
// 'status' => 'success',
|
||||||
]);
|
// ]);
|
||||||
} catch (Exception $e) {
|
// } catch (Exception $e) {
|
||||||
ray($e->getMessage());
|
// ray($e->getMessage());
|
||||||
send_internal_notification('Subscription webhook failed: ' . $e->getMessage());
|
// send_internal_notification('Subscription webhook failed: ' . $e->getMessage());
|
||||||
$webhook->update([
|
// $webhook->update([
|
||||||
'status' => 'failed',
|
// 'status' => 'failed',
|
||||||
'failure_reason' => $e->getMessage(),
|
// 'failure_reason' => $e->getMessage(),
|
||||||
]);
|
// ]);
|
||||||
} finally {
|
// } finally {
|
||||||
return response('OK');
|
// return response('OK');
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user