immediate cancel + reactivate old cart * - customer + addr -> keep order pending * - customer + !addr + config=cancel -> cancel * - customer + !addr + config=keep_pending -> keep pending * - !addr branch ALWAYS dispatches the no-address event */ class PaymentServiceCancelStrategyTest extends TestCase { protected function tearDown(): void { Mockery::close(); parent::tearDown(); } private function makeService(): PaymentService { return new PaymentService( Mockery::mock(OrderRepository::class), Mockery::mock(CartRepository::class), Mockery::mock(CartTokenService::class), Mockery::mock(InvoiceRepository::class), Mockery::mock(OrderTransactionRepository::class), Mockery::mock(PaymentAttemptRepository::class), ); } private function fakeOrder(?array $shippingFields, array $additional = []): object { $payment = (object) ['additional' => $additional]; $shipping = $shippingFields ? (object) $shippingFields : null; return new class($payment, $shipping) { public function __construct( public object $payment, public ?object $shipping_address, ) {} }; } public function test_guest_cancel_reactivates_old_cart(): void { Event::fake(); $order = $this->fakeOrder([ 'city' => 'NYC', 'postcode' => '10001', ], ['cart_id' => 42]); $result = $this->makeService()->decideCancelStrategy($order, isGuest: true); $this->assertSame('cancel', $result['action']); $this->assertSame('guest', $result['reason']); $this->assertSame(42, $result['reactivate_cart']); } public function test_customer_with_real_shipping_address_keeps_pending(): void { Event::fake(); $order = $this->fakeOrder([ 'city' => 'San Francisco', 'postcode' => '94016', ]); $result = $this->makeService()->decideCancelStrategy($order, isGuest: false); $this->assertSame('keep_pending', $result['action']); $this->assertSame('customer_has_shipping_address', $result['reason']); Event::assertNotDispatched('bagistoapi.express.cancel.no-address'); } public function test_customer_without_shipping_address_default_cancel_strategy(): void { config(['bagistoapi.express_checkout.cancel_without_address' => 'cancel']); Event::fake(); $order = $this->fakeOrder(null); $result = $this->makeService()->decideCancelStrategy($order, isGuest: false); $this->assertSame('cancel', $result['action']); $this->assertSame('customer_no_shipping_address', $result['reason']); Event::assertDispatched('bagistoapi.express.cancel.no-address'); } public function test_customer_without_shipping_address_keep_pending_strategy(): void { config(['bagistoapi.express_checkout.cancel_without_address' => 'keep_pending']); Event::fake(); $order = $this->fakeOrder(null); $result = $this->makeService()->decideCancelStrategy($order, isGuest: false); $this->assertSame('keep_pending', $result['action']); $this->assertSame('customer_no_shipping_address', $result['reason']); Event::assertDispatched('bagistoapi.express.cancel.no-address'); } public function test_customer_with_placeholder_address_is_treated_as_no_address(): void { config([ 'bagistoapi.express_checkout.cancel_without_address' => 'cancel', 'bagistoapi.express_checkout.placeholder_address' => [ 'city' => 'Pending', 'postcode' => '00000', ], ]); Event::fake(); $order = $this->fakeOrder([ 'city' => 'Pending', 'postcode' => '00000', ]); $result = $this->makeService()->decideCancelStrategy($order, isGuest: false); $this->assertSame('cancel', $result['action']); $this->assertSame('customer_no_shipping_address', $result['reason']); } }