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:
123
app/Filament/Widgets/RevenueMetrics.php
Normal file
123
app/Filament/Widgets/RevenueMetrics.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\Subscription;
|
||||
use Filament\Widgets\ChartWidget;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class RevenueMetrics extends ChartWidget
|
||||
{
|
||||
protected static ?int $sort = 1;
|
||||
|
||||
protected int|string|array $columnSpan = 'full';
|
||||
|
||||
public function getHeading(): string
|
||||
{
|
||||
return 'Revenue Trends';
|
||||
}
|
||||
|
||||
protected function getData(): array
|
||||
{
|
||||
$monthlyRevenue = $this->getMonthlyRevenueTrend();
|
||||
$mrrByProvider = $this->getMRRByProvider();
|
||||
|
||||
return [
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Monthly Revenue',
|
||||
'data' => array_values($monthlyRevenue),
|
||||
'borderColor' => 'rgba(34, 197, 94, 1)',
|
||||
'backgroundColor' => 'rgba(34, 197, 94, 0.1)',
|
||||
'fill' => true,
|
||||
'tension' => 0.4,
|
||||
],
|
||||
[
|
||||
'label' => 'MRR by Provider',
|
||||
'data' => array_values($mrrByProvider),
|
||||
'borderColor' => 'rgba(59, 130, 246, 1)',
|
||||
'backgroundColor' => 'rgba(59, 130, 246, 0.1)',
|
||||
'fill' => true,
|
||||
'tension' => 0.4,
|
||||
],
|
||||
],
|
||||
'labels' => array_keys($monthlyRevenue),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'line';
|
||||
}
|
||||
|
||||
protected function getOptions(): array
|
||||
{
|
||||
return [
|
||||
'responsive' => true,
|
||||
'interaction' => [
|
||||
'intersect' => false,
|
||||
'mode' => 'index',
|
||||
],
|
||||
'plugins' => [
|
||||
'legend' => [
|
||||
'position' => 'top',
|
||||
],
|
||||
'tooltip' => [
|
||||
'callbacks' => [
|
||||
'label' => 'function(context) {
|
||||
let label = context.dataset.label || "";
|
||||
if (label) {
|
||||
label += ": ";
|
||||
}
|
||||
if (context.parsed.y !== null) {
|
||||
label += "$" + context.parsed.y.toFixed(2);
|
||||
}
|
||||
return label;
|
||||
}',
|
||||
],
|
||||
],
|
||||
],
|
||||
'scales' => [
|
||||
'y' => [
|
||||
'beginAtZero' => true,
|
||||
'ticks' => [
|
||||
'callback' => 'function(value) {
|
||||
return "$" + value;
|
||||
}',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function getMonthlyRevenueTrend(): array
|
||||
{
|
||||
return Subscription::query()
|
||||
->select(
|
||||
DB::raw("strftime('%Y-%m', subscriptions.created_at) as month"),
|
||||
DB::raw('SUM(plans.price) as revenue')
|
||||
)
|
||||
->join('plans', 'subscriptions.plan_id', '=', 'plans.id')
|
||||
->where('subscriptions.status', 'active')
|
||||
->where('subscriptions.created_at', '>=', now()->subMonths(12))
|
||||
->groupBy('month')
|
||||
->orderBy('month')
|
||||
->pluck('revenue', 'month')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
private function getMRRByProvider(): array
|
||||
{
|
||||
return Subscription::query()
|
||||
->select(
|
||||
'provider',
|
||||
DB::raw('SUM(plans.price) as mrr')
|
||||
)
|
||||
->join('plans', 'subscriptions.plan_id', '=', 'plans.id')
|
||||
->where('subscriptions.status', 'active')
|
||||
->groupBy('provider')
|
||||
->orderBy('mrr', 'desc')
|
||||
->pluck('mrr', 'provider')
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user