At-least-once means the consumer must be idempotent

Every broker worth using guarantees at-least-once delivery, and the phrase is a warning rather than a feature: a message can and will arrive twice. Exactly-once is not available at any price the system can pay.

public function handle(OrderPlaced $event)
{
    // natural key, unique constraint, insert wins or throws
    if (! $this->invoices->createIfAbsent($event->orderId)) {
        return;   // already done, and that is fine
    }

    $this->send($event->orderId);
}

The duplicate arrives because the consumer crashed after doing the work and before acknowledging — the broker has no way to know which. The fix is always the same: find a natural key and let the database reject the second attempt. Where no natural key exists, a processed-message table keyed by message id is the fallback, and it needs an expiry policy of its own.