HomeExtensionsScriptsWordPressDocsSupport
Node.js scriptv4.0.0

Vidoo Store

Node.js script to run a paid file download store. Sell files and download packages, take payments, and manage everything from one admin panel. Runs on a VPS, Docker, Cloudflare Workers or Vercel.

This guide covers installing Vidoo Store 4.0.0 on each supported platform, the web installer, configuration and the scheduler, then licensing, the admin panel, updates, and moving over from the PHP 3.x version.

New in 4.0.0

Vidoo Store 4.0.0 is a Node.js rebuild that replaces the PHP and Laravel 3.2.x version. It uses the same database, so an existing 3.x store can move over without losing members, files or orders. See Upgrading from 3.x.

Overview

Vidoo Store is a self-hosted Node.js 22 application (Hono and TypeScript) for running a paid file, firmware, driver, or software download store. Visitors browse folders and files, buy single files or quota-based download packages, and you manage the whole storefront from an admin panel.

  • Public storefront: folders, files, articles/blog, FAQ, and a checkout flow.
  • Admin panel (default path /admin): files, orders, users, coupons, announcements, articles, and every setting.
  • Dark mode admin theme, on by default, with a per-install toggle.
  • Four platforms: a VPS, Docker, Cloudflare Workers or Vercel.
  • Files on disk by default (VPS and Docker), or in any S3-compatible storage such as Amazon S3, Cloudflare R2, Backblaze B2, MinIO or Wasabi.
Vidoo Store storefront home screen
The storefront in its default theme, with sample folders and files.
Vidoo Store admin dashboard screen
The admin dashboard at /admin.

Requirements

PlatformDatabaseFilesScheduler
VPS: Node.js 22 or newer, PM2 or systemd, Nginx in frontMySQL 8 or MariaDB 10.5+Disk, or S3-compatibleSCHEDULER=on
Docker: Docker with Compose (also Coolify, Railway, Render)MariaDB 11 from the included docker-compose.yml, or your own MySQL 8 / MariaDB 10.5+Docker volume, or S3-compatibleSCHEDULER=on
Cloudflare Workers: a Cloudflare accountD1R2Built in (Cron Triggers)
Vercel: a Vercel projectExternal MySQL (Aiven, TiDB Cloud, PlanetScale)S3-compatible (S3, R2, B2, MinIO, Wasabi)Vercel Cron, or an external pinger

To build or deploy you need Node.js 22 and npm on the machine you run the commands from. PHP is not used anywhere.

Install

Download the ZIP from the private link in your purchase email and extract it. Then follow the steps for your platform. Every platform ends at the web installer.

