Bonaventure OgetoBy Bonaventure Ogeto|

Mocking the Paystack API in PHPUnit

In Laravel: use Http::fake() to intercept Paystack API calls before they hit the network. Return fake responses for the initialize and verify endpoints. In plain PHP: inject a Paystack client interface and use Mockery::mock() in tests. For webhook testing, generate a valid HMAC signature in your test and POST directly to your webhook controller.

Laravel Http::fake() for Paystack

// tests/Feature/PaymentTest.php
use IlluminateSupportFacadesHttp;
use TestsTestCase;

class PaymentTest extends TestCase
{
    public function test_checkout_returns_authorization_url(): void
    {
        Http::fake([
            'https://api.paystack.co/transaction/initialize' => Http::response([
                'status' => true,
                'data' => [
                    'authorization_url' => 'https://checkout.paystack.com/test',
                    'access_code' => 'test_access',
                    'reference' => 'ref_test_001',
                ],
            ], 200),
        ]);

        $response = $this->postJson('/api/checkout', [
            'email' => 'user@test.com',
            'amount' => 10000,
            'order_id' => 1,
        ]);

        $response->assertStatus(200)
                 ->assertJson(['authorization_url' => 'https://checkout.paystack.com/test']);
    }

    public function test_webhook_processes_charge_success(): void
    {
        $order = Order::factory()->create(['status' => 'pending']);
        $secret = config('services.paystack.secret_key');

        $payload = json_encode([
            'event' => 'charge.success',
            'data' => [
                'reference' => 'ref_test_001',
                'amount' => 1000000,
                'status' => 'success',
                'metadata' => ['order_id' => $order->id],
            ],
        ]);

        $signature = hash_hmac('sha512', $payload, $secret);

        $response = $this->withHeaders([
            'x-paystack-signature' => $signature,
        ])->postJson('/webhook/paystack', json_decode($payload, true));

        $response->assertStatus(200);
        $this->assertDatabaseHas('orders', [
            'id' => $order->id,
            'status' => 'paid',
        ]);
    }
}

Plain PHP with Mockery

// Plain PHP test with Mockery
use PHPUnitFrameworkTestCase;
use Mockery;

class PaymentServiceTest extends TestCase
{
    public function test_returns_checkout_url_on_success(): void
    {
        $paystackMock = Mockery::mock(PaystackClient::class);
        $paystackMock->shouldReceive('initializeTransaction')
                     ->once()
                     ->with(Mockery::on(fn($args) => $args['email'] === 'user@test.com'))
                     ->andReturn([
                         'status' => true,
                         'data' => ['authorization_url' => 'https://checkout.paystack.com/test'],
                     ]);

        $service = new PaymentService($paystackMock);
        $result = $service->createCheckout('user@test.com', 10000);

        $this->assertEquals('https://checkout.paystack.com/test', $result['url']);
    }

    protected function tearDown(): void
    {
        Mockery::close();
    }
}

Learn More

Key Takeaways

  • In Laravel, use Http::fake() to intercept Paystack API calls — no real HTTP request is made.
  • Match the fake response structure to what Paystack actually returns: { status: true, data: { reference, amount, status } }.
  • For webhook tests, generate the HMAC SHA512 signature in the test: hash_hmac("sha512", $body, $secret).
  • Use Mockery::mock() or PHPUnit createMock() if you have a Paystack service class — mock the interface, not the HTTP client.
  • Test the decline scenario by returning { status: false, data: { gateway_response: "Do Not Honor" } } from your Http fake.
  • In Laravel, use RefreshDatabase trait so each test starts with a clean database state.

Frequently Asked Questions

Does Http::fake() intercept all Paystack API calls in Laravel?
Http::fake() intercepts calls made through Laravel's HTTP client (Http:: facade or the underlying Guzzle client that Laravel wraps). If your Paystack integration uses curl directly or a third-party SDK that does not use Laravel's HTTP client, Http::fake() will not intercept it — use Mockery instead.
How do I test the Paystack webhook signature validation in PHPUnit?
Generate a valid HMAC SHA512 signature in your test using the same algorithm your handler uses: $signature = hash_hmac("sha512", $rawBody, $secret). Set this as the x-paystack-signature header in your test POST request. To test invalid signature rejection, change one character of the signature and assert the handler returns 401.
What PHPUnit version is required for these patterns?
PHPUnit 10+ (the current standard with Laravel 11+). The patterns shown work with PHPUnit 9 and 10. Http::fake() requires Laravel 7+ — it was introduced with Laravel's built-in HTTP client.
Should I use Http::fake() or a Paystack SDK mock?
If you call the Paystack API directly via Laravel's Http client, use Http::fake(). If you use a Paystack PHP SDK (like unicodeveloper/paystack or yabacon/paystack-php), mock the SDK class with Mockery — it has its own HTTP layer that Http::fake() may not intercept.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.

Apply to the McTaba Marathon