CartThrob 9 documentation for ExpressionEngine commerce teams Visit CartThrob.com
CartThrob.com

Selling software downloads

CartThrob validates and delivers downloadable files, while your order/account template decides which customer is entitled to each download. Treat those as separate layers: a valid file is not automatically an authorized purchase.

1. Choose the product field

Use an ExpressionEngine File field when editors should select the product from File Manager. Use CartThrob Download when the stored value must instead be a managed-file reference, an approved absolute server path, or an approved HTTP(S) URL.

Avoid a plain Text field for new stores. Upgraded stores may temporarily approve an existing Text field with cartthrob_download_fields, but its stored values still have to pass CartThrob 10 source validation.

See CartThrob Download fieldtype for the supported values and configuration.

2. Sell the product normally

{exp:cartthrob:add_to_cart_form entry_id="{entry_id}" return="cart"}
    <button type="submit">Add to cart</button>
{/exp:cartthrob:add_to_cart_form}

The download field belongs to the product entry. Do not post a customer-supplied path or URL through the add-to-cart form.

3. Render downloads inside an authorized purchase list

For a logged-in account page, query only the current member’s purchased items and bind the download form to the same member:

{exp:cartthrob:order_items member_id="CURRENT_USER"}
    {exp:cartthrob:download_file_form
        entry_id="{item:entry_id}"
        field="product_download"
        member_id="CURRENT_USER"}
        <button type="submit">Download {title}</button>
    {/exp:cartthrob:download_file_form}
{/exp:cartthrob:order_items}

download_file_form is the preferred account-page output because it does not expose a reusable link. If an email or other design requires a link, use get_download_link and bind it to a member or role when it must not be transferable.

Do not assume that encrypting a source proves a purchase. Test the page as the purchaser, a different logged-in member, and a guest.

4. Configure non-managed sources narrowly

Raw local files require an approved parent directory in cartthrob_download_roots. External URLs require their exact public origin in cartthrob_download_origins. Prefer a managed file when possible, and never approve the whole web root merely to make one legacy value pass.

For example, a product value of https://downloads.example.com/releases/app.zip needs this exact-origin approval:

$config['cartthrob_download_origins'] = [
    'https://downloads.example.com',
];

That entry does not approve another scheme, host, or nondefault port. Redirects must also remain on an allowed public origin, and private/reserved network destinations remain blocked. If an upgraded store keeps a Text field, approve its exact channel and field IDs separately with cartthrob_download_fields; approving the field does not approve every URL or path stored in it.

For all source rules, tags, upgrade review steps, and configuration, see Downloadable products and Configuration Overrides.