Digital Goods

Let sellers attach downloadable files to a listing, stored privately and served through a gated link.

Version
2.4.1
Updated
3 November 2026
Works with
Classifieds and shops
Needs
ShopClass 7.0 or later
PHP
8.0 or later
Tested with
ShopClass 7.1
Category
Listings & moderation
Price
Free
Licence
GPL-3.0+
Downloads
396
Made by
Navjot Tomer
  • Who can download, file limits, and charging sellers for attachments
    Who can download, file limits, and charging sellers for attachments

Lets a seller attach downloadable files to a listing (a manual, an ebook, a sample pack). Buyers get them through a download link that checks who may have them.

Requirements

  • ShopClass 7.0.0 or later, tested up to 7.1
  • PHP 8.0 or later, with the fileinfo extension (on by default)
  • Optional: S3 storage, set up in core under Settings → Storage

Install

  1. Open Plugins → Browse (or upload the zip from the GitHub release).
  2. Install.
  3. Activate.

Or run php oc-cli.php market:install digital-goods.

Set up

  1. Open Billing → Digital Goods (Plugins → Digital Goods on cores before 7.1).
  2. Under Categories, pick the categories that take files.
  3. Under Files, set Who can download, Files per listing (1 to 20), Largest file (MB) and File types.
  4. Optional, under Charging: tick Charge sellers to attach files and set Credits per listing. It is off by default.
  5. Save. Files now appear on the post and edit forms and on the listing page.

Billing → Downloads lists every attached file, its listing and its download count.

How it works

  • Uploads are stored under a random key. Nothing about where a file is kept comes from the uploader. The download address is a separate random token.
  • Every download goes through the download link, which applies the access rule and counts the fetch.
  • Who can download has four choices:
    • Signed-in visitors (default)
    • Anyone
    • Anyone who bought it, and the seller: needs a paid order for the listing (ShopClass 7.0 shop mode). Pending, cancelled and refunded orders do not count.
    • The seller only
  • The seller can always download their own files.
  • Charging: sellers spend credits per listing, like a bump or highlight. Listings that already have files keep them. Only the seller pays. Buyers pay for the listing through the shop.

Storage

StorageWhere files goDelivery
Local (default)oc-content/downloads/digital-goods/, closed to the webthe link streams the file
S3 with signed URLsthe bucketthe link redirects to a signed URL that lasts 5 minutes
S3, public bucketthe bucketthe link streams the file

A public bucket serves a file to anyone who has its address. The link never shows that address, but use signed URLs for private files. The settings screen warns when a public bucket is in use.

Allowed file types

An upload must match its claimed extension when the file is examined. Only these can be offered: zip, 7z, gz, tgz, rar, pdf, epub, mp3, wav, mp4, png, jpg, svg, txt, csv. The default is zip,7z,gz,pdf,epub. Anything else typed into File types is dropped on save.

The size limit is also capped by what PHP accepts (upload_max_filesize).

Notes / limits

  • nginx: Apache gets an .htaccess that denies the folder. nginx ignores it, so add this to your server block (ShopClass's Docker images already have it):
    location ^~ /oc-content/downloads/ {
        deny all;
    }
  • Private folder: to keep files out of the web root, set this in config.php before files are uploaded, or move the digital-goods folder there:
    define('DG_PRIVATE_PATH', '/var/lib/shopclass-files');
  • Faster downloads (optional): PHP streams files in chunks and answers byte ranges. To let the web server send the file instead, use one of these in config.php.

    nginx:

    location ^~ /dg-private/ {
        internal;
        alias /path/to/site/oc-content/downloads/digital-goods/;
    }
    define('DG_ACCEL_REDIRECT', '/dg-private/');

    Apache with mod_xsendfile (XSendFilePath set to the folder):

    define('DG_XSENDFILE', true);
  • Upgrading from 2.0.x: local files lived in oc-content/uploads/digital-goods/, which the web server serves. On the first request after the upgrade the plugin moves them to the new folder and closes the old one with an .htaccess. Download links do not change. A file that cannot be moved is still served, and the move is retried. On nginx you can also deny the old folder:
    location ^~ /oc-content/uploads/digital-goods/ {
        deny all;
    }
  • Stored data: one table, t_item_dg_file (listing, random key, download token, name, size, type, download count). Settings are preferences in the digital_goods section.
  • Uninstall deletes the files, the table and the settings.

For developers

  • Actions: init, item_form, item_edit, item_detail, posted_item, edited_item, delete_item, admin_menu_init, api_listings_prefetch.
  • Filters: api_listing, cart_needs_shipping (a cart of downloads asks for no address), billing_receipt_details (adds download links to the paid receipt), user_menu_filter.
  • On ShopClass 7.0, API listings carry ext.digital-goods.downloads: [{name, url}] for callers the rule lets in, an empty list for everyone else.

Support

https://github.com/mindstellar/shopclass-plugin-digital-goods/issues

What's new in 2.4.1

Changed

  • Uses getInstance() in place of the deprecated newInstance() and StorageManager::instance().

Found a problem with Digital Goods? Open an issue on GitHub.