Stop form POSTs from 307ing to ladl.link or redirecting away to checkout.paystack.com, open Paystack Inline with access_code, and use the single-chrome sheet so payment stays on the Mini page.
245 lines
8.5 KiB
PHP
245 lines
8.5 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\QrCode;
|
|
use App\Models\User;
|
|
use App\Services\Pay\PayClient;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Str;
|
|
use Mockery;
|
|
use Tests\TestCase;
|
|
|
|
class MiniPaymentCheckoutTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_payment_landing_shows_secure_checkout_not_momo(): 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' => '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('data-ladill-pay-loading', 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)
|
|
->assertDontSee('window.location.assign(data.checkout_url)', 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_html_pay_post_does_not_redirect_browser_to_paystack(): void
|
|
{
|
|
$user = User::create([
|
|
'public_id' => (string) Str::uuid(),
|
|
'name' => 'Vendor',
|
|
'email' => 'vendor-html@example.com',
|
|
]);
|
|
|
|
$qr = QrCode::query()->create([
|
|
'user_id' => $user->id,
|
|
'short_code' => 'payhtml01',
|
|
'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' => 101,
|
|
'reference' => 'LP-TESTMINIHtml01',
|
|
'checkout_url' => 'https://checkout.paystack.com/mini-html',
|
|
'access_code' => 'mini-html',
|
|
'public_key' => 'pk_test_x',
|
|
'provider' => 'paystack',
|
|
'platform_fee_minor' => 15,
|
|
'merchant_amount_minor' => 985,
|
|
]);
|
|
$this->app->instance(PayClient::class, $pay);
|
|
|
|
// HTML form POST (no Accept: application/json) must stay on Mini and
|
|
// must not 307 to ladl.link or away to checkout.paystack.com.
|
|
$this->post('/q/'.$qr->short_code.'/pay', [
|
|
'amount' => 10,
|
|
])
|
|
->assertRedirect(url('/q/'.$qr->short_code))
|
|
->assertSessionHas('checkout_url', 'https://checkout.paystack.com/mini-html')
|
|
->assertSessionHas('access_code', 'mini-html')
|
|
->assertSessionMissing('error');
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|