To test webhook handling, post a JSON payload directly to the paddle/webhook route.
Without Signature Verification
If you unset cashier.webhook_secret in your test environment, you can post the payload directly:
config()->set('cashier.webhook_secret', null);
$this->postJson('paddle/webhook', $payload)->assertOk();
With Signature Verification
To test with signature verification enabled, you must manually generate a Paddle-Signature header using the cashier.webhook_secret. The signature format is ts={timestamp};h1={hmac_sha256}, where the HMAC is calculated over {$timestamp}:{$json_body}.
$payload = ['event_type' => 'transaction.completed', 'data' => [...]];
$json = json_encode($payload, JSON_THROW_ON_ERROR);
$timestamp = time();
$signature = hash_hmac('sha256', "{$timestamp}:{$json}", config('cashier.webhook_secret'));
$this->withHeader('Paddle-Signature', "ts={$timestamp};h1={$signature}")
->call('POST', 'paddle/webhook', [], [], [], ['CONTENT_TYPE' => 'application/json'], $json)
->assertOk();