create(); $payment = Payment::factory()->create(['booking_id' => $booking->id]); expect($payment->booking)->toBeInstanceOf(Booking::class) ->and($payment->booking->is($booking))->toBeTrue() ->and($booking->payments->first()->is($payment))->toBeTrue(); }); test('a booking can have more than one payment attempt', function () { $booking = Booking::factory()->create(); Payment::factory()->failed()->create(['booking_id' => $booking->id]); Payment::factory()->completed()->create(['booking_id' => $booking->id]); expect($booking->payments)->toHaveCount(2); }); test('gateway and status cast to their enums', function () { $payment = Payment::factory()->create([ 'gateway' => PaymentMethod::KbzMiniApp, 'status' => PaymentStatus::Completed, ]); expect($payment->gateway)->toBe(PaymentMethod::KbzMiniApp) ->and($payment->status)->toBe(PaymentStatus::Completed); }); test('a payment defaults to pending', function () { $payment = Payment::factory()->create(); expect($payment->status)->toBe(PaymentStatus::Pending); }); test('a refund belongs to a payment, not the booking directly', function () { $payment = Payment::factory()->completed()->create(); $refund = Refund::factory()->create(['payment_id' => $payment->id]); expect($refund->payment)->toBeInstanceOf(Payment::class) ->and($refund->payment->is($payment))->toBeTrue() ->and($payment->refunds->first()->is($refund))->toBeTrue(); }); test('a payment can have more than one refund for partial refunds', function () { $payment = Payment::factory()->completed()->create(['amount' => 20000]); Refund::factory()->completed()->create(['payment_id' => $payment->id, 'amount' => 8000]); Refund::factory()->create(['payment_id' => $payment->id, 'amount' => 12000]); expect($payment->refunds)->toHaveCount(2); }); test('refund status casts to its enum and defaults to pending', function () { $refund = Refund::factory()->create(); expect($refund->status)->toBe(RefundStatus::Pending); }); test('deleting a payment cascades to its refunds', function () { $payment = Payment::factory()->completed()->create(); $refund = Refund::factory()->create(['payment_id' => $payment->id]); $payment->delete(); expect(Refund::find($refund->id))->toBeNull(); }); test('gateway_transaction_id cannot be shared across unrelated retried attempts without being unique-constrained', function () { // gateway_transaction_id is not unique-constrained since a failed attempt // may legitimately be retried under a fresh Payment row with its own id; // this just documents the column accepts duplicates without throwing. Payment::factory()->create(['gateway_transaction_id' => 'kbz-txn-1']); expect(fn () => Payment::factory()->create(['gateway_transaction_id' => 'kbz-txn-1'])) ->not->toThrow(QueryException::class); });