A complete stock-photo and stock-video marketplace for Larapen: sell your own media with watermarked previews, unlisted YouTube embedding, queued background processing, FTP/SFTP bulk import, and secure signed-URL downloads bundled with a per-project license PDF.

Sell Photos & Videos

Publish photo and video items with per-license pricing (Personal, Commercial, Extended) or offer them for free.

Watermarked Previews

Automatically generate watermarked previews and multiple thumbnail sizes so source files stay protected until purchase.

Unlisted YouTube Embedding

Upload a short, watermarked video preview clip to YouTube as an unlisted video and embed it on the detail page.

FTP / SFTP Bulk Import

Drop files into per-category folders on an SFTP server and let the scheduler import them automatically, with optional metadata sidecars.

Queued Processing

Heavy image and video work runs on dedicated queues so the admin panel stays responsive even during large imports.

Secure Downloads

Buyers receive temporary signed-URL links. Each download ships as a ZIP containing the source file, a license PDF, and a README.

Multiple Gateways

Checkout integrates with the core payment service (Stripe, PayPal, Paddle, MoMo). Free items skip the gateway entirely.

Use Cases

Photographer Portfolio Store

You are a photographer who wants to sell licensed downloads of your work.

  • Upload high-resolution sources: buyers only ever see watermarked previews until they pay.
  • Offer Personal, Commercial, and Extended license tiers at different prices on the same item.
  • Let customers buy without an account: a guest email is enough to receive the download link.
  • Each download bundles a license PDF naming the buyer and the single project it covers.

Video Footage Marketplace

You sell stock video clips and want safe previews without exposing the full file.

  • The add-on generates a short, watermarked preview clip and uploads it to YouTube as unlisted.
  • The unlisted YouTube player is embedded on the detail page; the source MP4 stays private.
  • Requires ffmpeg/ffprobe on the server for video processing.

Bulk Catalog from an SFTP Drop

You have thousands of files to publish and don’t want to upload them one by one.

  • Organize files into per-category folders on an SFTP server.
  • Add an optional .meta.json sidecar to set title, price, license, and status per file.
  • The scheduler scans every few minutes, enqueues an import job per new file, and processes them in the background.
  • Imported files move to a _processed folder; permanent failures move to _failed and admins are notified.

Requirements

  • Larapen CMS v1.0.0 or later
  • PHP 8.3+
  • MySQL 8.0+
  • The PHP ZIP extension (downloads are streamed as ZIP archives)
  • A running queue worker consuming the media, video, import and default queues
  • ffmpeg and ffprobe on the server: required for video items only (photo items work without them)
No extra PHP packages. The YouTube uploader talks to the YouTube Data API v3 directly over HTTP via Laravel’s Http facade, so no Google SDK is required. The add-on declares no Composer dependencies and no add-on dependencies.
Payments are optional. Selling paid items uses the core payment service and whichever gateway add-ons are active (Stripe, PayPal, Paddle, MoMo). If you only publish free items, no gateway is needed.

Installation

Step 1: Upload the Add-on

In the admin panel, go to Admin → Add-ons and click Upload Add-on. Select the add-on’s ZIP file: the system extracts it automatically and the add-on appears in the installed add-ons list.

Step 2: Activate the Add-on

Find Stock Media in the list and click Activate. Its migrations and permissions are set up automatically. Activation creates seven stockmedia_ tables and registers 14 permissions.

Step 3: Start Queue Workers

Heavy media work runs on dedicated queues. Start a worker that consumes all of them:

php artisan queue:work --queue=video,media,import,default --tries=3

Step 4: Configure

Navigate to Admin → Stock Media → Settings to set the storage disk, default pricing, watermarking, FTP/SFTP import, and YouTube. See Configuration.

Step 5: Create Categories & Items

Create at least one category under Stock Media → Categories, then add your first item under Stock Media → Items → Add Item.

Purchase Code (License Key)

