=== Teamzlab Fedex ===
Contributors: teamzlab
Tags: fedex, woocommerce, shipping, tracking, freight
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 7.4
WC requires at least: 7.0
WC tested up to: 11.2
Stable tag: 1.2.4
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

FedEx REST shipping for WooCommerce: live rates, labels, tracking webhooks, paperless ETD customs, and a self-serve Return Tag portal.

== Description ==

**Teamzlab Fedex** wires 14 FedEx REST APIs into WooCommerce — rates, labels, tracking, customs and returns. Built REST-native, so the 2026 SOAP shutdown never touches your store.

= Features you won't find in the other FedEx plugins =

* **Real-time tracking webhooks** — FedEx AIV events (pickup, transit, delivery, customs holds) are pushed to your store via HMAC-signed webhooks. One-time setup: create the webhook subscription in the FedEx portal and paste the shared secret under **Fedex → Settings → Tracking**. No polling for status.
* **Self-serve Return Tag portal** — drop the `[fshipping_return_portal]` shortcode on the My-Account page. Customers pick items, get a FedEx-compliant return label in their email, and the three nearest drop-off counters on the confirmation page.
* **Paperless ETD customs** — one-click electronic trade documents upload for international shipments, plus commercial-invoice auto-generation from WooCommerce line items. No printing, no stapling.

= FedEx APIs wired =

OAuth, Ship, Rates + Transit Times, Basic Track, Address Validation, Postal Code Validation, Service Availability, Pickup Request, Locations Search, Tracking webhooks (AIV receiver), Open Ship, Global Trade, Trade Documents Upload, Return Tag — 14 APIs, 1 plugin. A Freight LTL client class is included for developers, but it is not yet wired into checkout or the admin UI, so freight rates and BOLs are not available out of the box.

= Customer-facing moat features =

* Dimensional-weight calculator shortcode `[fshipping_dim_weight]` — pure client-side, embed on any page.
* Embeddable live-rate calculator `[fshipping_rate_widget]` — merchants can drop a `<script>` on any site for backlink-compounding SEO.
* Drop-off finder `[fshipping_locations]` — the nearest FedEx drop-off locations for any postcode (10 by default, configurable with the `limit` attribute).
* Live tracking timeline `[fshipping_tracking_timeline]` — the order-status timeline customers ask for, fed by AIV webhook events.
* Hold-at-Location suggestion — when a delivery-exception AIV event arrives, the customer is automatically emailed the nearest FedEx counters with directions (requires tracking webhooks + shared secret).
* Residential address classification — the Address Validation API can classify residential vs. commercial addresses; the `fshipping_address_classified` hook and the savings meter let developers record and display avoided residential surcharges. (Automatic checkout reclassification is not enabled by default.)

= Admin features =

* Per-order special services — Saturday delivery, signature options, dry ice, COD and Hold-at-Location are honored from order meta when set (by custom code or the bundled meta-box classes).
* Dangerous goods flags — hazmat, alcohol and dry-ice markers per order are spliced into the label payload's special-services block.
* Shipment dashboard — every shipment with tracking ID, service, charges, label actions, pickup, returns, trade documents, status badges and SPOD.

= Built for production =

* REST-native. Zero SOAP. The 2026 FedEx SOAP retirement is a non-event.
* Checkout Safety Net — if the FedEx rate lookup returns nothing, the `fshipping_safety_net_fallback` filter lets developers serve a configured fallback rate so checkout still renders. No fallback is applied unless you register one.
* Staging Mode — set the `fshipping_staging_mode` option to log every rate call (destination, item count, method) to the staging debug log for review.
* HPOS (High-Performance Order Storage) compatible.

== Installation ==

