- Add unified payment provider architecture with contract-based design - Implement 6 payment providers: Stripe, Lemon Squeezy, Polar, Oxapay, Crypto, Activation Keys - Create subscription management with lifecycle handling (create, cancel, pause, resume, update) - Add coupon system with usage tracking and trial extensions - Build Filament admin resources for payment providers, subscriptions, coupons, and trials - Implement payment orchestration service with provider registry and configuration management - Add comprehensive payment logging and webhook handling for all providers - Create customer analytics dashboard with revenue, churn, and lifetime value metrics - Add subscription migration service for provider switching - Include extensive test coverage for all payment functionality
140 lines
5.1 KiB
PHP
140 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Widgets;
|
|
|
|
use App\Models\Coupon;
|
|
use App\Models\CouponUsage;
|
|
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
|
use Filament\Widgets\StatsOverviewWidget\Stat;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class CouponPerformanceMetrics extends BaseWidget
|
|
{
|
|
protected static ?int $sort = 3;
|
|
|
|
protected function getStats(): array
|
|
{
|
|
$totalCoupons = Coupon::count();
|
|
$activeCoupons = Coupon::where('is_active', true)->count();
|
|
$totalUsages = CouponUsage::count();
|
|
$totalDiscount = CouponUsage::sum('discount_amount');
|
|
|
|
$conversionRate = $this->getCouponConversionRate();
|
|
$avgDiscountValue = $totalUsages > 0 ? $totalDiscount / $totalUsages : 0;
|
|
$topPerformingCoupon = $this->getTopPerformingCoupon();
|
|
$monthlyUsage = $this->getMonthlyUsage();
|
|
|
|
return [
|
|
Stat::make('Total Coupons', $totalCoupons)
|
|
->description($activeCoupons.' active')
|
|
->descriptionIcon('heroicon-o-ticket')
|
|
->color('primary'),
|
|
|
|
Stat::make('Total Usages', $totalUsages)
|
|
->description($this->getUsageGrowthRate())
|
|
->descriptionIcon($this->getUsageGrowthIcon())
|
|
->color($this->getUsageGrowthColor()),
|
|
|
|
Stat::make('Total Discount Given', '$'.number_format($totalDiscount, 2))
|
|
->description('Total value of discounts')
|
|
->descriptionIcon('heroicon-o-gift')
|
|
->color('success'),
|
|
|
|
Stat::make('Conversion Rate', $conversionRate.'%')
|
|
->description('Coupon to subscription rate')
|
|
->descriptionIcon('heroicon-o-chart-bar')
|
|
->color('info'),
|
|
|
|
Stat::make('Avg Discount Value', '$'.number_format($avgDiscountValue, 2))
|
|
->description('Per usage average')
|
|
->descriptionIcon('heroicon-o-calculator')
|
|
->color('warning'),
|
|
|
|
Stat::make('Top Performing', $topPerformingCoupon ? ($topPerformingCoupon['name'] ?? 'N/A') : 'N/A')
|
|
->description($topPerformingCoupon ? ($topPerformingCoupon['usages'] ?? 0).' usages' : '0 usages')
|
|
->descriptionIcon('heroicon-o-trophy')
|
|
->color('purple'),
|
|
|
|
Stat::make('Monthly Usage', $monthlyUsage)
|
|
->description('This month')
|
|
->descriptionIcon('heroicon-o-calendar')
|
|
->color('success'),
|
|
|
|
Stat::make('Revenue Impact', '$'.number_format($this->calculateRevenueImpact(), 2))
|
|
->description('Estimated new revenue')
|
|
->descriptionIcon('heroicon-o-currency-dollar')
|
|
->color('success'),
|
|
];
|
|
}
|
|
|
|
private function getCouponConversionRate(): string
|
|
{
|
|
$totalCoupons = Coupon::count();
|
|
if ($totalCoupons == 0) {
|
|
return '0';
|
|
}
|
|
|
|
$usedCoupons = Coupon::whereHas('usages')->count();
|
|
|
|
return number_format(($usedCoupons / $totalCoupons) * 100, 1);
|
|
}
|
|
|
|
private function getUsageGrowthRate(): string
|
|
{
|
|
$currentMonth = CouponUsage::whereMonth('created_at', now()->month)->count();
|
|
$previousMonth = CouponUsage::whereMonth('created_at', now()->subMonth()->month)->count();
|
|
|
|
if ($previousMonth == 0) {
|
|
return 'New this month';
|
|
}
|
|
|
|
$growth = (($currentMonth - $previousMonth) / $previousMonth) * 100;
|
|
|
|
return $growth >= 0 ? "+{$growth}%" : "{$growth}%";
|
|
}
|
|
|
|
private function getUsageGrowthIcon(): string
|
|
{
|
|
$currentMonth = CouponUsage::whereMonth('created_at', now()->month)->count();
|
|
$previousMonth = CouponUsage::whereMonth('created_at', now()->subMonth()->month)->count();
|
|
|
|
return $currentMonth > $previousMonth ? 'heroicon-o-arrow-trending-up' : 'heroicon-o-arrow-trending-down';
|
|
}
|
|
|
|
private function getUsageGrowthColor(): string
|
|
{
|
|
$currentMonth = CouponUsage::whereMonth('created_at', now()->month)->count();
|
|
$previousMonth = CouponUsage::whereMonth('created_at', now()->subMonth()->month)->count();
|
|
|
|
return $currentMonth > $previousMonth ? 'success' : 'danger';
|
|
}
|
|
|
|
private function getTopPerformingCoupon(): ?array
|
|
{
|
|
return CouponUsage::query()
|
|
->select('coupon_id', DB::raw('count(*) as usages, sum(discount_amount) as total_discount'))
|
|
->with('coupon:id,code')
|
|
->groupBy('coupon_id')
|
|
->orderBy('usages', 'desc')
|
|
->first()
|
|
?->toArray();
|
|
}
|
|
|
|
private function getMonthlyUsage(): int
|
|
{
|
|
return CouponUsage::whereMonth('created_at', now()->month)->count();
|
|
}
|
|
|
|
private function calculateRevenueImpact(): float
|
|
{
|
|
// Estimate revenue from coupons that led to subscriptions
|
|
return CouponUsage::query()
|
|
->whereHas('subscription', function ($query) {
|
|
$query->where('status', 'active');
|
|
})
|
|
->join('subscriptions', 'coupon_usages.subscription_id', '=', 'subscriptions.id')
|
|
->join('plans', 'subscriptions.plan_id', '=', 'plans.id')
|
|
->sum('plans.price');
|
|
}
|
|
}
|