feat: implement comprehensive multi-provider payment processing system
- 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
This commit is contained in:
126
app/Filament/Widgets/SubscriptionMetrics.php
Normal file
126
app/Filament/Widgets/SubscriptionMetrics.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\Subscription;
|
||||
use Filament\Widgets\ChartWidget;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SubscriptionMetrics extends ChartWidget
|
||||
{
|
||||
protected static ?int $sort = 2;
|
||||
|
||||
protected int|string|array $columnSpan = 'full';
|
||||
|
||||
public function getHeading(): string
|
||||
{
|
||||
return 'Subscription Metrics';
|
||||
}
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$period = $this->getPeriod();
|
||||
|
||||
$subscriptionsByProvider = $this->getSubscriptionsByProvider($period);
|
||||
$subscriptionsByStatus = $this->getSubscriptionsByStatus();
|
||||
$monthlyTrend = $this->getMonthlySubscriptionTrend();
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Subscriptions by Provider',
|
||||
'data' => array_values($subscriptionsByProvider),
|
||||
'backgroundColor' => [
|
||||
'rgba(59, 130, 246, 0.8)', // blue
|
||||
'rgba(34, 197, 94, 0.8)', // green
|
||||
'rgba(168, 85, 247, 0.8)', // purple
|
||||
'rgba(251, 146, 60, 0.8)', // orange
|
||||
'rgba(107, 114, 128, 0.8)', // gray
|
||||
'rgba(236, 72, 153, 0.8)', // pink
|
||||
],
|
||||
'borderColor' => [
|
||||
'rgba(59, 130, 246, 1)',
|
||||
'rgba(34, 197, 94, 1)',
|
||||
'rgba(168, 85, 247, 1)',
|
||||
'rgba(251, 146, 60, 1)',
|
||||
'rgba(107, 114, 128, 1)',
|
||||
'rgba(236, 72, 153, 1)',
|
||||
],
|
||||
],
|
||||
],
|
||||
'labels' => array_keys($subscriptionsByProvider),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'doughnut';
|
||||
}
|
||||
|
||||
protected function getOptions(): array
|
||||
{
|
||||
return [
|
||||
'responsive' => true,
|
||||
'plugins' => [
|
||||
'legend' => [
|
||||
'position' => 'bottom',
|
||||
],
|
||||
'tooltip' => [
|
||||
'callbacks' => [
|
||||
'label' => 'function(context) {
|
||||
const label = context.label || "";
|
||||
const value = context.parsed || 0;
|
||||
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
||||
const percentage = ((value / total) * 100).toFixed(1);
|
||||
return label + ": " + value + " (" + percentage + "%)";
|
||||
}',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function getPeriod(): string
|
||||
{
|
||||
return 'last_30_days'; // Could be made configurable
|
||||
}
|
||||
|
||||
private function getSubscriptionsByProvider(string $period): array
|
||||
{
|
||||
$query = Subscription::query();
|
||||
|
||||
if ($period === 'last_30_days') {
|
||||
$query->where('created_at', '>=', now()->subDays(30));
|
||||
}
|
||||
|
||||
return $query
|
||||
->select('provider', DB::raw('count(*) as count'))
|
||||
->groupBy('provider')
|
||||
->orderBy('count', 'desc')
|
||||
->pluck('count', 'provider')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private function getSubscriptionsByStatus(): array
|
||||
{
|
||||
return Subscription::query()
|
||||
->select('status', DB::raw('count(*) as count'))
|
||||
->groupBy('status')
|
||||
->orderBy('count', 'desc')
|
||||
->pluck('count', 'status')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private function getMonthlySubscriptionTrend(): array
|
||||
{
|
||||
return Subscription::query()
|
||||
->select(
|
||||
DB::raw("strftime('%Y-%m', subscriptions.created_at) as month"),
|
||||
DB::raw('count(*) as count')
|
||||
)
|
||||
->groupBy('month')
|
||||
->orderBy('month')
|
||||
->pluck('count', 'month')
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user