Developers
WooCommerce plugin.
A WordPress plugin that scores checkouts, shows the result on the order, and lets your team confirm the outcome with one click. It writes no code into your theme and it cannot interrupt a purchase. The check runs after the order exists, and every failure path ends in the order continuing.
Install
A ZIP, an upload, an activation.
- Requirements
- WordPress 6.0 or newer, PHP 7.4 or newer, and WooCommerce active. Both the classic checkout and the block-based (Store API) checkout are supported. The plugin bundles the Heatrail PHP SDK, so Composer is not needed on the host.
- Where the ZIP comes from
- The partner portal serves the plugin archive. It appears there once your application is approved, on the Free plan as much as on Pro. Each release is built reproducibly from a tagged commit, and its SHA-256 is published on the API reference. Check the archive against that value before you install it.
- Installing
- Plugins → Add New → Upload Plugin, choose
heatrail.zip, install, activate. The plugin folder isheatrail. - What activation does
- Creates the local retry-queue table
{prefix}heatrail_queuein your own database, and schedules the queue-drain task to run every minute. Deactivating unschedules it. Nothing outside your WordPress install is touched. - Before it is configured
- With no key ID and secret saved, the plugin does nothing at all, no calls, no errors, no effect on checkout. “Not configured” is a silent, safe state by design.
Configure
Nine settings, under WooCommerce → Heatrail.
The settings screen requires the manage_options capability, so administrators see it and shop managers do not. What is typed here, the API secret, the network salt, the proxy pair and the API host, is site administration rather than shop management. Two of the fields are write-only: once saved, only their last four characters are ever rendered again, and submitting them empty keeps the stored value rather than clearing it.
- API base URL
- The Heatrail API origin,
https://api.heatrail.eu. Read it from configuration rather than pasting it into your code: it is the one value that would have to change everywhere at once. - Key ID
- Public identifier of your API key, of the form
hk_…. - API secret
- Write-only. Used only to sign requests. Never sent, only the signature derived from it is.
- Network salt
- Write-only. Used only to hash e-mail addresses and phone numbers inside your own server, before anything leaves it.
- Test mode
- A local toggle that reveals the magic test identifiers on this screen. It does not change what is sent. Test behaviour itself is gated on our side, per shop, see section 06.
- Device module
- Off unless you switch it on, as the Terms and the data processing agreement state. Controls the optional device layer described in section 05. While it is off the device script is never enqueued and the field is never sent. Switching it on is a decision you make as controller, including whether it is strictly necessary in your market.
- Browser evidence (beacon)
- On unless you switch it off, from plugin version 0.3.0. Controls the browser evidence described in section 03. While it is off the beacon script is never enqueued, your site stores no token and none is sent with a check.
- Trusted client IP header
- Only if a reverse proxy or CDN you control sits in front of your shop: the header it writes the visitor’s address into, for Cloudflare
CF-Connecting-IP. Leave it empty otherwise. The plugin never readsX-Forwarded-FororX-Real-IP, because any visitor can send those. - Trusted proxy ranges
- The IP ranges the proxy connects from, in CIDR notation, separated by spaces or commas. The header above is trusted only on a connection that comes from one of these ranges, and a header without ranges is ignored. Both fields empty is the default and means a direct host: the address is the one your own server sees on the connection.
Test connection sends a signed request to /v1/health and reports back plainly: connection OK, connection failed, or not configured. Use it before you expect the first order, a broken key found here costs a minute, and found in checkout costs a day of silently degraded checks.
Checkout
What happens when an order is placed.
The plugin hooks the point at which WooCommerce has already created the order, woocommerce_checkout_order_processed on the classic checkout, and the Store API equivalent on the block checkout. The order exists before Heatrail is consulted, which is the structural reason a check can never block a sale.
Figure 1
It then builds the allowlisted payload: stage-one hashes of the billing e-mail and phone (omitted entirely when empty or a placeholder), the customer IP, the WooCommerce session reference, the device vector if the device layer is on, the beacon token if the browser obtained one, and a coarse context, product risk class, item quantity, an amount bucket, and an account-age class. No product names, no totals to the cent, no addresses.
The call is bounded at 800 ms and the whole handler is wrapped so that no error, in the SDK or anywhere else, can escape into the checkout. The result, real or degraded, is stored on the order.
Browser evidence (beacon)
From plugin version 0.3.0 the checkout page also asks the Heatrail beacon, at beacon.heatrail.eu, to observe the connection the browser makes. It is on by default and it is a separate setting from the device layer in section 05. This is exactly what travels.
- When
- Once when the checkout page loads, and again when the customer presses the order button. Not on the rest of the storefront and not on the order-received page. The order never waits for it.
- What the request carries
- Your shop’s public key ID and a one-time reference tied to the checkout session. The reference is a keyed hash made with your site’s own secret, never a user ID. Like any server, the beacon also sees what every connection shows it: the IP address and the port.
- What stays in the browser
- Nothing. There are no cookies and the script stores nothing in the browser.
- Where the result goes
- The beacon signs what it observed. The browser hands that signed token to your own WordPress site, at
POST /wp-json/heatrail/v1/beacon, which keeps it for that checkout session for at most a day, and it reaches Heatrail together with the check. - Turning it off
- Untick Browser evidence (beacon) in the settings. Until Heatrail has added your shop’s domain to its allowlist, the beacon gives no answer and checks go out without a token.
Merchant view
What your team actually sees.
On the orders list
A Heatrail column, on both the classic orders table and the HPOS orders screen.
- A score badge
- The integer score, shown in red from 70 upward, plain below that.
TEST- The check was a simulated test-mode response, not a real assessment.
degraded- Heatrail was not reached in time. The check is queued for replay, and the order was not delayed by so much as a millisecond. Once the queued check is delivered, the plugin writes the real score and check ID onto the order and the badge becomes a normal score, usually within minutes, since the scheduled task runs every minute.
- A dash
- No check was recorded for this order at all.
On the order screen
A Heatrail panel in the sidebar showing the score and the reasons that fired, exactly as the API reference describes them. Below them:
- REAL and FRAUD
- One-click labelling. FRAUD carries a reason code, stolen card, chargeback received, account takeover, reseller abuse, or other. Labelling sends feedback to the network and never affects how this order is processed.
- Details
- A signed request that re-reads the score, the reasons and the time of the check for this order. Scoped to your own shop: another shop’s order reference is indistinguishable from one that does not exist.
- Request authorities memo
- Records a request, and nothing more. The memo itself is prepared and approved by a human operator, never generated automatically, and never contains another shop’s data. Heatrail sends nothing to any authority on its own.
- After a successful label
- The panel states which label was applied, by which WordPress user, with the incident identifier. A label is displayed as applied only after the API confirmed it. If confirmation fails, your operator is told “Label failed. Heatrail did not confirm. Try again.” rather than being shown a success that did not happen.
Labelling from your own workflow
The plugin registers an order status, Fraud (Heatrail). Moving an order into it prepares a pre-filled confirmation notice on the order screen. So does cancelling or refunding an order whose refund reason text mentions fraud. That second trigger is a heuristic, and it only pre-fills.
Nothing is sent until a human clicks Confirm. The status change itself never labels anything. If the order already carries a label, no prompt appears at all.
Alert e-mails
The plugin sends no e-mail. Alerts for high-scoring checks are sent by Heatrail, to the administrator of the shop whose order it was, and to nobody else.
Device layer
The optional device layer, and what enabling it means.
With the device module on, a small script is enqueued on the checkout page only, not on the rest of the storefront, and not on the order-received page. It collects a coarse device vector and posts it to your own WordPress site, at POST /wp-json/heatrail/v1/device, authenticated with a WordPress REST nonce and rate-limited to five posts per ten minutes per session. Your server validates it against the same strict allowlist the API enforces, parks it in the WooCommerce session, and attaches it to the check server-side.
The device script never contacts Heatrail. It makes no third-party request and no cross-origin call, and it puts no tracking pixel on your checkout. The beacon in section 03 is a separate setting, and that one does contact Heatrail.
What it collects
- Screen and rendering
- Screen width and height, device pixel ratio, whether the device reports touch input.
- Machine class
- Logical CPU core count and a coarse device-memory class, where the browser exposes them.
- Locale
- The IANA time zone and up to eight browser language tags.
- Platform class
- The platform string and a derived coarse class, desktop, iOS mobile, Android mobile, other mobile. The raw user-agent string never leaves the page, and the API refuses it if you try to send it.
- Canvas and WebGL tokens
- Two short non-cryptographic hashes, eight hex characters each, of a canvas rendering and of the graphics vendor and renderer strings. They are equality tokens for spotting the same machine again. The underlying renderer strings are hashed in the page and never transmitted.
- Never collected
- No names, no addresses, no e-mail, no phone, no raw user agent, no cookies of ours, no free text. Missing values are omitted rather than filled in, and the whole vector is rejected outright if it carries a single key outside the allowlist.
The privacy note that goes with turning it on
This is device fingerprinting, in the ordinary meaning of the phrase, and we call it nothing softer. It reads characteristics of the visitor’s device in order to recognise that device again. It is coarse and it is bounded, and it is still fingerprinting.
You are the data controller for it. Before enabling the device layer, decide whether your own privacy notice and cookie or tracking policy cover it, and whether your jurisdiction requires consent for storing or reading device information for this purpose. Ours is the processor role: we process what your shop sends, on your instructions, under the Data Processing Agreement.
The toggle is genuinely a toggle. Off means the script is never enqueued, the endpoint receives nothing, and the field is absent from every check. The signal is lower without it, because the device layer is one input among several. Your customers can also read our plain-English disclosure page.
Test mode
Rehearse the banner without real fraud.
With test mode enabled for your shop on the Heatrail side, place an order in your own checkout using fraud-test@heatrail.eu or +372 55550666. The check comes back with a simulated score of 85, reasons prefixed TEST MODE:, and a TEST badge. So you can see the real banner and the real buttons.
A test check creates no heat, records no velocity, touches no network graph, sends no alert and is never billed. The plugin’s own test-mode checkbox only displays these instructions. Without the server-side flag the magic values are treated exactly like placeholders and discarded, so they cannot produce a fake alert on a live shop by accident.
Failure
What happens when we are down.
The order completes. That is the entire answer, and it is the one behaviour in this product that is not negotiable.
Mechanically: the check abandons at 800 ms, is written to the local queue table in your own database, and the order proceeds with a degraded marker on it. Every minute the scheduled task probes /v1/health first, so a recovering service is never hammered, and replays queued checks with a fresh signature each time and the original idempotency key. The PHP SDK page draws that sequence, and the plugin is the store behind it.
A job that keeps failing backs off, doubling from a minute up to an hour between attempts. After fifty attempts it is parked, not deleted, and the settings screen shows both the pending and the parked counts, because a queue that quietly loses records is worse than no queue.
Because WordPress cron fires on site traffic, “every minute” is best-effort on a quiet shop. If that matters to you, drive wp-cron from a real system cron.