php-commerce/Data/PaymentResult.php
Snider 51f9595797 feat(commerce): implement DunningService with 5 methods + DunningSchedule DTO (#860)
- schedule(subscription) → DunningSchedule (retry dates + suspension date)
- retry(invoice) → PaymentResult
- suspend(subscription) → void
- notify(subscription, stage) → void (event-driven per dunning stage)
- recover(subscription) → void (clears dunning after payment)

Data/DunningSchedule.php + Data/PaymentResult.php as readonly DTOs.
Pest tests _Good/_Bad/_Ugly per AX-10 for all 5 methods.
pint/pest skipped (vendor binaries missing in sandbox).

Co-authored-by: Codex <noreply@openai.com>
Closes tasks.lthn.sh/view.php?id=860
2026-04-25 04:57:33 +01:00

51 lines
1.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Data;
use Carbon\Carbon;
use Core\Mod\Commerce\Models\Payment;
/**
* Result from an attempted automatic invoice payment retry.
*/
readonly class PaymentResult
{
public function __construct(
public bool $successful,
public ?Payment $payment = null,
public ?string $reason = null,
public int $attempts = 0,
public ?Carbon $nextRetryAt = null,
) {}
public static function successful(?Payment $payment = null, int $attempts = 0): self
{
return new self(
successful: true,
payment: $payment,
attempts: $attempts,
);
}
public static function failed(string $reason, int $attempts = 0, ?Carbon $nextRetryAt = null): self
{
return new self(
successful: false,
reason: $reason,
attempts: $attempts,
nextRetryAt: $nextRetryAt,
);
}
public function succeeded(): bool
{
return $this->successful;
}
public function isFailed(): bool
{
return ! $this->successful;
}
}