1. Upload the `teamzlab-fedex` folder to `/wp-content/plugins/` (or install via **Plugins → Add New → Upload Plugin**).
2. Activate the plugin through the **Plugins** screen.
3. Go to **WooCommerce → Settings → Shipping → Zones** and enable **FedEx Shipping** on the zones that need it.
4. Open **Fedex → Settings** and enter:
   * Client ID + Client Secret (core scope) — from [developer.fedex.com](https://developer.fedex.com).
   * Account number — your FedEx billing account.
   * Environment — sandbox to start, production when ready.
5. (Optional) **Fedex → Settings** (tracking section) — add your separate tracking-scope Client ID + Secret (FedEx issues them as two separate projects).

== Frequently Asked Questions ==

= Will this break when FedEx SOAP retires? =

No. This plugin has always been REST-native; there is no SOAP code to retire. If you're coming from a SOAP-based plugin whose store broke after 2026-06-01, enter FedEx sandbox credentials under **Fedex → Settings** and switch the environment to sandbox to verify your flows before going back to production.

= Is there a free tier? =

Yes — the unlicensed/free tier is fully functional (live rates, labels, tracking webhooks, Return Tag portal), with a **daily quota of 10 checkouts served with FedEx rates and label purchases**. Paid tiers raise the quota to 100/day; the highest tiers are unlimited. Developers can override the limit with the `fshipping_daily_shipment_limit` filter. Once the quota is reached, FedEx rates pause at checkout (a notice is shown) until the next day — other shipping methods are unaffected.

= Does it work with Dokan / WCFM multi-vendor? =

The plugin uses one store origin from **Fedex → Settings** for FedEx rate calls. Multi-vendor marketplaces can still vary ship-from per package by attaching `_fshipping_origin` (`postal_code`, `country`, `state`) in custom code on the `woocommerce_cart_shipping_packages` filter; the rate client reads that when building the FedEx payload.

= Does it support HPOS (High-Performance Order Storage)? =

Yes. The plugin declares HPOS compatibility in its header and uses `\WC_Order` APIs exclusively — never direct post-meta for order fields that HPOS migrated to `wc_orders_*`.

= What happens if FedEx returns a 500? =

FedEx rates simply don't render for that recalculation — the plugin logs the failure and checkout continues with any other configured shipping methods; it never blocks the order. If you want a fallback price instead, register one with the `fshipping_safety_net_fallback` filter (no fallback is applied unless you do — there is no settings field for it).

= Do I need two FedEx developer accounts? =

Technically FedEx issues OAuth credentials as **two separate projects** — one covering Ship + Rates + Address (the "core" scope), one covering Tracking + AIV webhooks. They're free to create on the same FedEx developer account. Enter both pairs in the plugin settings; otherwise tracking calls return 401.

= Is there a live demo? =

Install the plugin on a WordPress sandbox — every feature works with FedEx's public sandbox credentials. Enter them under **Fedex → Settings** and switch the environment to sandbox.

== Screenshots ==

1. Fedex → Settings, Authorization tab — enter your FedEx REST API credentials (core scope).
2. Fedex → Settings, Account tab — FedEx account number, ship-from origin, and sandbox/production environment.
3. FedEx Pickup History — schedule one consolidated global pickup and audit which pickup covered which shipments.
4. Shipments & Labeling — every shipment with tracking ID, service, total charge, label actions, pickup, return, trade documents, status, and SPOD.

== Changelog ==

= 1.2.2 =
* Security: AIV webhook receiver now **fails closed** — with no shared secret configured every delivery is rejected instead of trusted.
* Security: Cancel-shipment AJAX requires shop-manager capability and is no longer reachable by logged-out visitors.
* Security: Shipment records are no longer publicly queryable (REST / permalinks disabled).
* Security: Return Tag portal and the tracking-timeline shortcode now require the order's billing email as proof of ownership.
* Security: Rate-quote rate limiting no longer trusts spoofable forwarding headers; the label PDF folder is hardened against direct browsing.
* Fixed: Orders placed through **block checkout** now create instant FedEx shipments (the Store API hook was never wired, so block-checkout orders silently got no label).
* Fixed: Multi-quantity orders no longer fail label purchase with `DIMENSIONS.EXCEEDS.LIMITS` — package dimensions are now stacked along the smallest dimension instead of multiplying every axis by quantity.
* Fixed: LB↔KG weight conversion silently no-opped (Woo expects the `lbs` slug), so labels could declare the wrong weight.
* Fixed: Duplicate checkout events can no longer purchase duplicate labels for one order (per-order guard + order note).
* Fixed: Locations asset versioning referenced an undefined constant.

= 1.2.4 =
* Added: **FedEx packaging presets on Product Box** — box cards can auto-fill dimensions (and name) from FedEx Envelope/Pak/Tube/Box supplies.
* Changed: Human-readable dropdown labels everywhere — settings selects and the shipments Service column no longer show raw FedEx API enums.
* Changed: Admin tables scroll cleanly on narrow screens; the shipments list keeps its **Download SPOD** action pinned while scrolling.
* Fixed: Invisible labels on **Schedule pickup** / **Save Product Box** primary buttons.
* Fixed: Select2 multi-select chip overlap and collapsed search field.
* Fixed: **Remove box** / **Remove** buttons rendered as raw browser chrome.
* Fixed: Ghost controls bleeding through the sticky Settings save bar.
* Fixed: Freemius **Upgrade ➤ / Account** menu missing (it pointed at a renamed menu slug).
* Fixed: Open Ship weight-normalization fatal (`Class "FSHIPPING\OpenShip\Helper" not found`) on the affected code path.

= 1.2.3 =
* Fixed: **WooCommerce Block Checkout is fully supported** — rates render for the entered address, address and cart changes re-quote through the Store API, the placed order keeps the correct destination and shipping amount, and instant labels use the final saved order data. A FedEx error during post-checkout label creation no longer breaks checkout with an HTTP 500 (orders can no longer be stranded at pending by label errors).
* Fixed: Deactivating and reactivating the plugin no longer removes the FedEx method from your shipping zones.
* Fixed: Rate caching rebuilt — no per-request FedEx hammering, no stale UI rates, no re-quote on rate selection; a settings change forces exactly one recalculation.
* Fixed: Daily shipment quota now works on HPOS stores and maps every plan (agency/golden unlimited, pro/premium/core 100/day, free 10/day).
* Fixed: Shipment-confirmation emails can no longer repeat when WP-Cron re-delivers a scheduled event.
* Fixed: Uninstall cleanup now removes all plugin options (including staging mode and savings events) and scheduled email events.
* Security: Label, return-label, and SPOD PDFs now live in randomized per-install storage directories (old guessable URLs stop resolving); existing shipments keep working.
* Security: Full request/response payloads (customer PII) are no longer written to logs unless WP_DEBUG is enabled.
* Security: AIV webhook deliveries are deduplicated (body hash + event id) with a staleness window.
* Security: Public tracking / address-validation / postal-validation AJAX endpoints are rate-limited per IP.
* Security: Order deletion is HPOS-aware (no orphaned order stubs left behind).
* Security: The bundled Freemius SDK can no longer silently disable TLS verification.
* Added: Shipped mocked regression tests (`php tests/regression.php hpos|legacy` — no WordPress needed).
* Changed: wp-admin screens and public shortcodes redesigned with a FedEx-branded design system; see CHANGELOG.md for the full screen-by-screen list.
* Changed: Removed **Webhook** and **Freight** tabs from **Fedex → Settings** (`admin.php?page=fedex_shipping`).
* Removed: **Fedex → Warehouses** admin page (`fshipping_warehouses`), `Origin\WarehouseRouter`, and automatic multi-warehouse cart splitting. Package `_fshipping_origin` is still honored when set by custom code (see FAQ: Dokan / WCFM).
* Removed: **Fedex → Migration Doctor** admin page (`fshipping_migration_doctor`) and its staging-mode POST handler. Staging mode remains via the `fshipping_staging_mode` option.

= 1.2.1 =
* Removed: **FedEx → Box library** admin submenu and the built-in box packer (`Packing\BoxLibrary`, `Packing\BoxPicker`, `Admin\BoxLibraryPage`). The `fshipping_rates_package_line_items` filter in rate requests is unchanged for custom code.

= 1.2.0 =
* New: Commercial invoice auto-generator — FedEx now produces the customs invoice PDF from your WooCommerce line items. `TradeDocuments` upload remains the escape hatch for custom templates.
* New: FedEx One Rate comparator — surfaces flat-rate pricing alongside standard rates at checkout with "save $X" badge when cheaper.
* New: Dimensional-weight calculator shortcode `[fshipping_dim_weight]` — pure client-side, embed on any page.
* New: Box picker — rate calls now use the smallest merchant-registered box that fits the cart.
* New: Multi-warehouse cart split — Dokan / WCFM stores get per-warehouse sub-packages automatically.
* New: Staging Mode dry-run logging + Checkout Safety Net runtime wiring.
* New: `Trade\CommercialInvoiceBuilder`, `Shipping\OneRateComparator`, `Packing\BoxPicker`, `Events\RuntimeIntegrator`, `Widget\DimensionalWeightCalculator`, plus Migration Doctor / Warehouses / Box Library admin pages.
* Fixed: Residential-surcharge savings are now recorded whenever Address Validation reclassifies residential → business. Dashboard widget previously always showed $0.
* Security: internal secret-scanning patterns tightened — now require value-looking literals after the key.

= 1.1.0 =
* Added shortcodes: `[fshipping_track]`, `[fshipping_locations]`, `[fshipping_tracking_timeline]`, `[fshipping_return_portal]`, `[fshipping_rate_widget]`.
* Added AIV tracking-webhook receiver + HMAC-SHA256 signature verification.
* Added REST `/fshipping/v1/rate-quote` endpoint for the embeddable widget.
* Added admin meta-boxes: Special Services, ETD Invoice, Close Day page.

= 1.0.0 =
* Initial release — 15 FedEx REST APIs, WooCommerce shipping method, tracking + labels.

== Upgrade Notice ==

= 1.2.1 =
Removes the Box library admin screen and built-in box picker from rate calls. Merchants who used only WooCommerce product dimensions are unaffected. Custom code may still use the `fshipping_rates_package_line_items` filter.

= 1.2.0 =
Major feature release. Adds commercial-invoice auto-generation, FedEx One Rate comparator, box-library pack routine, dimensional-weight calculator, and full runtime wiring for the Wave-1 Migration Lifeboat. Zero breaking changes. Update recommended for all international-shipping merchants.
