pending()->create([ 'amount_total' => $total, 'amount_paid' => 0, ]); } #[Test] public function record_payment_reaching_total_dispatches_order_paid_once(): void { $order = $this->pendingOrder(10000); Event::fake([OrderPaid::class]); $order->recordPayment(10000, 'pi_paid', 'card', 'stripe'); Event::assertDispatchedTimes(OrderPaid::class, 1); Event::assertDispatched(OrderPaid::class, fn (OrderPaid $e) => $e->order->is($order)); // Overpayment / a second recordPayment on an already-paid order stays quiet. $order->recordPayment(500); // ... and so do unrelated later saves (status move, meta touch). $order->updateMetaKey('tracking_number', 'TRK-1'); $order->markAsShipped('TRK-1', 'DHL'); Event::assertDispatchedTimes(OrderPaid::class, 1); $this->assertNotNull($order->fresh()->paid_at); } #[Test] public function partial_payment_does_not_dispatch_until_total_is_reached(): void { $order = $this->pendingOrder(10000); Event::fake([OrderPaid::class]); $order->recordPayment(4000); Event::assertNotDispatched(OrderPaid::class); $this->assertNull($order->fresh()->paid_at); $order->recordPayment(6000); Event::assertDispatchedTimes(OrderPaid::class, 1); $this->assertNotNull($order->fresh()->paid_at); } #[Test] public function direct_update_setting_paid_at_dispatches_order_paid_once(): void { $order = $this->pendingOrder(10000); Event::fake([OrderPaid::class]); $order->update(['paid_at' => now()]); Event::assertDispatchedTimes(OrderPaid::class, 1); // Moving an existing paid_at (value -> value) is not a "became paid" transition. $order->update(['paid_at' => now()->addMinute()]); $order->fresh()->update(['internal_note' => 'touched']); Event::assertDispatchedTimes(OrderPaid::class, 1); } #[Test] public function order_paid_fires_for_a_configured_host_subclass_with_that_instance(): void { config(['shop.models.order' => HostPaidOrder::class]); $order = HostPaidOrder::create(Order::factory()->pending()->raw([ 'amount_total' => 10000, 'amount_paid' => 0, ])); Event::fake([OrderPaid::class]); $order->recordPayment(10000, 'pi_host', 'card', 'stripe'); $order->recordPayment(1); Event::assertDispatchedTimes(OrderPaid::class, 1); Event::assertDispatched( OrderPaid::class, fn (OrderPaid $e) => $e->order instanceof HostPaidOrder && $e->order->is($order) ); } }