FluentCart downloads and storage

SkillFiles & storage

Implements and audits FluentCart downloadable products, entitlement checks, signed delivery URLs, download limits/expiry logs, Local/S3 storage, custom storage drivers, and Pro R2 behavior. Use when working with ProductDownload, OrderDownloadPermission, generateDownloadFileLink(), FileDownloader, fluent_cart/product_download/can_be_downloaded, fluent_cart/register_storage_drivers, customer download pages, subscription- gated files, private object storage, or preventing raw file-path disclosure.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the FluentCart downloads and storage skill

What this skill tells your AI

The instructions your AI receives, as published by lonsdale201/wp-agent-skills in fluentcart/fluentcart-downloads-storage/SKILL.md and read by ahel’s review.

Separate file metadata, commercial entitlement, signed delivery, and download accounting. Possession of a path, download ID, or signed URL must not create a new entitlement.

Read download-delivery.md before generating links, changing access rules, or implementing a driver.

Preserve the delivery chain

Use ProductDownload records to map product/variation availability to driver metadata. Use Order::getDownloads() or the customer-profile flow to obtain entitled files. Generate delivery links with Helper::generateDownloadFileLink($download, $orderId).

For customer delivery always include the qualifying order ID. The helper signs download_identifier, valid_till, and order_id. FileDownloader then validates signature/time, successful payment, a matching product post on one supplied order, subscription access validity, and the final can_be_downloaded filter before dispatching to the storage driver.

Do not:

  • expose Local file paths, S3/R2 keys, bucket names, or raw file_url values;
  • build signed query strings manually;
  • use the admin preview's order-less link as a customer entitlement link;
  • cache signed URLs beyond their validity;
  • assume a successful order grants every variation's files.

Do not assume the active route enforces limits or login

The customer-profile listing is login/customer scoped before it generates the URL. The resulting 1.6.0 signed URL is bearer authorization: FileDownloader does not re-check the current WP user and does not enforce the configured download_limit/download_expiry or update fct_order_download_permissions.

CustomerHelper contains limit/expiry/accounting code and DownloadService registers a fluent_cart/before_download_check_permission_and_store_log listener, but the active FileDownloader path does not emit that action in the tested source. Treat OrderDownloadPermission accounting as unwired for this route until a runtime test proves otherwise.

If the addon promises download limits, a customer-bound link, or single-use delivery, implement an addon-owned atomic claim before issuing/streaming the file and then delegate to the storage layer. Protect concurrent clicks and do not increment a count merely for rendering a link. Re-audit core after upgrade so enforcement is not accidentally duplicated.

Use fluent_cart/product_download/can_be_downloaded only to further constrain or explicitly extend a verified order context. Inspect orders and download from the payload; never return true for an unbound public identifier.

Extend storage drivers

Extend BaseStorageDriver, provide a settings class/fields, hiddenSettingKeys, driver implementation, connection verification, signed URL/download behavior, and bucket support if required. Attach the registration listener by fluentcart_loaded and call the driver instance's init() during fluent_cart/register_storage_drivers. The action runs on init priority 9, before fluent_cart/init, so registering its listener from fluent_cart/init is too late.

  • Keep credentials in hidden settings and exclude them from REST/UI responses.
  • Sanitize settings and verify connectivity without leaking provider errors.
  • Enforce least-privilege bucket policy and private objects.
  • Set a short provider URL expiry and validate filenames/content disposition.
  • Prevent path traversal and cross-bucket/object access.
  • Make upload/delete idempotent and distinguish missing from unauthorized.

Free 1.6.0 ships Local and S3. Cloudflare R2 is a Pro driver; Free only exposes a promo descriptor.

Subscription and license gates

Subscription downloads depend on Subscription::hasAccessValidity(), not merely the existence of a subscription row. Pro licensing also filters downloads when license validity applies. Compose restrictions; do not replace another module's false decision with true unless the addon deliberately owns that entitlement.

Test matrix

Test paid/unpaid/refunded order, signed-link reuse without login, wrong product/ variation, expired signature, tampered query, the source-confirmed absence or presence of core limit/expiry accounting, addon limit concurrency, active/ expired subscription, valid/expired Pro license, missing object, disabled/ misconfigured driver, filename injection, and cached URL after expiry.

Cross-references

  • Use fluentcart-products-inventory for file-to-variation assignment.
  • Use fluentcart-subscriptions-renewals for access validity.
  • Use fluentcart-licensing-pro for licensed software delivery.

References

  • Verified Free source paths:
    • fluent-cart/app/Models/ProductDownload.php
    • fluent-cart/app/Models/OrderDownloadPermission.php
    • fluent-cart/app/Models/Order.php
    • fluent-cart/app/Http/Controllers/WebController/FileDownloader.php
    • fluent-cart/app/Services/FileSystem/DownloadService.php
    • fluent-cart/app/Helpers/Helper.php
    • fluent-cart/app/Helpers/CustomerHelper.php
    • fluent-cart/app/Hooks/Handlers/GlobalStorageHandler.php
    • fluent-cart/app/Modules/StorageDrivers/
  • Verified Pro source path:
    • fluent-cart-pro/app/Modules/StorageDrivers/R2/

Signals

GitHub stars
22
Forks
2
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
fluentcart-downloads-storage
Source
github.com/lonsdale201/wp-agent-skills