# etechflow/module-backorder-eta-display

> Shows expected restock or shipping ETA for backorder items on product pages, in cart, at checkout, and in order confirmation emails. Reduces uncertainty for customers and cuts merchant support tickets.

`composer require etechflow/module-backorder-eta-display`

Canonical URL: https://packagento.com/etechflow/module-backorder-eta-display

## At a glance

- **Vendor**: etechflow (https://packagento.com/etechflow.md)
- **Latest version**: 1.3.0 — released 2026-06-02
- **Pricing**: Free
- **Package type**: Magento 2 module
- **Status**: active, accepting new buyers

## Installation

Packagento is licence-gated, so even free packages need a licence on a project before Composer can resolve them.

1. **Sign in or create an account** at https://packagento.com/customer/account/.

2. **Add the package to your account.** Open https://packagento.com/etechflow/module-backorder-eta-display and complete the free checkout. A licence is minted automatically.

3. **Create or pick a project, then activate the licence on it.**
   - Projects represent the Magento installs you deploy to. Manage them at https://packagento.com/projects/.
   - Activate the new licence on the project you'll deploy this package to. Activation is what generates the Composer credentials scoped to that project.

4. **Add the project credentials to your Magento codebase.**

   Grab the project's public + private key from https://packagento.com/projects/ (open the project, then its Credentials tab), and add them to `auth.json`:

   ```json
   {
     "http-basic": {
       "packagento.com": {
         "username": "ppk_live_...",
         "password": "psk_live_..."
       }
     }
   }
   ```

   Add the Packagento Composer repository to `composer.json`:

   ```json
   {
     "repositories": [
       { "type": "composer", "url": "https://packagento.com" }
     ]
   }
   ```

5. **Install and apply.**

   ```bash
   composer require etechflow/module-backorder-eta-display:*
   bin/magento setup:upgrade
   bin/magento setup:di:compile
   bin/magento cache:flush
   ```

## What it does

Shows expected restock or shipping ETA for backorder items on product pages, in cart, at checkout, and in order confirmation emails. Reduces uncertainty for customers and cuts merchant support tickets.

## README

**Show customers when their backordered items will actually ship.**

Turns "this item is on backorder" from a customer-service nightmare into a clear, dated promise. Renders on product page, cart, checkout, and order confirmation email.

---

### What's new

The full version history lives in `CHANGELOG.md`. Highlights:

- **v1.1.0** — Module Status banner at the top of admin (shows whether the module is actually active), inline tooltips on every field, `backorder_eta` attribute now visible + filterable in the product grid, expanded i18n CSV, stale install guides removed.
- **v1.0.2** — Production Environment toggle for non-standard dev domains.

---

### What it does

Magento's default backorder message tells customers nothing — just "available for backorder", no date. Customers either don't notice (then complain when packages are late) or they cancel rather than wait an unknown amount of time. This module:

- Adds a per-product `backorder_eta` text attribute ("Ships in 5-7 business days", "Available Dec 15", "Pre-order — January 5")
- Falls back to a configurable store-wide default ETA if a product has no specific value
- Renders the ETA in four places: product detail page badge, cart summary, checkout summary, order confirmation email
- Three colour styles to match your store branding: warning (amber), info (blue), neutral (grey)
- Fully Hyvä compatible with dark-mode support

### Requirements

| | |
|---|---|
| **Magento** | Open Source 2.4.4+ OR Adobe Commerce 2.4.4+ |
| **PHP** | 8.1, 8.2, 8.3, or 8.4 |
| **Compatible themes** | Luma (default) + Hyvä + Hyvä Checkout |

### Installation

#### Option A — Composer (recommended)

```bash
composer require etechflow/module-backorder-eta-display:^1.0
bin/magento module:enable ETechFlow_BackorderEtaDisplay
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush
```

#### Option B — Manual (from zip)

1. Unzip `etechflow-module-backorder-eta-display-1.0.1.zip` into:
   ```
   <magento-root>/app/code/ETechFlow/BackorderEtaDisplay/
   ```
   **Directory MUST be `ETechFlow` (capital E, T, F) — case-sensitive on Linux.**

2. Enable and set up:
   ```bash
   bin/magento module:enable ETechFlow_BackorderEtaDisplay
   bin/magento setup:upgrade
   bin/magento setup:di:compile
   bin/magento cache:flush
   ```

### After install — quick start

#### 1. License key

Admin → **Stores → Configuration → eTechFlow → Backorder ETA Display → License**

Paste the key from your purchase email. Skip this on dev/staging — see USER_GUIDE for the auto-bypass patterns.

#### 2. Enable + set defaults

Same admin page:

- **General → Enabled = Yes**
- **General → Default ETA** = "Ships in 5-7 business days" (or whatever your default backorder window is)
- **General → ETA Label Prefix** = "Estimated delivery: " (or leave blank if you prefer the raw ETA)
- **Display → Badge Style** = warning / info / neutral (pick the colour that matches your branding)
- **Display → Show on PDP / Cart / Checkout / Order Email** = all Yes (or pick the ones you want)

Save Config.

#### 3. (Optional) Customise per product

Edit any product → eTechFlow Shipping → **Backorder ETA** field. Type whatever ETA you want for that specific product (e.g. "Available December 15" for a pre-order). Save.

Products with no custom ETA fall back to your store-wide default.

### What customers will see

| Where | Renders as |
|---|---|
| Product detail page (PDP) | Badge below price with truck icon + ETA text |
| Cart page | Summary block at top listing each backorder item + its ETA |
| Checkout shipping step | Same summary block |
| Order confirmation email | Styled HTML table appended below items list |

The module only renders when a product is genuinely on backorder AND has a non-empty ETA. In-stock products show nothing.

### Documentation

| File | Read when |
|---|---|
| `README.md` (this file) | First — overview + install |
| `docs/installation_guide.html` | Detailed install walkthrough with screenshots |
| `docs/USER_GUIDE.md` | Configuration + usage + troubleshooting |
| `CHANGELOG.md` | What changed in each version |
| `LICENSE.txt` | Licence terms |

### Support

- **Email:** support@etechflow.com — typically responds within one business day
- **Website:** https://etechflow.com

### License

Proprietary — see `LICENSE.txt`. Licensed per Magento installation. Unlimited dev/staging environments included.

## Changelog

All notable changes to this module. Adheres to [Semantic Versioning](https://semver.org/).

---

### [1.3.0] — 2026-06-02 — Stripe portal licensing + tri-state validation

Replaces the HMAC-only licence model with a Stripe Checkout subscription
flow gated by the eTechFlow licensing portal. Customers can now subscribe
directly from inside Magento Admin without ever leaving the panel — plan
selection, card entry, and SP-XXXX key activation all happen in the same
embedded frame. HMAC validation is retained as a fallback so existing
v1.2.x licences continue to work without re-activation.

#### Added

- **Admin subscription gate** under *Stores → Configuration → ETECHFLOW →
  Backorder ETA Display* — when no licence is detected on a production
  environment, the configuration page is replaced with three plan cards
  (Starter $19/mo, Professional $49/mo, Enterprise $99/mo) and an
  embedded customer-details form.
- **`Controller/Adminhtml/License/Checkout.php`** — builds a Stripe
  Checkout session against the selected plan and redirects the browser
  to Stripe's hosted payment page.
- **`Controller/Adminhtml/License/Activated.php`** — Stripe success-URL
  endpoint. Calls the portal's `/license/activate` route with the
  session ID, decrypts the stored secret key, receives an SP-XXXX
  licence in the response, and writes it to `core_config_data`.
- **Three new admin fields** in *License* group: `portal_url`,
  `portal_api_url`. New *Payment* group: `stripe_secret_key` (obscured
  + encrypted backend model), `stripe_publishable_key`, `stripe_currency`.
- **Tri-state portal validation** in `Model/LicenseValidator.php` — the
  validator now returns `?bool` for portal calls: `true` (valid),
  `false` (explicit reject, HTTP 200+valid:false / 401 / 403), or
  `null` (unreachable). Only `null` falls back to the 48h local grace,
  so admin IP-removal or subscription suspension locks the storefront
  immediately rather than waiting for the grace window to expire.
- **Split cache TTLs** — valid responses cached 1h
  (`CACHE_TTL_VALID = 3600`), explicit rejections cached only 60s
  (`CACHE_TTL_REJECT = 60`) so re-authorisation propagates fast.

#### Changed

- `Model/LicenseValidator.php` constructor now takes 4 arguments
  (`ScopeConfigInterface`, `StoreManagerInterface`, `CacheInterface`,
  `Curl`) — added Cache + Curl dependencies for portal validation. Any
  caller using `ObjectManager::get()` is unaffected. Direct
  instantiation in tests or DI overrides needs updating.
- `isValid()` SP-key branch now calls the portal **before** checking
  the local 48h grace. The grace is only used when the portal cannot
  be reached, never when the portal explicitly rejects.
- Removed the hyphen-suffix dev-host bypass (e.g. `*-dev.com`,
  `*-staging.com`, `*-uat.com`). It false-matched legitimate production
  hosts like `magento-dev.etechflow.com` and gave them a free unlimited
  bypass. Hosts matching `dev.`, `staging.`, `uat.` etc. as a
  subdomain prefix still bypass.
- Added `.ngrok-free.dev` to the recognised tunnel suffixes.

#### Test changes

- `Test/Unit/Model/LicenseValidatorTest.php` constructor calls updated
  to 4-arg form with mocked `CacheInterface` and `Curl`.
- Removed 3 dev-host bypass entries that tested the now-removed
  hyphen-suffix behaviour. Added one entry for `.ngrok-free.dev`.

#### Upgrade notes

- `bin/magento setup:upgrade` will need a generated/code wipe before
  it runs cleanly: `rm -rf generated/code/* generated/metadata/* var/cache/*`
  — the validator's constructor arity changed from 2 to 4 and stale
  metadata causes `ArgumentCountError`.
- Set your Stripe keys after upgrade: *Stores → Configuration →
  ETECHFLOW → Backorder ETA Display → Payment*. Secret key is stored
  encrypted via Magento's Encrypted backend model.
- Existing v1.2.x HMAC licences are NOT affected — `isValid()` only
  enters the portal branch for SP-prefixed keys.

---

_(Changelog truncated for .md surface. Full history on https://packagento.com/etechflow/module-backorder-eta-display.)_

## Recent Versions

| Version | Released |
|---|---|
| 1.3.0 | 2026-06-02 |
| 1.2.3 | 2026-05-30 |
| 1.2.2 | 2026-05-25 |
| 1.2.1 | 2026-05-20 |

## Dependencies

### Require

| Package | Constraint |
|---|---|
| magento/framework | ^103.0\|\|^104.0 |
| magento/module-bundle | ^101.0\|\|^102.0 |
| magento/module-catalog | ^104.0\|\|^105.0 |
| magento/module-catalog-inventory | ^100.4\|\|^101.0 |
| magento/module-checkout | ^100.4\|\|^101.0 |
| magento/module-config | ^101.2\|\|^102.0 |
| magento/module-configurable-product | ^100.4\|\|^101.0 |
| magento/module-eav | ^102.1\|\|^103.0 |
| magento/module-quote | ^101.2\|\|^102.0 |
| magento/module-sales | ^103.0\|\|^104.0 |
| magento/module-store | ^101.1\|\|^102.0 |
| php | ~8.1.0\|\|~8.2.0\|\|~8.3.0\|\|~8.4.0 |

### Suggest

| Package | Constraint |
|---|---|
| etechflow/module-next-day-eligibility | Pair with Next Day Eligibility (v1.1.0+) for the complete backorder loop — NDE removes the broken fast-shipping option, BED tells the customer when to expect the item instead. |
| hyva-themes/magento2-default-theme | Hyvä Theme support is built in — install Hyvä to enable Tailwind-styled templates automatically. |

## Quality

Latest release (1.3.0) fails the Packagento QA pipeline. Verdicts below are per-cell (Magento line × PHP version) for the matrixed tools, and run-once for the static / security tiers.


### Compatibility

Each Magento line is installed on its supported PHP versions, then the module is built (DI compile + static-content deploy). Cells show passed / failed / untested; staircase gaps render as `–`.

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | Pass | Pass | – | – |
| 2.4.8 | – | Pass | Pass | – |
| 2.4.9 | – | – | Pass | not tested |


### Code Quality

Advisory checks against the module's source. Never affect the Compatibility verdict — a phpcs finding can't make a module incompatible.

#### Static Analysis

Coding standards (phpcs), mess detection (phpmd), copy-pasted code (cpd), PHP cross-version compatibility, composer.json validity. Each runs once for the whole module.

| Tool | Status | Findings | Summary |
|---|---|---|---|
| PHPCS | Fail | 158 | 14 errors, 144 warnings (ruleset: Magento2) — 52 auto-fixable with phpcbf |
| PHPMD | Warning | 34 | 34 rule violations (MissingImport:14, NPathComplexity:8, CyclomaticComplexity:4, UnusedFormalParameter:3, TooManyPublicMethods:3) |
| Cpd | Pass | 0 |  |
| Composer validate | Info | 1 | valid; 1 advisory note (composer validate --strict) |

#### PHPStan

Type-checks the module against a real Magento install. Re-runs per Magento + PHP version because resolvable symbols differ between releases.

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | 21 | 21 | – | – |
| 2.4.8 | – | 21 | 21 | – |
| 2.4.9 | – | – | 21 | N/A |


### Tests

Unit and integration suites run per Magento + PHP cell. Test failures speak to the module's behaviour, not its compatibility with a line, so they're reported here separately.

#### Unit Tests

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | N/A | N/A | – | – |
| 2.4.8 | – | N/A | N/A | – |
| 2.4.9 | – | – | N/A | N/A |

#### Integration Tests

| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | N/A | N/A | – | – |
| 2.4.8 | – | N/A | N/A | – |
| 2.4.9 | – | – | N/A | N/A |


### Security

Dependency-advisory audit (composer audit) plus a source malware scan. A malware detection fails the version outright.

| Tool | Status | Findings | Summary |
|---|---|---|---|
| Composer audit | Pass | 0 |  |
| Malware scan | Pass | 0 |  |

## Licence and pricing

Free. A licence is still minted on checkout and bound to your project for Composer access — no payment step.

Refundable within 14 days of first purchase via https://packagento.com/account/refunds/.

## Install via Claude Code or any MCP client

The Packagento MCP server can run the licence + project + Composer steps above in one tool call:

```
purchase_and_install_packages(
  composer_names=["etechflow/module-backorder-eta-display"],
  project_id="proj_xxx"
)
```

This handles cart, checkout, licence minting, project activation, and writes auth.json credentials. Connect a client with `claude mcp add packagento https://mcp.packagento.com`. Full setup at https://packagento.com/docs/mcp-setup.

## Vendor

etechflow is a Magento 2 vendor on Packagento. See https://packagento.com/etechflow.md for their full catalogue.