Stock Media is sold as a separate product, so it has its own purchase code (license key), distinct from the purchase code of the main application and from the one of every other add-on. You are asked for it when you activate Stock Media in Admin panel → Add-ons.

Our products are sold on three platforms. The way you receive a purchase code depends on where you bought the product.

Platform / Marketplace How you get the purchase code Where to find it again
bedigit.com Store
In-site purchase (Shop)
Generated automatically when the order is paid, then sent by email, either in its own license email, or inside the order confirmation email. My Account → My Licenses on bedigit.com
Gumroad Created as soon as Gumroad notifies us of the sale, then sent in a separate email, in addition to the Gumroad receipt. The license email, your Gumroad Library, and My Account → My Licenses on bedigit.com
Envato Market
CodeCanyon
Issued by Envato, not by us, and never sent by email: you download it yourself from your Envato account. Envato account → Downloads → License certificate & purchase code
Check your spam folder. For both bedigit.com Store and Gumroad purchases, the purchase code is delivered by email. Automated license emails are very often filtered, so if the message is not in your inbox, look in your spam / junk folder before contacting support, and add our sender address to your contacts or allow list.

1. bedigit.com Store (in-site purchase)

  • As soon as the order’s payment status becomes Paid, a license key is generated automatically for every licensed item in the order (one key per purchased unit: buying 3 units gives 3 distinct keys).
  • It is emailed to the address used on the order, either in a dedicated license email or inside the order confirmation email. Check your inbox and your spam / junk folder.
  • The key stays available in your account under My Account → My Licenses. Keys are masked in the list; open the license detail page to reveal and copy the full key, see the domains it is activated on, and deactivate a domain to free an activation slot.
  • The matching invoice is under My Account → My Orders.

2. Gumroad

  • A Gumroad purchase produces two separate emails: the Gumroad receipt (sent by Gumroad, giving access to the files) and a license key email (sent by bedigit.com) that contains your purchase code.
  • The license key email is generated as soon as Gumroad notifies us of the sale, so it normally arrives within seconds of the payment. Here too, check your inbox and your spam / junk folder.
  • When the Gumroad product uses Gumroad’s own license-key feature, the same key also appears in your Gumroad receipt and under Library → your purchase on gumroad.com.
  • Use the same email address on bedigit.com as on Gumroad: your keys are then linked to your account automatically and listed under My Account → My Licenses, even if you register after the purchase. You can also add a Gumroad key manually from My Account → My Gumroad Licenses.

3. Envato Market (CodeCanyon)

  • Envato purchase codes are issued and delivered by Envato Market, never emailed by us, so there is nothing to look for in your spam folder: you retrieve the code from your Envato account.
  • Log in to your Envato / CodeCanyon account, open the Downloads page, find the item, and choose License certificate & purchase code from the Download dropdown. The code is written in that certificate.
  • An Envato purchase code looks like 12345678-90ab-cdef-1234-567890abcdef (8-4-4-4-12 characters). It never changes, and renewing item support does not issue a new one.
  • Official Envato article: Where Is My Purchase Code?
Lost your purchase code? Search your mailbox (spam folder included) for “license” or “purchase code”, then check My Account → My Licenses on bedigit.com for Store and Gumroad purchases, or Downloads → License certificate on Envato. If it is still missing, open a ticket on our Help Center with your order number (Store), Gumroad sale ID or buyer email (Gumroad), or Envato username and item name (Envato).

Configuration

Settings are managed on a single tabbed page at Admin → Stock Media → Settings (stored in the settings table with the stockmedia_ prefix). Config file defaults live in config/stockmedia.php. The page has four tabs (General, Watermark, FTP/SFTP, and YouTube) plus a Page Header tab for front-end header overrides.

General Settings

