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 fromfshipping_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
$_POSTat 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 tofree. - 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. TheShipmentQuota::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 matchfshipping_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\Checkoutadds 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_baselinerecords which day the counter belongs to so the count resets at local midnight. Licensing\FeatureGate::allows()returnstrueunconditionally (: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_capblocks trashing/deleting shipments that are not canceled.- Once canceled, deletion is allowed — and
before_delete_postpermanently deletes the linked WooCommerce order (wp_delete_post($order_id, true)).
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):
- 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 oneDELETE ... LIKE 'woocommerce_fshipping_custom%_settings'query. - Deletes the transients
fshipping_auth_tokenandfshipping_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). - Clears the scheduled
fshipping_send_shipment_emailcron event. - Force-deletes every
fedex_shippingpost — which triggers the delete cascade: any canceled shipment takes its linked WooCommerce order with it. - Leaves WooCommerce order meta (
_fshipping_*on order records) and the uploaded label folders underwp-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.