outcome, $this->providerRef, $this->reason, $this->retryable, $this->needsAttention, $status, $ms); } /** * It landed. `$providerRef` is the remote id, where the vendor gives one. */ public static function success(?string $providerRef = null): self { return new self(PushOutcome::Success, $providerRef, null, false); } /** * There was nothing to send — and that is an ordinary outcome, not a * failure. See {@see PushOutcome::Skipped}. */ public static function skipped(string $why): self { return new self(PushOutcome::Skipped, null, $why, false); } /** * The vendor is having a bad time: a timeout, a 500, a revoked key. * Retrying the whole of `push()` is safe, because `push()` is idempotent * — for a webhook, because every attempt carries one `deliveryId` for the * receiver to dedupe on (ADR 0148). */ public static function retryable(string $error): self { return new self(PushOutcome::Failed, null, $error, true); } /** * This Lead will never land at this Destination, however many times it is * tried. Invisible to health, and recorded in the failure ring. */ public static function terminal(string $error): self { return new self(PushOutcome::Failed, null, $error, false); } /** Account or route needs merchant repair; automatic retries cannot fix it. */ public static function attention(string $error): self { return new self(PushOutcome::Failed, null, $error, false, true); } public function isFailure(): bool { return $this->outcome === PushOutcome::Failed; } }