Setting Description Default
stockmedia_storage_disk Filesystem disk used to store original source files (e.g. local, s3). local
stockmedia_default_price Default price suggested when creating a new item. 0
stockmedia_default_currency Default 3-letter currency code for items without an explicit currency. USD
stockmedia_default_license_type Default license tier: personal, commercial, or extended. personal
stockmedia_max_downloads Maximum downloads allowed per purchased item. Set to 0 for unlimited. 5
stockmedia_download_expiry_days Days a download link stays valid after purchase. Set to 0 to never expire. 7
stockmedia_free_require_email Require an email address when claiming a free item (otherwise it is optional). true
stockmedia_payment_terms_override_enabled Override the global sale terms (Settings › General › Sale Terms) for the paid checkout. When off, the global settings apply. When not required, the stock media checkout keeps its generic terms checkbox. false
stockmedia_payment_terms_enabled While overriding: require buyers to accept the sale terms before paying. global value
stockmedia_payment_terms_page_id While overriding: the published page whose content is shown in the terms accordion. global value
stockmedia_image_quality JPEG/WebP quality (1–100) for generated previews and thumbnails. 85
stockmedia_related_items_count Number of similar items (2–6) shown in the “You may also like” block of a media detail page. Items from the same category and of the same media type come first; its “See more like this” link opens that filtered listing. 5

Watermark Settings

Watermarking is configured separately for photos and videos. Upload your watermark PNG (with transparency) to the core Media library first, then paste its Media ID into the appropriate field.

Setting Description Default
stockmedia_watermark_photo_enabled / stockmedia_watermark_video_enabled Master toggle for photo / video watermarking. off
stockmedia_watermark_photo_image_path / ..._video_image_path The Media library reference for the watermark overlay image. :
stockmedia_watermark_photo_position / ..._video_position One of top-left, top-right, center, bottom-left, bottom-right, tile. bottom-right
stockmedia_watermark_photo_opacity / ..._video_opacity Overlay opacity, 0–100. 60
stockmedia_watermark_photo_scale / ..._video_scale Overlay size as a percentage of the media width, 1–80. 20
stockmedia_watermark_video_clip_seconds Length (seconds) of the generated video preview clip, 3–60. 15
stockmedia_ffmpeg_binary / stockmedia_ffprobe_binary Paths to the ffmpeg / ffprobe binaries (used for video processing). system default
Test ffmpeg. The Watermark tab has a Test ffmpeg button that confirms Larapen can call the binary before you attempt a video upload.

FTP / SFTP Import Settings

Setting Description Default
stockmedia_ingest_enabledMaster toggle for the FTP/SFTP import pipeline.off
stockmedia_ingest_driverConnection driver: ftp or sftp.sftp
stockmedia_ingest_hostServer hostname.:
stockmedia_ingest_portServer port.22 (sftp) / 21 (ftp)
stockmedia_ingest_username / stockmedia_ingest_passwordConnection credentials.:
stockmedia_ingest_rootRoot path scanned on the remote server./
stockmedia_ingest_sslUse SSL for the FTP driver.off
stockmedia_ingest_delete_after_importDelete (rather than move to _processed) after a successful import.off
stockmedia_ingest_timeoutConnection timeout in seconds, 1–300.30
Test connection. The FTP/SFTP tab exposes a Test connection button that opens a session with the current credentials before you rely on the scheduler.

YouTube Settings

Setting Description
stockmedia_youtube_enabledMaster toggle for uploading video preview clips to YouTube.
stockmedia_youtube_client_id / stockmedia_youtube_client_secretOAuth 2.0 credentials from a Google Cloud project with the YouTube Data API v3 enabled.

After saving your client credentials, click Connect to YouTube and grant the requested scope. The refresh token is stored encrypted in the settings table.

Admin: Items

Items are the photos and videos you sell. Managed via Stock Media → Items.

Creating an Item

The item form (Items → Add Item) collects:

  • Type: photo or video.
  • Title, description, alt text, keywords (translatable).
  • Category: links the item to a core category.
  • Source file: the original high-resolution file, stored on the configured storage disk.
  • Per-license pricing: a price for each offered tier (Personal, Commercial, Extended). A tier with no price set is simply not offered. A tier priced at 0 is offered for free.
  • Currency and SEO title/description (translatable).
  • Status: draft, published, or archived.

