cloud: send notification email if payment

This commit is contained in:
Andras Bacsai 2024-01-24 11:28:01 +01:00
parent 01f7b07fa3
commit 6ecb9c21ce
2 changed files with 141 additions and 126 deletions

View File

@ -5,13 +5,13 @@
<h1>Dashboard</h1>
<div class="subtitle">Your self-hosted environment</div>
@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"
viewBox="0 0 24 24">
<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" />
</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>
@endif
@if ($projects->count() === 0 && $servers->count() === 0)

View File

@ -1,5 +1,6 @@
<?php
use App\Jobs\SubscriptionInvoiceFailedJob;
use App\Jobs\SubscriptionTrialEndedJob;
use App\Jobs\SubscriptionTrialEndsSoonJob;
use App\Models\Application;
@ -593,6 +594,16 @@
'stripe_invoice_paid' => 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');
// }
// });