VPS (Node.js + MySQL or MariaDB)

  1. Create an empty MySQL 8 or MariaDB 10.5+ database and a user for it.
  2. Upload the extracted folder to the server, for example /var/www/vidoo-store, and install the packages:
    cd /var/www/vidoo-store
    npm ci --omit=dev
    npm run key
  3. Copy .env.example to .env and fill in APP_KEY (the value npm run key printed), APP_URL, APP_NAME, DATABASE_URL and CRON_SECRET, plus SCHEDULER=on. See Configuration.
  4. Start the app with PM2 (or a systemd unit) so it restarts on reboot:
    pm2 start npm --name vidoo-store -- start
    pm2 save
  5. Point Nginx at the app. It listens on 127.0.0.1:3000 unless you set PORT:
    server {
        server_name example.com;
        client_max_body_size 2g;
        location / {
            proxy_pass http://127.0.0.1:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    Add HTTPS with Certbot (or your panel), set TRUSTED_PROXIES=127.0.0.1 so visitor IPs come through, then open https://example.com/install.

Docker

  1. In the extracted folder, copy .env.example to .env and fill in APP_KEY (run npm run key, or use any 32-byte base64 key), APP_URL, APP_NAME and CRON_SECRET. The included docker-compose.yml sets DATABASE_URL for its own MariaDB 11 service.
  2. Build and start the app and the database:
    docker compose up -d --build
  3. Put your reverse proxy or panel in front of the app container and open /install.

On Coolify, Railway or Render, deploy the folder with its Dockerfile, add a MySQL or MariaDB service (or an external one) and set the same environment variables in the dashboard. Keep a persistent volume for the storage folder, or use S3-compatible storage.

Cloudflare Workers

  1. On your computer, in the extracted folder, install the packages and sign in to Cloudflare:
    npm ci
    npx wrangler login
  2. Run the setup command. It creates the D1 database, the KV namespace and the R2 buckets, sets up the Worker and deploys it:
    npm run setup:cloudflare
  3. Add your domain to the Worker under Workers & Pages → your Worker → Settings → Domains & Routes, then open https://example.com/install.

The scheduler is already set up as a Cron Trigger, so there is nothing else to add.

Vercel

  1. Create a MySQL database with a hosted provider such as Aiven, TiDB Cloud or PlanetScale, and an S3 or R2 bucket for files.
  2. Push the extracted folder to a Git repository and import it in Vercel (or deploy it with npx vercel --prod).
  3. In Project → Settings → Environment Variables, set APP_KEY, APP_URL, APP_NAME, APP_TIMEZONE, DATABASE_URL, CRON_SECRET, FILES_DRIVER=s3 and the S3 variables, then redeploy.
  4. Open https://your-project.vercel.app/install (or your own domain).

Vercel Hobby plan

On the Hobby plan Vercel Cron runs at most once a day, which is too slow for download-token expiry and package resets. Use an external pinger (cron-job.org, UptimeRobot or your own server) to call /__cron/run every minute, as shown under Scheduler.

Web installer

Open /install on your site. The installer is a 6-step wizard:

Vidoo Store installer check step screen
Step 1 checks the runtime, the environment variables, the database connection and file storage before anything is written.
StepWhat it does
CheckConfirms the app key, site address and database connection are set and that file storage is writable.
DatabaseCreates every table and fills in the default roles, permissions and settings.
AdminCreates your administrator account.
SiteSets your site name and logo.
LicenseEnter the key from your purchase email, or skip it to start a 3-day trial (see Licensing).
DoneShows where to sign in and locks the installer.

When it finishes, the installer locks itself and cannot be run again. Sign in at /admin/login.

Vidoo Store installer admin step screen
Step 3 creates the first administrator account.
Vidoo Store installer license step screen
Step 5: activate your license key, or start the 3-day trial and add the key later.

Configuration

All settings that the app needs before it can start are environment variables. On a VPS or Docker they go in .env in the app folder (or your process manager's environment); on Vercel in the project settings; on Cloudflare npm run setup:cloudflare writes them for you.

VariableWhat it is
APP_KEYEncryption key for sessions and encrypted settings. Generate it once with npm run key and never change it on a live site.
APP_URLPublic address of the site, for example https://example.com. Used for links in emails, sitemaps and canonical tags.
APP_NAMEYour site name. The session cookie name is based on it.
APP_TIMEZONETime zone for stored dates, for example Asia/Kolkata. UTC when empty.
DATABASE_URLMySQL or MariaDB connection, for example mysql://user:[email protected]:3306/vidoo. Not used on Cloudflare (D1).
CRON_SECRETA long random string that protects /__cron/run.
SCHEDULERon to run scheduled jobs inside the app (one VPS or Docker server only).
PORT, HOSTWhere the Node server listens. Defaults: 3000 on 127.0.0.1.
TRUSTED_PROXIESAddresses of Nginx, Cloudflare or a load balancer in front of the app, comma separated (or *), so login limits and IP-locked download links see the real visitor IP.
SESSION_SECURE_COOKIEtrue once the site runs on https.
FILES_DRIVEREmpty for files on disk, s3 for S3-compatible storage.
S3_ENDPOINT, S3_REGION, S3_KEY, S3_SECRET, S3_BUCKET_PUBLIC, S3_BUCKET_PRIVATE, S3_PATH_STYLEThe S3 connection when FILES_DRIVER=s3. The public bucket holds images and logos; the private bucket holds the files you sell.

Everything else (SMTP, payment gateways, caching, ads and so on) is set in the admin panel after install.

Scheduler

Background jobs (download-token expiry, package resets, cache cleanup, queued emails, backups) run every minute. Pick one of these:

  • VPS or Docker, single server: set SCHEDULER=on. The app runs the jobs itself. Turn it on for exactly one server, never on a copy that shares the live database.
  • Cloudflare Workers: built in, nothing to do.
  • Vercel, or several servers: call /__cron/run every minute with your secret:
    curl -s -H "Authorization: Bearer YOUR_CRON_SECRET" https://example.com/__cron/run

Warning

Without a scheduler the site still works, but download tokens, expired packages, old logs, queued emails and scheduled backups silently stop being cleaned up or run. System → Cron Tasks shows when each job last ran.

Licensing

Your license key is emailed to you automatically right after purchase. To activate it:

  1. Log in at /admin/login.
  2. Open System → Software License.
  3. Paste in your license key and save.

The license checks automatically once a day. It binds to the first domain it's checked from (example.com and www.example.com count as the same domain). If you need to move it to a different domain later, contact support.

Your license to run the version you have stays valid indefinitely. Free updates and support are included for 1 year from your purchase date; after that, your current version keeps working, but new updates and support require a renewal.

Skipping license activation in the installer starts a 3-day trial automatically, so you can finish setting the site up before activating.

Admin panel

Open https://your-domain.com/admin. Dark mode is on by default (you can change that under Settings → Dashboard Template).

  • Files & Folders: upload, organize, and price your downloads.
  • Orders, Transactions & Invoices: every purchase, with generated PDF invoices.
  • Users: manage customer accounts, roles, and permissions.
  • Announcements & Articles: sitewide banners and a built-in blog.
  • System: cron task history, module toggles, updates, and your software license.

Optional two-factor authentication is available per admin account under Account → Security.

Vidoo Store admin file manager screen
The file manager: folders and files in a grid or table, with bulk select, cut, copy and paste.

Site settings

Most of what visitors see is set under Settings → Website Template. These parts are worth knowing:

Vidoo Store website template settings screen
Website Template: logos, site icons, social links, Connect buttons, hero slider, home blocks and more.

Maintenance mode

Under System → Maintenance, turn maintenance on to show visitors a maintenance page (in your site's own header and footer) instead of the store. Set the heading, the message and an optional expected return time. The admin panel, payment callbacks and the installer keep working, and you can let specific IP addresses or ranges, or anyone who opens the private preview link, browse the site normally. The site does not reopen by itself at the return time: switch it off when you are done.

Vidoo Store maintenance settings screen
System → Maintenance, with an optional scheduled start.

Site icons

Website Template → General → Site icons lets you replace the browser tab icon (favicon.ico, favicon.svg, favicon-96x96.png), the iPhone and iPad home-screen icon (apple-touch-icon.png, 180 x 180) and the web app manifest with its 192 and 512 pixel icons. An empty slot keeps the default; the page title used on home screens is your site name.

Ads (Google AdSense)

The Ads tab adds your AdSense script just before </head> from your Publisher ID, or from code you paste. You can show ad units on each file's page (between the details and the Download button, and below the file card), hide ads from members who have a running package, and edit ads.txt in the same tab. An ad unit that gets no ad collapses completely, leaving no gap.

robots.txt

Website Template → SEO has a robots.txt editor with a one-click set of recommended rules (it keeps crawlers out of the admin panel, checkout and account pages). Leave it empty to allow everything.

Floating Connect buttons

The Connect tab shows WhatsApp, Telegram, Facebook and YouTube buttons plus a scroll-to-top button in a corner of every page, on the right or the left. A button appears only when its link is filled in under Social Links.

Packages & coupons

Beyond single-file purchases, you can sell download packages: a quota of downloads over a time window (for example, "50 downloads over 30 days"). Packages expire and reset automatically through the scheduler; nothing manual is needed once configured.

Coupons support both percentage and fixed-amount discounts, with optional usage limits and expiry dates, managed under Coupons in the admin sidebar.

Vidoo Store download packages admin screen
Catalog → Download Packages.
Vidoo Store pricing page screen
The public pricing page lists each package with its validity, bandwidth, fair-use limit and device count.

Payments

Vidoo Store has its own payment gateway settings for charging your customers, separate from however you paid for the script itself. Configure your gateway under Settings → Payment Gateways.

Customers can also pay from an in-site wallet balance, which supports peer-to-peer transfers between accounts.

Vidoo Store payment gateways screen
Payment gateways come installed in sandbox mode and switched off until you add your keys.

Email / SMTP

Order confirmations, invoices, and account emails send through the SMTP settings under Settings → Email (SMTP). Any standard SMTP provider works, including Google Workspace's SMTP relay. Use an app password rather than your normal account password.

Caching & performance

PlatformWhat you get
Cloudflare WorkersEdge page cache: guest pages are served from Cloudflare's network close to each visitor and cleared automatically when you edit a file, folder, article or page. Files are served from R2.
VPS and DockerThe app's own caching settings below. Put Cloudflare (or another CDN) in front for static assets, and set TRUSTED_PROXIES.
VercelVercel's CDN for static assets, plus the settings below. Keep the database in the same region as your Vercel functions.

Under Settings → Cache on every platform:

  • Automatic WebP/AVIF conversion: newly uploaded images get modern-format siblings, served with a fallback for older browsers.
  • Cloudflare integration: add your Cloudflare API token and zone ID to enable one-click cache purging from the admin panel.
  • CDN support: point asset and storage URLs at a CDN base URL.

Security

  • Two-factor authentication with trusted-device support, per account. It is asked for on the storefront sign-in and on the admin panel sign-in.
  • Changing or resetting a password signs out the account's remembered devices, staff-app sessions and trusted devices.
  • Login lockout after repeated failed attempts, plus a full login-attempt log.
  • IP blacklisting from the admin panel.
  • CSRF protection on every form.
  • Download links stop working after a set number of uses (default 10, change it under Settings → Downloads) as well as after they expire, and can be bound to the IP they were issued to.
  • Pages, articles and file descriptions saved by staff who are not administrators are stripped of scripts, and only administrators can upload SVG images. Administrators keep full HTML.
Vidoo Store file page screen
A file page. Download links expire and stop working after their use limit.

API and webhooks

Vidoo Store has a read-only catalog API, so another website, an app or a script can list your files, folders, articles, announcements and pages. Webhooks go the other way: the store sends a signed request to your server when something happens, such as a paid order.

Create an API key

  1. Check that the API is on under Settings → Modules → Public/Account API. It is on by default. While it is off, every API call answers 404 with {"message":"API is disabled."}.
  2. Open Developers → API Keys and click New api key. Give it a title (for example the app that will use it).
  3. Tick the permissions this key needs: View files & folders, View articles, View announcements and View pages. Give each key only what it needs.
  4. Save. The Credentials box then shows the API key (32 characters) and the API secret (48 characters). Switch Is active off to stop a key without deleting it.
Vidoo Store edit API key screen
Developers → API Keys: each key has its own permissions, key and secret.

Making requests

Every request is a GET to https://your-store.com/api/v1/... with the key and secret in two headers:

X-Api-Key: YOUR_API_KEY
X-Api-Secret: YOUR_API_SECRET

Responses are JSON. The API accepts calls from any origin (CORS), but treat the secret like a password: call the API from your own server, not from JavaScript in a public web page or a mobile app where people can read it.

EndpointReturnsPermission
/api/v1/filesActive files, newest first, 20 per page. Add ?folder=folder-slug to list one folder.View files & folders
/api/v1/files/{slug}One file with its folderView files & folders
/api/v1/foldersFolders, 50 per pageView files & folders
/api/v1/folders/{slug}One folder with its subfolders and filesView files & folders
/api/v1/articlesPublished articles with category and author, 20 per pageView articles
/api/v1/articles/{slug}One articleView articles
/api/v1/announcementsAnnouncements, 20 per pageView announcements
/api/v1/pages/{slug}One pageView pages

A single record comes back inside data:

{
  "data": {
    "id": 1,
    "title": "Nova_X5_Pro_Firmware_v3.2.1.zip",
    "slug": "nova-x5-pro-firmware-v3-2-1",
    "description": "Full stock firmware with flashing guide.",
    "body": null,
    "thumbnail": null,
    "price": 2,
    "is_free": false,
    "size": 3221225472,
    "formatted_size": "3 GB",
    "views_count": 18422,
    "downloads_count": 4210,
    "rating_avg": 0,
    "rating_count": 0,
    "is_featured": true,
    "folder": { "id": 12, "title": "Phones 2026", "slug": "phones-2026" },
    "created_at": "2026-11-03T12:40:38.000000Z"
  }
}

Lists add links and meta for paging. Ask for the next page with ?page=2, and stop when links.next is null:

{
  "data": [ ... ],
  "links": { "first": "...?page=1", "last": "...?page=3", "prev": null, "next": "...?page=2" },
  "meta": { "current_page": 1, "from": 1, "last_page": 3, "path": "...", "per_page": 20, "to": 20, "total": 48 }
}

Errors:

  • 401 {"message":"Invalid or unauthorized API key."}: the key or secret is wrong, the key is switched off, or it lacks the permission for that endpoint.
  • 404: no record with that slug, or the API is switched off.

Examples

cURL:

curl -H "X-Api-Key: YOUR_API_KEY" -H "X-Api-Secret: YOUR_API_SECRET" \
  "https://your-store.com/api/v1/files?folder=usb-drivers"

JavaScript (Node.js 18 or later, on your server):

const res = await fetch('https://your-store.com/api/v1/files?page=1', {
  headers: {
    'X-Api-Key': process.env.VIDOO_API_KEY,
    'X-Api-Secret': process.env.VIDOO_API_SECRET,
  },
});
if (!res.ok) throw new Error(`API error ${res.status}`);
const { data, links } = await res.json();
for (const file of data) console.log(file.title, file.formatted_size);

PHP:

$ch = curl_init('https://your-store.com/api/v1/articles');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'X-Api-Key: ' . getenv('VIDOO_API_KEY'),
        'X-Api-Secret: ' . getenv('VIDOO_API_SECRET'),
    ],
]);
$articles = json_decode(curl_exec($ch), true)['data'] ?? [];

Python, reading every page:

import os, requests

headers = {"X-Api-Key": os.environ["VIDOO_API_KEY"], "X-Api-Secret": os.environ["VIDOO_API_SECRET"]}
url = "https://your-store.com/api/v1/files"
while url:
    page = requests.get(url, headers=headers, timeout=15).json()
    for f in page["data"]:
        print(f["slug"], f["price"])
    url = page["links"]["next"]

Every call is recorded under Developers → API Logs, which helps when a key stops working or you want to see who is using it.

Webhooks

  1. Open Developers → Webhooks and click New endpoint.
  2. Enter a name and the URL on your server that should receive the events, and tick the events you want (or Select all).
  3. Save, then reveal the Signing secret (it starts with whsec_) and copy it into your receiver. Use Send test to send a webhook.test event.
Vidoo Store create webhook endpoint screen
Developers → Webhooks → Create Endpoint.

Events you can choose:

EventSent when
order.createdA customer placed an order (paid or awaiting payment)
order.paidAn order became fully paid (gateway, wallet or staff)
payment.succeededA payment was booked against an invoice
file.purchasedA paid order included a file
subscription.activatedA paid order included a download package
subscription.expiredA member's download package ran out
wallet.deposit.completedA paid order added money to a member's wallet
customer.createdSomeone signed up, or staff added a member
ticket.created / ticket.repliedA member opened a support ticket, or a public reply was added (internal notes are not sent)
service.order.updatedA service order was completed, or rejected and refunded
file.created / file.deletedA file was added or deleted in the admin file manager

Each event is a POST with a JSON body and three headers:

X-Webhook-Event: order.paid
X-Webhook-Id: 1842
X-Webhook-Signature: t=1793700000,v1=5f2c...e9

{
  "id": "evt_...",
  "event": "order.paid",
  "created_at": "2026-11-03T10:15:00.000Z",
  "data": { ... }
}

Answer with any 2xx status within 10 seconds. Anything else counts as a failure, and the store tries again after 1, 5, 30, 120 and 360 minutes. X-Webhook-Id stays the same on every retry, so you can skip deliveries you have already handled. Each endpoint's delivery history is kept for 30 days.

Check the signature before you trust a request. It is an HMAC-SHA256 of {t}.{raw body} with your whsec_ secret. In Node.js with Express:

import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';

const app = express();
const SECRET = process.env.VIDOO_WEBHOOK_SECRET; // whsec_...

app.post('/webhooks/store', express.raw({ type: 'application/json' }), (req, res) => {
  const header = req.get('X-Webhook-Signature') || '';
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const body = req.body.toString('utf8');
  const expected = createHmac('sha256', SECRET).update(`${parts.t}.${body}`).digest('hex');
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  const ok = parts.v1 && parts.v1.length === expected.length &&
    timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
  if (!ok || !fresh) return res.sendStatus(400);

  const event = JSON.parse(body);
  if (event.event === 'order.paid') {
    // fulfil the order in your own system
  }
  res.sendStatus(200);
});

Tip

Use the raw request body for the signature, not a re-encoded copy of the parsed JSON, or the check will fail. Rejecting timestamps older than 5 minutes stops old requests from being replayed.

Updating

System → Updates shows the installed version, and its Check button looks up the latest release on quintaphon.com. To install a new release:

Vidoo Store updates page screen
System → Updates on a VPS install.

VPS and Docker

  1. Replace the app files with the new release (keep your .env and the storage folder).
  2. Install the packages and restart:
    npm ci --omit=dev
    pm2 restart vidoo-store
    On Docker, run docker compose up -d --build instead.
  3. Open System → Updates and run the database migrations (or run npm run migrate on the server).

Vercel

Replace the files in your repository with the new release and push (or redeploy). Then run the migrations from System → Updates.

Cloudflare Workers

In the new release folder on your computer, run:

npm ci
npm run deploy:cloudflare

Cloudflare keeps your earlier versions. If something looks wrong, System → Updates shows the exact command to roll back to the previous version.

Updates and support are included for 1 year from purchase; after that your current version keeps working.

Upgrading from 3.x

Vidoo Store 4.0.0 works with the database of a 3.x (PHP and Laravel) store, so members, files, orders and settings carry over.

  1. Back up the 3.x database and the storage/app folder.
  2. Install 4.0.0 on the platform you want, and point DATABASE_URL at the same MySQL or MariaDB database. Point file storage at the same files (copy them to the new storage folder, or upload them to your S3 or R2 bucket).
  3. Copy APP_KEY and APP_NAME from the 3.x .env exactly. They keep members signed in and let 4.0.0 read the encrypted settings (SMTP and gateway passwords).
  4. Start the app, sign in at /admin/login with your existing admin account and run the migrations from System → Updates.
  5. Switch your domain over to 4.0.0 once you have checked it.

You can leave the PHP copy in place as a fallback until you are happy. Turn its cron entry off and use SCHEDULER=on (or /__cron/run) on the new copy only, so the jobs do not run twice.

Not in 4.0.0

The Real Easy Store import (System → Legacy Import in 3.x) is not part of 4.0.0. If you still need to import a Real Easy Store export, do it in 3.x first, then upgrade.

Backups and moving

On Cloudflare Workers, scheduled backups of the D1 database go to your Google Drive once you connect it under Settings → Backup, and you can restore one from the admin panel. Your files stay in R2.

On a VPS, Docker or Vercel, back up the database with mysqldump (or your provider's backups) and the storage folder (or your S3 bucket):

mysqldump --single-transaction -u user -p vidoo > vidoo-$(date +%F).sql
tar czf vidoo-storage-$(date +%F).tar.gz storage

To move to a new server:

  • Copy the app folder with its .env and storage folder, or reinstall the same version and copy them in.
  • Import your latest database dump on the new host.
  • Keep the same APP_KEY. Update APP_URL if the domain changed.
  • Re-check your license under System → Software License if you're also changing domains.

Troubleshooting

ProblemWhat to check
The app does not start, or shows a setup problem pageA required variable is missing or wrong: usually APP_KEY, APP_URL or DATABASE_URL. On a VPS, run pm2 logs vidoo-store to see the exact message.
502 Bad Gateway from NginxThe Node app is not running or listens on another port. Check pm2 status and that proxy_pass matches PORT.
Database connection refusedCheck the host, port, user and password in DATABASE_URL. Hosted MySQL (Aiven, TiDB Cloud, PlanetScale) usually needs SSL and allows only listed IPs.
Everyone was signed out after upgrading from 3.xAPP_KEY or APP_NAME differs from the 3.x .env. Copy both exactly and restart.
License shows "unlicensed"Confirm the key was entered exactly as emailed, with no extra spaces, under System → Software License.
License shows a domain mismatchThe license already bound to a different domain. Contact support to re-bind it.
Scheduled tasks never runNo scheduler is set up. Set SCHEDULER=on or call /__cron/run every minute (see Scheduler). A 401 from /__cron/run means the Bearer token does not match CRON_SECRET.
Jobs run only once a day on VercelThe Hobby plan limits Vercel Cron to daily. Use an external pinger.
Uploads fail with S3 storageCheck FILES_DRIVER=s3, the endpoint, keys and bucket names. For R2 and MinIO set S3_PATH_STYLE=true if your endpoint needs it.
Uploads over a certain size fail on a VPSRaise client_max_body_size in Nginx.
Emails never arriveTest your SMTP settings using the "Send test email" button under Settings → Email (SMTP).
Every visitor shares one IP, or the login lockout hits everyoneThe site is behind a proxy or Cloudflare. Set TRUSTED_PROXIES (see Configuration).

Changelog

4.0.0 (November 3, 2026)

  • Rebuilt from the ground up on Node.js 22 (Hono and TypeScript), replacing the PHP and Laravel 3.2.x line.
  • Runs on a VPS (PM2 or systemd with Nginx), Docker (Dockerfile and docker-compose with MariaDB), Cloudflare Workers (D1, KV and R2) or Vercel.
  • Files on disk or in any S3-compatible storage (FILES_DRIVER=s3).
  • New web installer at /install, with a 3-day trial when you skip the license step.
  • Helper commands: npm run key, npm run migrate, npm run build, npm run build:vercel, npm run setup:cloudflare and npm run deploy:cloudflare.
  • Settings in environment variables; scheduler with SCHEDULER=on or /__cron/run.
  • Cloudflare only: npm run setup:cloudflare one-command setup, edge page cache, Google Drive backups with in-admin restore, and rollback to earlier versions from the Updates page.
  • Read-only catalog API (/api/v1) with per-key permissions and API logs, and signed webhooks for orders, payments, members, tickets and files.
  • Works with an existing 3.x database. The Real Easy Store import is not included.

Need help?

Read this page from install through licensing first. If something still fails, note the exact error message, your platform (VPS, Docker, Cloudflare or Vercel), your Node.js version, and what you were doing when it happened.

Contact us on the support page.