'decimal:2', 'gateway_response' => 'array', ]; // Relationships public function payment(): BelongsTo { return $this->belongsTo(Payment::class); } public function initiator(): BelongsTo { return $this->belongsTo(User::class, 'initiated_by'); } public function creditNote(): HasOne { return $this->hasOne(CreditNote::class); } // Status helpers public function isPending(): bool { return $this->status === 'pending'; } public function isSucceeded(): bool { return $this->status === 'succeeded'; } public function isFailed(): bool { return $this->status === 'failed'; } public function isCancelled(): bool { return $this->status === 'cancelled'; } // Actions public function markAsSucceeded(?string $gatewayRefundId = null): void { $this->update([ 'status' => 'succeeded', 'gateway_refund_id' => $gatewayRefundId ?? $this->gateway_refund_id, ]); // Update payment refunded amount $this->payment->recordRefund($this->amount); } public function markAsFailed(?array $response = null): void { $this->update([ 'status' => 'failed', 'gateway_response' => $response, ]); } public function cancel(): void { $this->update(['status' => 'cancelled']); } // Scopes public function scopePending($query) { return $query->where('status', 'pending'); } public function scopeSucceeded($query) { return $query->where('status', 'succeeded'); } // Reason helpers public function getReasonLabel(): string { return match ($this->reason) { 'duplicate' => 'Duplicate payment', 'fraudulent' => 'Fraudulent transaction', 'requested_by_customer' => 'Customer request', 'other' => 'Other', default => 'Unknown', }; } public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->logOnly(['status', 'amount', 'reason']) ->logOnlyDirty() ->dontSubmitEmptyLogs() ->setDescriptionForEvent(fn (string $eventName) => "Refund {$eventName}"); } }