feat(notifications): implement comprehensive telegram notifications for payment providers

- Add NotifyMe trait with centralized Telegram bot integration
  - Implement webhook notifications for Polar, OxaPay, and ActivationKey providers
  - Add subscription lifecycle notifications (create, activate, cancel, pause, resume)
  - Enhance Polar webhook processing with user context and error handling
  - Fix subscription.updated and subscription.canceled webhook column errors
  - Add idempotent webhook processing to prevent duplicate handling
This commit is contained in:
idevakk
2025-12-08 09:25:19 -08:00
parent 8d8cd44ea5
commit 0d33c57b32
5 changed files with 654 additions and 47 deletions

View File

@@ -7,6 +7,7 @@ use App\Models\ActivationKey;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\User;
use App\NotifyMe;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
@@ -14,6 +15,8 @@ use Illuminate\Support\Str;
class ActivationKeyProvider implements PaymentProviderContract
{
use NotifyMe;
protected array $config;
public function __construct(array $config = [])
@@ -74,6 +77,9 @@ class ActivationKeyProvider implements PaymentProviderContract
DB::commit();
// Notify activation key generated
$this->notifyActivationKeyGenerated($user, $plan, $activationKey);
return [
'provider_subscription_id' => $keyRecord->id,
'status' => 'pending_activation',
@@ -106,6 +112,9 @@ class ActivationKeyProvider implements PaymentProviderContract
'cancellation_reason' => $reason,
]);
// Notify subscription cancelled
$this->notifySubscriptionCancelled($subscription, $reason ?: 'Manual cancellation');
return true;
} catch (\Exception $e) {
@@ -381,6 +390,10 @@ class ActivationKeyProvider implements PaymentProviderContract
DB::commit();
// Notify activation key redeemed and subscription activated
$this->notifyActivationKeyRedeemed($user, $plan, $activationKey);
$this->notifySubscriptionActivated($user, $plan, $subscription);
return [
'success' => true,
'subscription_id' => $subscription->id,
@@ -494,4 +507,79 @@ class ActivationKeyProvider implements PaymentProviderContract
{
throw new \Exception('Import to activation keys not implemented');
}
// Notification methods
protected function notifyActivationKeyGenerated(User $user, Plan $plan, string $activationKey): void
{
$message = "🔑 ACTIVATION KEY GENERATED\n".
"👤 User: {$user->name} ({$user->email})\n".
"📋 Plan: {$plan->name}\n".
'💰 Price: $'.number_format($plan->price, 2)."\n".
"🏪 Provider: Activation Key\n".
"🔑 Key: {$activationKey}\n".
'📅 Generated: '.now()->format('Y-m-d H:i:s');
$this->sendTelegramNotification($message);
}
protected function notifyActivationKeyRedeemed(User $user, Plan $plan, string $activationKey): void
{
$message = "✅ ACTIVATION KEY REDEEMED\n".
"👤 User: {$user->name} ({$user->email})\n".
"📋 Plan: {$plan->name}\n".
"🏪 Provider: Activation Key\n".
"🔑 Key: {$activationKey}\n".
'📅 Redeemed: '.now()->format('Y-m-d H:i:s');
$this->sendTelegramNotification($message);
}
protected function notifySubscriptionActivated(User $user, Plan $plan, Subscription $subscription): void
{
$message = "🎉 SUBSCRIPTION ACTIVATED\n".
"👤 User: {$user->name} ({$user->email})\n".
"📋 Plan: {$plan->name}\n".
"🏪 Provider: Activation Key\n".
"🔄 Subscription ID: {$subscription->id}\n".
'📅 Activated: '.now()->format('Y-m-d H:i:s');
$this->sendTelegramNotification($message);
}
protected function notifyProviderError(string $operation, string $error, array $context = []): void
{
$contextStr = '';
if (! empty($context)) {
$contextStr = '📝 Details: '.json_encode(array_slice($context, 0, 3, true), JSON_UNESCAPED_SLASHES)."\n";
if (count($context) > 3) {
$contextStr .= '📝 Additional: '.(count($context) - 3).' more items'."\n";
}
}
$message = "🚨 PROVIDER ERROR\n".
"🏪 Provider: Activation Key\n".
"📡 Operation: {$operation}\n".
"💥 Error: {$error}\n".
$contextStr.
'⏰ Time: '.now()->format('Y-m-d H:i:s');
$this->sendTelegramNotification($message);
}
protected function notifySubscriptionCancelled(Subscription $subscription, string $reason): void
{
$user = $subscription->user;
$plan = $subscription->plan;
$message = "❌ SUBSCRIPTION CANCELLED\n".
"👤 User: {$user->name} ({$user->email})\n".
"📋 Plan: {$plan->name}\n".
"🏪 Provider: Activation Key\n".
"💭 Reason: {$reason}\n".
"🆔 Subscription ID: {$subscription->id}\n".
($subscription->ends_at ? '📅 Effective: '.$subscription->ends_at->format('Y-m-d')."\n" : '').
'⏰ Cancelled: '.now()->format('Y-m-d H:i:s');
$this->sendTelegramNotification($message);
}
}