whisperr/php
Whisperr for PHP is a lightweight integration package for Laravel and other PHP applications, offering simple configuration, an expressive API, and framework-friendly access to Whisperr services. It is designed for developers who want a clean, maintainable connection without unnecessary boilerplate...
Install the package and publish its config, then confirm the credentials and defaults in config/whisperr.php and the package README. The first thing to check after upgrading to v0.3.0 is that you are on the new SDK contract: every outbound event should now include context.sdk_name and context.sdk_version.
Start with one non-critical event so you can inspect the payload shape:
use Whisperr\Laravel\Facades\Whisperr;
Whisperr::track('user_signed_up', [
'plan' => 'trial',
], [
// Optional per-event context overrides
'source' => 'web',
]);
Then set WHISPERR_APP_VERSION in each environment where you want release attribution. An empty value is intentionally omitted from the payload, so do not assume the key will always exist downstream.
Your first real use case should be billing lifecycle events. Use Whisperr\BillingEvent constants and BillingEvent::properties() instead of hand-writing event names and property arrays. That gives you the reserved playbook fallback names and consistent billing property validation from day one.
Use the billing helper as the boundary for billing payloads. Keep BillingEvent::properties() close to the code that knows about your billing provider, then pass the result into track() or publish():
use Whisperr\BillingEvent;
use Whisperr\Laravel\Facades\Whisperr;
Whisperr::track(
BillingEvent::RENEWAL_SUCCEEDED,
BillingEvent::properties([
'plan_id' => $subscription->plan_id,
'price' => $invoice->total / 100, // major units, not cents
'currency' => $invoice->currency, // ISO 4217, e.g. "usd"
'plan_period' => 'monthly',
'transaction_id' => $invoice->transaction_id,
'store' => 'stripe',
'extra' => [
'coupon' => $invoice->coupon,
],
])
);
The exact call signature should follow the README, but the pattern is to centralize property construction so currency casing, price units, and required keys are handled once.
Map your domain events to the reserved constants. Create a small mapper or enum-like class in your app so your internal event names do not leak into analytics:
final class BillingEventMapper
{
public static function fromSubscriptionEvent(SubscriptionEvent $event): string
{
return match ($event->type) {
'trial.started' => BillingEvent::TRIAL_STARTED,
'trial.converted' => BillingEvent::TRIAL_CONVERTED,
'renewal.succeeded' => BillingEvent::RENEWAL_SUCCEEDED,
'membership.ended' => BillingEvent::MEMBERSHIP_ENDED,
default => throw new InvalidArgumentException("Unmapped billing event: {$event->type}"),
};
}
}
This keeps the reserved names centralized and makes future spec changes easier to absorb.
Set app version globally, override only when necessary. Configure WHISPERR_APP_VERSION from your deploy pipeline or release tag. If you need a one-off override, pass app_version in the per-event $context; that value wins for that event. Avoid scattering per-event overrides because it makes release attribution inconsistent.
Send events after the transaction commits. For billing events, dispatch after your database transaction succeeds. If you queue the event, make sure the payload is fully constructed before enqueueing and that the queued job does not depend on request-scoped state. A simple job wrapper keeps HTTP calls out of the request path:
dispatch(new TrackWhisperrEvent(
BillingEvent::MEMBERSHIP_STARTED,
$properties,
$context,
))->afterCommit();
Test payloads, not just that the call happened. In application tests, assert the outgoing event name, billing properties, and context keys. If the package uses Laravel’s HTTP client, Http::fake() and Http::assertSent() are the fastest route. Otherwise bind a spy or fake implementation of the client and assert the constructed payload. Include a test that context.sdk_name, context.sdk_version, and context.app_version are present when configured.
Keep your own analytics wrapper. Rather than calling Whisperr::track() directly from controllers and jobs, expose an application-level service such as Analytics::track(). That wrapper can add tenant IDs, user IDs, and environment context, while leaving SDK metadata and billing property validation to the package.
app_version is omitted when empty. If your warehouse expects the key on every event, enforce WHISPERR_APP_VERSION in deployment checks. An empty string, unset variable, or null config value will result in no context.app_version key.
Per-event context wins. A caller-supplied app_version in $context overrides the global value. This is useful for backfills or special releases, but it can also create inconsistent attribution. Use it deliberately and document why.
Do not manually set SDK metadata. context.sdk_name and context.sdk_version are added by the package. Avoid putting those keys in your own context; rely on Whisperr::VERSION and the package’s automatic injection. If you need to debug, log the final payload rather than guessing.
price is in major units. BillingEvent::properties() expects major units, not cents. Passing 4900 for a $49.00 charge will inflate revenue. Convert at the boundary and keep the conversion beside the billing provider integration.
Currency validation is strict. Currency must be exactly three letters; the helper upper-cases it. Values like "US", "USDD", or "usd " will fail or need normalization first. Normalize and validate before calling, and convert InvalidArgumentException into a domain-specific exception if billing payloads are assembled in a service.
Price must be finite and non-negative. Negative prices and INF/NAN throw InvalidArgumentException. Guard against bad provider data, refunds represented as negative amounts, and calculations that can produce non-finite values.
Reserved billing names have meaning. Constants such as TRIAL_STARTED, RENEWAL_SUCCEEDED, and BILLING_RECOVERED are reserved fallback names from the playbook engine spec. Do not reuse them for unrelated product events. If you need a custom event, use your own namespaced event name.
Upgrade consumers may see new context keys. v0.3.0 adds context.sdk_name, context.sdk_version, and optionally context.app_version. If downstream schemas are strict, update them before rolling out. No breaking changes are called out in the release notes, but strict payload validators can still fail.
Check the installed version when debugging. Confirm composer show reports v0.3.0 and that Whisperr::VERSION matches the release. If context.sdk_version is missing or stale, you may be running an older lockfile or a cached config.
Use the conformance tests as a reference. The package now runs server sections of whisperr-spec conformance/v2.json. When in doubt about payload shape, inspect those tests and the README’s app_language and billing fallback sections rather than reverse-engineering from production traffic.
How can I help you explore Laravel packages today?