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:
idevakk
2025-11-19 09:37:00 -08:00
parent 0560016f33
commit 27ac13948c
83 changed files with 15613 additions and 103 deletions

View File

@@ -0,0 +1,138 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class SubscriptionChange extends Model
{
protected $fillable = [
'subscription_id',
'user_id',
'change_type',
'change_description',
'old_values',
'new_values',
'reason',
'effective_at',
'processed_at',
'is_processed',
'metadata',
];
protected $casts = [
'old_values' => 'array',
'new_values' => 'array',
'effective_at' => 'datetime',
'processed_at' => 'datetime',
'is_processed' => 'boolean',
'metadata' => 'array',
];
protected $dates = [
'effective_at',
'processed_at',
];
/**
* Relationships
*/
public function subscription()
{
return $this->belongsTo(Subscription::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
/**
* Get human-readable change type
*/
public function getChangeTypeLabelAttribute(): string
{
return [
'plan_change' => 'Plan Change',
'cancellation' => 'Cancellation',
'pause' => 'Pause',
'resume' => 'Resume',
'migration' => 'Migration',
'provider_change' => 'Provider Change',
][$this->change_type] ?? ucfirst($this->change_type);
}
/**
* Mark as processed
*/
public function markAsProcessed(): void
{
$this->update([
'is_processed' => true,
'processed_at' => now(),
]);
}
/**
* Scope: By change type
*/
public function scopeByType($query, string $type)
{
return $query->where('change_type', $type);
}
/**
* Scope: Processed
*/
public function scopeProcessed($query)
{
return $query->where('is_processed', true);
}
/**
* Scope: Pending processing
*/
public function scopePending($query)
{
return $query->where('is_processed', false);
}
/**
* Scope: By user
*/
public function scopeByUser($query, $userId)
{
return $query->where('user_id', $userId);
}
/**
* Scope: Within date range
*/
public function scopeBetweenDates($query, $startDate, $endDate)
{
return $query->whereBetween('effective_at', [$startDate, $endDate]);
}
/**
* Create a subscription change record
*/
public static function createRecord(
Subscription $subscription,
string $changeType,
string $description,
?array $oldValues = null,
?array $newValues = null,
?string $reason = null
): self {
return static::create([
'subscription_id' => $subscription->id,
'user_id' => $subscription->user_id,
'change_type' => $changeType,
'change_description' => $description,
'old_values' => $oldValues,
'new_values' => $newValues,
'reason' => $reason,
'effective_at' => now(),
]);
}
}