(string) Str::uuid(), 'name' => 'Vendor', 'email' => 'vendor@example.com', ]); $qr = QrCode::query()->create([ 'user_id' => $user->id, 'short_code' => 'payland01', 'type' => QrCode::TYPE_PAYMENT, 'label' => 'Till', 'payload' => [ 'content' => [ 'business_name' => 'Accra Kiosk', 'currency' => 'GHS', ], 'style' => [], ], 'is_active' => true, ]); $this->withHeader(\App\Support\LadillLink::INTERNAL_HEADER, '1') ->get('/q/'.$qr->short_code) ->assertOk() ->assertSee('Your payment is processed securely', false) ->assertSee('md:items-center', false) ->assertSee('Complete your payment', false) ->assertSee('data-ladill-pay-sheet', false) ->assertSee('resumeTransaction', false) ->assertSee('/q/'.$qr->short_code.'/pay', false) ->assertDontSee('https://ladl.link/'.$qr->short_code.'/pay', false) ->assertDontSee('MTN MoMo number', false) ->assertDontSee('Pay with MTN MoMo', false) ->assertDontSee('Continue to Paystack', false) ->assertDontSee('Paystack checkout', false); } public function test_json_pay_post_is_not_redirected_to_ladill_link(): void { $user = User::create([ 'public_id' => (string) Str::uuid(), 'name' => 'Vendor', 'email' => 'vendor-ajax@example.com', ]); $qr = QrCode::query()->create([ 'user_id' => $user->id, 'short_code' => 'payajax01', 'type' => QrCode::TYPE_PAYMENT, 'label' => 'Till', 'payload' => [ 'content' => [ 'business_name' => 'Accra Kiosk', 'currency' => 'GHS', ], 'style' => [], ], 'is_active' => true, ]); $pay = Mockery::mock(PayClient::class); $pay->shouldReceive('createCheckout') ->once() ->andReturn([ 'id' => 100, 'reference' => 'LP-TESTMINIAjax01', 'checkout_url' => 'https://checkout.paystack.com/mini-ajax', 'access_code' => 'mini-ajax', 'public_key' => 'pk_test_x', 'provider' => 'paystack', 'platform_fee_minor' => 15, 'merchant_amount_minor' => 985, ]); $this->app->instance(PayClient::class, $pay); // No INTERNAL header — XHR/JSON must still hit Mini, not 307 to ladl.link. $this->postJson('/q/'.$qr->short_code.'/pay', [ 'amount' => 10, ]) ->assertOk() ->assertJsonPath('checkout_url', 'https://checkout.paystack.com/mini-ajax') ->assertJsonPath('access_code', 'mini-ajax') ->assertJsonPath('provider', 'paystack'); } public function test_pay_initiation_does_not_require_phone_and_returns_paystack_checkout(): void { $user = User::create([ 'public_id' => (string) Str::uuid(), 'name' => 'Vendor', 'email' => 'vendor@example.com', ]); $qr = QrCode::query()->create([ 'user_id' => $user->id, 'short_code' => 'payinit01', 'type' => QrCode::TYPE_PAYMENT, 'label' => 'Till', 'payload' => [ 'content' => [ 'business_name' => 'Accra Kiosk', 'currency' => 'GHS', ], 'style' => [], ], 'is_active' => true, ]); $pay = Mockery::mock(PayClient::class); $pay->shouldReceive('createCheckout') ->once() ->with(Mockery::on(function (array $payload): bool { return ($payload['source_service'] ?? '') === 'mini' && ! array_key_exists('customer_phone', $payload) && ($payload['line_items'][0]['unit_price_minor'] ?? 0) === 2500; })) ->andReturn([ 'id' => 99, 'reference' => 'LP-TESTMINIPAY001', 'checkout_url' => 'https://checkout.paystack.com/mini-test', 'access_code' => 'mini-test', 'public_key' => 'pk_test_x', 'provider' => 'paystack', 'platform_fee_minor' => 38, 'merchant_amount_minor' => 2462, ]); $this->app->instance(PayClient::class, $pay); $this->withHeader(\App\Support\LadillLink::INTERNAL_HEADER, '1') ->postJson('/q/'.$qr->short_code.'/pay', [ 'amount' => 25, ]) ->assertOk() ->assertJsonPath('checkout_url', 'https://checkout.paystack.com/mini-test') ->assertJsonPath('access_code', 'mini-test') ->assertJsonPath('provider', 'paystack'); $this->assertDatabaseHas('mini_payments', [ 'qr_code_id' => $qr->id, 'amount_minor' => 2500, 'payment_reference' => 'LP-TESTMINIPAY001', 'payer_phone' => null, ]); } public function test_payment_callback_returns_top_frame_escape(): void { $user = User::create([ 'public_id' => (string) Str::uuid(), 'name' => 'Vendor', 'email' => 'vendor3@example.com', ]); $qr = QrCode::query()->create([ 'user_id' => $user->id, 'short_code' => 'paycb001', 'type' => QrCode::TYPE_PAYMENT, 'label' => 'Till', 'payload' => [ 'content' => [ 'business_name' => 'Accra Kiosk', 'currency' => 'GHS', ], 'style' => [], ], 'is_active' => true, ]); $this->withHeader(\App\Support\LadillLink::INTERNAL_HEADER, '1') ->get('/q/'.$qr->short_code.'/pay/callback') ->assertOk() ->assertSee('Confirming your payment', false) ->assertSee('window.top.location.replace', false); } }