The fshipping_options option

A single associative array holds every setting. Settings UI fields (see Settings Reference) write these keys: fshipping_client_id, fshipping_client_secret, fshipping_acc_number, fshipping_person_name, fshipping_phone_number, fshipping_postal_code, fshipping_city, state_provincecod, fshipping_country_code, fshipping_select_environemnt, fshipping_shipping_mode, fshipping_pickup_type, fshipping_rate_request_type, fshipping_shipping_methods, fshipping_enable_service_availability_filter, fshipping_enable_checkout_address_validation, fshipping_address_validation_allow_on_failure, fshipping_labeling_streetline, fshipping_labeling_packaging_type, fshipping_labeling_pickup_type, fshipping_labeling_payment_type, fshipping_labeling_label_format_type, fshipping_labeling_label_order, fshipping_labeling_stock_type, fshipping_labeling_rotation, fshipping_labeling_image_type, fshipping_labeling_bottom_orientation, fshipping_tracking_client_id, fshipping_tracking_client_secret, fshipping_aiv_shared_secret. Two keys need a warning before you touch them in code:
state_provincecod is the only key without the fshipping_ prefix — it is spelled exactly that way in includes/Dashboard/Settings.php and read back in includes/HTTP/Shipment.php. A blanket str_replace( 'fshipping_', '', $key ) or prefix-adding migration will miss it.
fshipping_select_environemnt (environment selector: “production” vs sandbox) is misspelled in the code (environemnt). The typo is load-bearing: Utils\Helper::is_production_environment() and the Settings page both read that exact key, so a “fix” in one place silently switches the site to sandbox behavior. Do not rename it; match it verbatim.

Hidden settings keys (no UI)

Eight keys live in the same array but no Settings tab renders them. They are read directly from fshipping_options by the classes below — set them with WP-CLI or a update_option mu-plugin snippet: fshipping_aiv_shared_secret is not in this table — despite its absence from the Authorization tab, it is a rendered field on Settings → Tracking (with a copy button), so it is a normal UI-backed setting. See AIV webhooks.

Sanitization is a blind merge

Dashboard\Settings::sanitize_fshipping_options() (includes/Dashboard/Settings.php:51-68) does array_merge( $prev, $input ) over the stored array and returns it. Consequences:
  • There is no key whitelist — any key present in $_POST at save time lands in the option, and keys absent from the form (including the eight hidden keys above) survive every save untouched.
  • The only value that is sanitized inside the callback is fshipping_aiv_shared_secret (sanitize_text_field). Everything else is stored exactly as posted, and is escaped at render time instead.
  • If you write your own settings importer, run your own sanitization before merging into fshipping_options.

Transients

All keys are exact; <md5(...)> is the md5 of the parenthesized value. The unsuffixed names fshipping_auth_token and fshipping_track_auth_token are not written by any current code path — see the uninstall caveat below.

Daily shipment quota

Licensing\ShipmentQuota throttles FedEx orders per day:
  • Plan detection (ShipmentQuota::quota_plan_slug(), :66-76) checks highest tier first — agency, golden, pro, premium, core — and falls back to free.
  • The Settings panel titled Daily Quota Controls (bottom of the Settings screen, Dashboard/Settings.php:639-703) is read-only: plan badge, today’s usage, a progress bar, and a “Quota is currently reached.” notice when applicable. There is no reset button. The ShipmentQuota::reset_today_quota_baseline() method exists (ShipmentQuota.php:263) but nothing in the plugin calls it — clearing the count is a WP-CLI/DB operation only.
  • Counting (raw_count_shipments_today()) is the number of orders created today whose shipping items match fshipping_custom / fshipping_custom% — HPOS-aware (reads the order table for the store’s storage backend; a posts-only count would be 0 under HPOS). Cached 60 s.
  • At checkout, the quota is enforced softly, twice. The precheck replaces FedEx rates with the quota message (“Checkout limit has been exceeded for today. Please try tomorrow.”, filter fshipping_quota_reached_checkout_message). If a late order still gets through, Frontend\Checkout adds an order note and skips the label — it does not throw, and the order completes without a shipment.
  • The baseline option fshipping_quota_daily_reset_baseline records which day the counter belongs to so the count resets at local midnight.
  • Licensing\FeatureGate::allows() returns true unconditionally (:102-105), so the quota is the only plan enforcement that actually runs.

The fedex_shipping post type

One post per purchased label. Registered non-public (includes/CPT/Shipments.php:810-832): 'public' => false, 'publicly_queryable' => false, 'show_ui' => true, 'show_in_menu' => false, 'query_var' => false, 'rewrite' => false, 'has_archive' => false, 'show_in_rest' => false, 'map_meta_cap' => true. There are no frontend single pages, archives, or sitemap entries — the admin list table (show_ui) is the only surface. See Architecture for the full registration snippet. Key meta: tracking number, label file(s), FedEx status, _fedex_shipment_order_id (link to the Woo order), AIV last event, Open Ship session data (advanced mode).

Delete cascade (destructive)

  • map_meta_cap blocks trashing/deleting shipments that are not canceled.
  • Once canceled, deletion is allowed — and before_delete_post permanently deletes the linked WooCommerce order (wp_delete_post($order_id, true)).
Never empty the trash with canceled shipment posts inside unless you intend to destroy the orders too. Code reference: CPT\Shipments::maybe_delete_linked_order().

Other options

Uninstall

uninstall.php runs the closure $fshipping_uninstall_delete_site() per site (multisite-aware via switch_to_blog):
  1. Deletes every option in the table above except fshipping_product_boxes — that one is not removed, so box templates survive reinstallation (remove it manually if you want a truly clean slate). Per-zone method instances go via one DELETE ... LIKE 'woocommerce_fshipping_custom%_settings' query.
  2. Deletes the transients fshipping_auth_token and fshipping_track_auth_token. Note these are the unsuffixed legacy names — current code never writes them (tokens are cached under the md5-suffixed keys above), so cached OAuth tokens are left behind until they expire naturally (worst case ~expires_in).
  3. Clears the scheduled fshipping_send_shipment_email cron event.
  4. Force-deletes every fedex_shipping post — which triggers the delete cascade: any canceled shipment takes its linked WooCommerce order with it.
  5. Leaves WooCommerce order meta (_fshipping_* on order records) and the uploaded label folders under wp-content/uploads/ (fedex-labels/, fedex-return-labels/, fedex-spod/) in place, deliberately — order history stays readable after uninstall.

Architecture

Where these options and transients are read and written.

Logging

The staging log option and everything written to the WooCommerce logger.