'integer', 'exp_year' => 'integer', 'expires_at' => 'datetime', 'is_default' => 'boolean', 'is_active' => 'boolean', 'meta' => 'object', ]; public function __construct(array $attributes = []) { parent::__construct($attributes); $this->table = config('shop.tables.payment_methods', 'payment_methods'); } /** * Get the payment provider identity that owns this payment method. */ public function paymentProviderIdentity(): BelongsTo { return $this->belongsTo(config('shop.models.payment_provider_identity', PaymentProviderIdentity::class)); } /** * Get the customer through the payment provider identity. */ public function customer() { return $this->paymentProviderIdentity->customer(); } /** * Check if this payment method is expired. */ public function isExpired(): bool { $now = now(); // Prefer explicit timestamp if provided (for non-card methods like crypto wallets) if ($this->expires_at) { return $now->isAfter($this->expires_at); } // Fallback to month/year for card-like methods if ($this->exp_month && $this->exp_year) { $expirationDate = now()->setYear($this->exp_year)->setMonth($this->exp_month)->endOfMonth(); return $now->isAfter($expirationDate); } return false; } /** * Get a formatted display name for the payment method. */ public function getDisplayNameAttribute(): string { if ($this->name) { return $this->name; } $parts = []; if ($this->brand) { $parts[] = ucfirst($this->brand); } if ($this->last_digits) { $parts[] = "ending in {$this->last_digits}"; } return implode(' ', $parts) ?: 'Payment Method'; } /** * Get a formatted expiration date. */ public function getFormattedExpirationAttribute(): ?string { if (!$this->exp_month || !$this->exp_year) { return null; } return sprintf('%02d/%d', $this->exp_month, $this->exp_year); } /** * Set this payment method as the default for its provider identity. */ public function setAsDefault(): self { // Remove default flag from all other payment methods for this provider identity static::where('payment_provider_identity_id', $this->payment_provider_identity_id) ->where('id', '!=', $this->id) ->update(['is_default' => false]); $this->is_default = true; $this->save(); return $this; } /** * Deactivate this payment method. */ public function deactivate(): self { $this->is_active = false; $this->save(); return $this; } /** * Activate this payment method. */ public function activate(): self { $this->is_active = true; $this->save(); return $this; } /** * Scope a query to only include active payment methods. */ public function scopeActive($query) { return $query->where('is_active', true); } /** * Scope a query to only include default payment methods. */ public function scopeDefault($query) { return $query->where('is_default', true); } /** * Scope a query to only include non-expired payment methods. */ public function scopeNotExpired($query) { return $query->where(function ($q) { $q->whereNull('exp_year') ->orWhere('exp_year', '>', now()->year) ->orWhere(function ($q2) { $q2->where('exp_year', now()->year) ->where('exp_month', '>=', now()->month); }); }); } /** * Create a new factory instance for the model. */ protected static function newFactory() { return PaymentMethodFactory::new(); } }