create(['total' => 200_000]); $order = Order::create([ 'order_num' => 'ORD-' . uniqid(), 'customer_id' => $customer->id, 'sub_total' => 100_000, 'total' => 100_000, 'status' => OrderStatus::PENDING->value, ]); $invoice = Invoice::create([ 'invoice_number' => 'INV-' . uniqid(), 'customer_id' => $customer->id, 'order_id' => $order->id, 'invoice_type' => 1, 'sub_total' => 100_000, 'tax' => 0, 'total' => 100_000, 'status' => InvoiceStatus::UNPAID->value, ]); $service = app(InvoicePaymentService::class); $service->payWithBalance($invoice, $customer, 'balance'); // Simulate a retried request (double-click / network retry) for the same invoice. $service->payWithBalance($invoice->fresh(), $customer->fresh(), 'balance'); $this->assertSame(100_000, (int) $customer->fresh()->total); $this->assertSame(1, Transaction::where('customer_id', $customer->id)->where('type', 'invoice')->count()); } public function test_a_cancelled_invoice_cannot_be_paid_and_is_never_counted_as_paid(): void { $customer = Customer::factory()->create(['total' => 200_000]); $order = Order::create([ 'order_num' => 'ORD-' . uniqid(), 'customer_id' => $customer->id, 'sub_total' => 100_000, 'total' => 100_000, 'status' => OrderStatus::CANCELLED->value, ]); $invoice = Invoice::create([ 'invoice_number' => 'INV-' . uniqid(), 'customer_id' => $customer->id, 'order_id' => $order->id, 'invoice_type' => 1, 'sub_total' => 100_000, 'tax' => 0, 'total' => 100_000, 'status' => InvoiceStatus::CANCELLED->value, ]); $service = app(InvoicePaymentService::class); $this->expectException(\InvalidArgumentException::class); try { $service->payWithBalance($invoice, $customer, 'balance'); } finally { $this->assertSame(200_000, (int) $customer->fresh()->total); $this->assertSame(InvoiceStatus::CANCELLED->value, (int) $invoice->fresh()->status); $this->assertNull($invoice->fresh()->paid_at); } } }