Processing & Status

When an item is created or its source changes, the add-on dispatches a background job (ProcessPhotoJob or ProcessVideoJob) that generates the watermarked preview, thumbnails (thumbnail / medium / large), and, for video, a watermarked preview clip uploaded to YouTube. The item carries a processing status badge:

  • pending → queued, not yet processed
  • processing → a worker is generating previews
  • ready → previews generated; the item can be published and bought
  • failed → processing error (shown in admin notifications; the item carries a processing_error message)
An item only appears on the front-end when it is published and its processing status is ready (and its published_at date, if set, is in the past).

Reprocessing

If processing fails (for example because ffmpeg was missing), use the Reprocess action on the item to re-dispatch its jobs. The stockmedia.items.reprocess permission controls this action.

Admin: Categories

Categories organize your catalog and drive the front-end category pages. Managed via Stock Media → Categories. They reuse the core categories table with the stockmedia_item categorizable type.

  • Standard CRUD: create, edit, delete.
  • Reorder: a dedicated drag-and-drop reorder screen controls the display order of categories.
  • Categories are also used as the folder names for the FTP/SFTP import (see FTP Import).

Admin: Settings

All add-on options live on the single tabbed Settings page. See the Configuration section for the full reference of every tab and setting: General, Watermark, FTP/SFTP, and YouTube.

Admin: FTP / SFTP Import

The import pipeline lets you publish files in bulk from an SFTP (or FTP) server, organized by category slug. Managed via Stock Media → Import.

Folder Layout

Create one folder per category slug under the configured root. Each file may have an optional .meta.json sidecar that overrides title, price, license, and other fields:

/<root>/
  /sunsets/
    photo-001.jpg
    photo-001.jpg.meta.json   # optional
  /city-life/
    skyline.mp4
    skyline.mp4.meta.json
{
  "title":        { "en": "Sunset over Paris" },
  "keywords":     ["sunset", "paris"],
  "is_free":      false,
  "price":        9.99,
  "currency":     "USD",
  "license_type": "commercial",
  "status":       "published"
}

How Import Runs

  • The scheduler runs stockmedia:import every 5 minutes by default (configurable in config/stockmedia.php).
  • Each new file becomes a row in stockmedia_import_jobs and is processed in the background.
  • Allowed image types: jpg, jpeg, png, webp, avif. Allowed video types: mp4, mov, mkv, webm.
  • Successful files move to <root>/_processed/…; permanent failures move to <root>/_failed/… and admins receive a notification.
  • You can also trigger a run manually from the Import screen, or via the CLI:
php artisan stockmedia:import --limit=50
php artisan stockmedia:import --category=sunsets

Import Jobs Table

The Import screen lists recent import jobs with their category, detected type, status badge (queued, processing, imported, failed, skipped), and any error message.

Admin: Purchases

Every checkout (paid or free) creates a purchase record. Managed via Stock Media → Purchases.

  • List: purchase number, buyer email/name, total, currency, status badge, and date.
  • Detail: the purchased item(s), license tier, payment method and reference, transactions, and download counters.
  • Refund: an admin with the stockmedia.orders.refund permission can refund a paid purchase, which sets its status to refunded and revokes download access.

Purchase status values are pending, paid, failed, refunded, and expired. Purchase numbers follow the pattern SM-YYYY-000001.

Admin: Reports

The Sales report (Stock Media → Reports) summarizes revenue and sales volume over time. Requires the stockmedia.reports.view permission.

Dashboard Widgets

The add-on also contributes three widgets to the core admin dashboard:

  • Recent Sales: the latest completed purchases.
  • Top Items: best-selling items by purchase count.
  • Processing Queue: items currently pending or processing.

Front-end: Browse & Detail

Browse

The public catalog lives at /stock (and /{locale}/stock). Visitors can browse all published items and filter by category at /stock/category/{slug}. Each card shows the watermarked preview, title, and price.

