outcome; } public function needs_customer_action(): bool { return Charge_Outcome::RequiresAction === $this->outcome; } /** * A definitive no from the gateway. Only these bump attempt_group, so only these * cause the next attempt to use a fresh idempotency key. */ public function is_definitive_decline(): bool { return in_array( $this->outcome, array( Charge_Outcome::SoftDecline, Charge_Outcome::HardDecline ), true ); } public function is_pending(): bool { return Charge_Outcome::Pending === $this->outcome; } /** * Unknown outcome — safe to re-enqueue, never safe to re-key. */ public function is_transient(): bool { return Charge_Outcome::GatewayError === $this->outcome; } public function should_enter_dunning(): bool { return Charge_Outcome::SoftDecline === $this->outcome; } public function describe(): string { return trim( $this->outcome->value . ( $this->code ? " ({$this->code})" : '' ) . ( $this->message ? ": {$this->message}" : '' ) ); } }