set('services.revenuecat.webhook', 'shared-secret'); $payload = [ 'event' => [ 'app_user_id' => 'tenant-123', 'product_id' => 'pro_month', ], ]; $json = json_encode($payload); $signature = base64_encode(hash_hmac('sha1', $json, 'shared-secret', true)); Bus::fake(); $response = $this->postJson('/api/v1/webhooks/revenuecat', $payload, [ 'X-Signature' => $signature, ]); $response->assertStatus(202) ->assertJson(['status' => 'accepted']); Bus::assertDispatched(ProcessRevenueCatWebhook::class); } public function test_webhook_rejects_invalid_signature(): void { config()->set('services.revenuecat.webhook', 'shared-secret'); Bus::fake(); $response = $this->postJson('/api/v1/webhooks/revenuecat', [ 'event' => ['app_user_id' => 'tenant-123'], ], [ 'X-Signature' => 'invalid-signature', ]); $response->assertStatus(400) ->assertJson(['error' => 'Invalid signature']); Bus::assertNotDispatched(ProcessRevenueCatWebhook::class); } }