Item Detail

The detail page at /stock/{slug} shows:

  • The watermarked preview (photo) or the embedded unlisted YouTube player (video).
  • Title, description, dimensions, aspect ratio, and, for video, duration and framerate.
  • The offered license tiers and their prices, with a per-tier coverage breakdown (worldwide, perpetual, single-project, commercial use, resale, etc.).
  • A Buy now (or Get for free) call to action.

Front-end: Buy & Checkout

Paid Items

  1. The buyer picks a license tier and clicks Buy now, landing on /stock/checkout/{slug}.
  2. They provide an email, optional name, and a project name (licenses are per-project), then choose a payment method.
  3. The add-on creates a pending purchase and hands off to the selected gateway (Stripe, PayPal, Paddle, or MoMo).
  4. When the gateway confirms payment, the purchase is marked paid and the buyer is sent a signed download link by email.

Free Items

Items whose selected tier is priced at 0 use the free flow at /stock/free/{slug}. The buyer still provides a project name (and an email if stockmedia_free_require_email is on). The purchase is created and immediately marked paid with total = 0, no gateway is involved, and the download link is dispatched.

No account required. Both paid and free checkouts work for guests. If the buyer is logged in, the purchase is linked to their account and appears under My Downloads.

Front-end: Download & Licenses

Downloads are served from a single signed route: /stock/download/{purchase}/{item}.

  • The link in the buyer’s email is a temporary signed URL that expires after stockmedia_download_expiry_days days (0 = effectively never).
  • Logged-in owners can also download directly from their account without a signed link (ownership is checked).
  • Each item can be downloaded up to stockmedia_max_downloads times (0 = unlimited). Exceeding the limit, an expired purchase, or a non-paid purchase returns an error.
  • Every download is recorded in an audit table (IP, user agent, bytes served).

What’s in the ZIP

The download is streamed as a ZIP archive containing:

  • The original source file.
  • A license PDF certificate naming the buyer, the project, and the license tier clauses.
  • A short README.txt for non-technical buyers.

License Tiers

Tier Summary
Personal Worldwide, perpetual, single project, personal media use. No commercial use, no resale.
Commercial Adds commercial use across all digital and print media. No resale.
Extended Adds resale within end products (the asset may not be redistributed on its own).

Front-end: My Downloads

Authenticated buyers get a My downloads entry in their account menu, linking to /account/stockmedia/purchases. It lists their purchases and links each one to a detail page (/account/stockmedia/purchases/{number}) where they can re-download purchased items (subject to the download limit and expiry).

Notifications

The add-on registers five notification types with the central notification system (Admin → Settings → Notifications):

Notification Recipient Trigger Channels
SendDownloadLinkNotification Buyer Purchase completed (paid) mail
NewSaleAdminNotification Admins Purchase completed (paid) mail, database
ItemProcessedNotification Admins An item finished background processing database
FtpImportFailedNotification Admins An FTP/SFTP import permanently failed mail, database
YoutubeUploadFailedNotification Admins A YouTube preview upload failed mail, database
Buyer link delivery is forced. The download-link notification is marked as a forced type, so it is always delivered regardless of per-type admin toggles. The other four can be toggled centrally.

Updating

There are two ways to update this add-on: via the admin panel (recommended) or by replacing files manually.

Method 1: Admin Panel Upload (Recommended)

  1. Download the latest .zip file of this add-on.
  2. Go to Admin panel → Add-ons and click Upload.
  3. Select or drag the .zip file into the upload area.
  4. A confirmation prompt shows the current and new version numbers. Click Replace to proceed.
  5. Go to Admin panel → System Update (/admin/update) to apply any pending database migrations.

Method 2: Manual File Replacement

  1. Replace the extensions/addons/stockmedia directory with the new version.
  2. Run php artisan migrate to apply new migrations.
  3. Clear caches: php artisan config:clear && php artisan view:clear && php artisan route:clear.
  4. Restart your queue workers so they pick up new job code.
