Send paid orders from any PHP backend

One client sends your server events when the request ends, and an idempotency key makes a repeated webhook count once.

Package mirafive/sdk-php

Set up in 5 steps

  1. Install the package

    It needs PHP 8.3 or later with ext-json and has no required Composer dependencies. It sends with ext-curl when it is loaded, otherwise with PHP streams, or through your own PSR-18 client.

    Code for step 1 of the PHP setup: Install the package. composer require mirafive/sdk-php

  2. Add the secret key

    Use the secret key of a server source from Data → Sources. It stays on the server, so never print it into HTML, JavaScript or a mobile app.

    Code for step 2 of the PHP setup: Add the secret key. MIRAFIVE_SECRET_KEY=mf_…

  3. Create one client

    new Mira reads the key from $_ENV, $_SERVER or getenv(). track() buffers, and the buffer is sent at the end of the request, every 100 events and on flush(). A flush never takes longer than 3 seconds, even when MIRA FIVE is unreachable.

    Code for step 3 of the PHP setup: Create one client. <?php declare(strict_types=1); use MiraFive\Mira; require __DIR__.'/vendor/autoload.php'; // Reads MIRAFIVE_SECRET_KEY, and MIRAFIVE_HOST when it is set. $mira = new Mira;

  4. Send the paid order

    Call track() with named arguments where the payment is confirmed. Ids are strings, so cast integer ids with (string). The revenue property feeds your purchase goal.

    Code for step 4 of the PHP setup: Send the paid order. $mira->track('order_paid', userId: (string) $order->customerId, properties: [ 'revenue' => 328, 'currency' => 'EUR', ]);

  5. Check the key

    The install check proves the key and host work and is never stored or billed. A wrong key throws a MiraError instead. Then track a real event and find it under Data → Live.

    Code for step 5 of the PHP setup: Check the key. $receipt = $mira->send([['name' => '$install_check']]); var_dump($receipt->reason); // string(13) "install_check"

MIRA FIVE is analytics built around the person. mirafive/sdk-php is its server side for plain PHP and for frameworks without a MIRA FIVE package: it buffers events, sends them when the request ends, and evaluates feature flags in your process. Under PHP-FPM, pass a PSR-16 cache your app already has (new Mira(cache: $psr16Cache)), so every PHP process shares the flag document and pauses delivery together during an outage.

Where do pageviews come from?

From the browser. Add the two-line script tag to your templates with the website key of a website source. Both sources report into the same project, and the secret key never goes into a page.

How does a paid order reach its channel?

Through the person who paid. With consent, the browser keeps an anonymous id. Send it to your server, for example in a hidden field of the checkout form, and identify links the visitor’s earlier visits to your user id:

$mira->identify((string) $user->id, ['plan' => 'pro'], anonymousId: $anonymousId);

The paid order carries the same user id, so MIRA FIVE puts it on that person, next to the channel of their first visit. Use your internal id, never an email address. Create a purchase goal for order_paid to see buyers and revenue per channel and campaign.

How do I make a webhook count once?

send() delivers at once and returns the server’s Receipt. With an idempotency key, a repeat is stored and billed once:

$receipt = $mira->send([
    ['name' => 'order_paid', 'userId' => (string) $order->customerId, 'properties' => ['revenue' => 328, 'currency' => 'EUR']],
], idempotencyKey: "order-{$order->id}");

send() throws a MiraError when MIRA FIVE refuses the batch or cannot be reached after the retries. track() and flush() never throw for transport reasons.

The source’s Install tab has Let your coding agent install it, a prompt for Claude Code, Cursor or Codex with every step and the check; for a server source it never contains the secret key. The PHP SDK docs cover queues, transports and flags, and the setup page shows who does what, in order.

Other setups

The same numbers, whichever way your site is built.

Questions and answers

What happens when MIRA FIVE is unreachable?
A flush gives up after 3 seconds at most. After a retryable failure, flushes skip the network for 30 seconds and drop their events; with a PSR-16 cache, that pause covers every PHP process. handOff moves delivery to your queue.
What about Octane, RoadRunner and queue workers?
Long-running workers serve many requests in one process and reach the end-of-request flush only when they stop. Call $mira->flush() after each request or job.
How do I keep tests and local runs quiet?
new Mira(enabled: getenv('APP_ENV') === 'production') sends nothing and needs no key, but still refuses the input production would. To assert on what was sent, pass a transport that records requests.
I use Laravel or Symfony. Is this the right package?
No. mirafive/sdk-laravel and mirafive/sdk-symfony wire this SDK into the container and send after the response. See the Laravel setup and the Symfony setup.

See which channel brings buyers

Free for 25,000 events a month. No card needed.