Backup first: Always back up your database before running migrations on a production system.

Uninstallation

Switching an add-on off without losing anything is a deactivation: go to Admin panel → Add-ons, find Stock Media and click Deactivate.

  • Its routes, views, admin menu entries and permissions stop being registered, and its front-end pages stop answering.
  • Its database tables and all the data they hold are kept, and its files stay under extensions/addons/stockmedia/. Nothing is deleted.
  • The purchase code recorded at activation is kept too, so activating the add-on again does not ask for it.
  • Deactivation is refused while another active add-on depends on this one: deactivate that add-on first.

Click Activate on the same card to switch it back on. Pending migrations are re-run, assets are republished, and the add-on picks up exactly where it left off.

Removing

Removing is permanent and destroys the add-on's data. The Remove button only appears on a deactivated add-on, so removal is always two steps:

  1. Deactivate Stock Media (see Uninstallation).
  2. Click Remove on its card and confirm the prompt.

The admin panel then, in one pass:

  • runs the add-on's uninstall hook, if it ships one, while its code is still on disk;
  • revokes the permissions declared in its addon.json;
  • rolls back its migrations (this drops its database tables and every row they hold) and purges its entries from the migrations table, so a later reinstall migrates from scratch;
  • deletes its published assets: public/addons/stockmedia/, public/vendor/stockmedia/ and storage/app/public/addons/stockmedia/;
  • deletes the add-on directory extensions/addons/stockmedia/;
  • deletes its row in the addons table (the recorded purchase code goes with it) and clears the application cache.
This cannot be undone. Back up your database before removing an add-on whose data you may still need: installing it again later creates empty tables, not your old content.

Removal is refused, with an explanatory message and before anything is destroyed, when the add-on is still active, when another active add-on depends on it, or when the web server (PHP) user cannot delete extensions/addons/stockmedia/. In that last case, give that user write permission on the directory and on its parent, then try again.

Deleting the folder over FTP or SSH is not equivalent: the add-on's tables, its entries in the migrations table and its addons row are all left behind, and its card stays in the list. Use Remove in the admin panel instead.

Troubleshooting

Item stuck on “pending” or “processing”

  • Confirm a queue worker is running and consuming the media, video and import queues.
  • Check the failed_jobs table and storage/logs/laravel.log for job errors.
  • Use the Reprocess action (or php artisan stockmedia:reprocess {id}) to re-dispatch.

Video items fail processing

  • Video processing requires ffmpeg and ffprobe. Install them on the server.
  • Use the Test ffmpeg button on the Watermark settings tab to confirm Larapen can call the binary.
  • If the YouTube upload fails, the item still processes but the YouTube preview is marked failed and admins are notified.

Item not visible on the front-end

  • The item must be published and have processing status ready.
  • If a published_at date is set, it must be in the past.
  • At least one license tier must have a price set, otherwise the item offers nothing to buy.

FTP / SFTP import does nothing

  • Confirm stockmedia_ingest_enabled is on and the host is set, then use Test connection.
  • Files must live in a folder named after an existing category slug.
  • Run php artisan stockmedia:import manually to see queued counts; check that the scheduler is running.

Buyer didn’t receive the download link

  • Verify your mail configuration in .env (SMTP, Mailgun, etc.).
  • Ensure a worker is consuming the default queue (notifications are queued).
  • Check that the gateway webhook actually fired and the purchase is marked paid.

Download link returns an error

  • 410 Gone: the per-item download limit was reached, the link expired, or the purchase is no longer paid.
  • 403: an unsigned link was used by a non-owner. Use the signed link from the email, or log in as the buyer.
  • Ensure the PHP ZIP extension is installed and storage/app is writable.

Stock Media v1.0.0: Part of the Larapen CMS platform.

© BeDigit. All rights reserved.

Was this article helpful?

Thank you for your feedback!

Still need help? Create a support ticket

Create a Ticket
Sep 28, 2026