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.


/admin.Requirements
| Platform | Database | Files | Scheduler |
|---|---|---|---|
| VPS: Node.js 22 or newer, PM2 or systemd, Nginx in front | MySQL 8 or MariaDB 10.5+ | Disk, or S3-compatible | SCHEDULER=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-compatible | SCHEDULER=on |
| Cloudflare Workers: a Cloudflare account | D1 | R2 | Built in (Cron Triggers) |
| Vercel: a Vercel project | External 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)
- Create an empty MySQL 8 or MariaDB 10.5+ database and a user for it.
- 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 - Copy
.env.exampleto.envand fill inAPP_KEY(the valuenpm run keyprinted),APP_URL,APP_NAME,DATABASE_URLandCRON_SECRET, plusSCHEDULER=on. See Configuration. - Start the app with PM2 (or a systemd unit) so it restarts on reboot:
pm2 start npm --name vidoo-store -- start pm2 save - Point Nginx at the app. It listens on
127.0.0.1:3000unless you setPORT:
Add HTTPS with Certbot (or your panel), setserver { 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; } }TRUSTED_PROXIES=127.0.0.1so visitor IPs come through, then openhttps://example.com/install.
Docker
- In the extracted folder, copy
.env.exampleto.envand fill inAPP_KEY(runnpm run key, or use any 32-byte base64 key),APP_URL,APP_NAMEandCRON_SECRET. The includeddocker-compose.ymlsetsDATABASE_URLfor its own MariaDB 11 service. - Build and start the app and the database:
docker compose up -d --build - 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
- On your computer, in the extracted folder, install the packages and sign in to Cloudflare:
npm ci npx wrangler login - 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 - 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
- Create a MySQL database with a hosted provider such as Aiven, TiDB Cloud or PlanetScale, and an S3 or R2 bucket for files.
- Push the extracted folder to a Git repository and import it in Vercel (or deploy it with
npx vercel --prod). - In Project → Settings → Environment Variables, set
APP_KEY,APP_URL,APP_NAME,APP_TIMEZONE,DATABASE_URL,CRON_SECRET,FILES_DRIVER=s3and the S3 variables, then redeploy. - 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:

| Step | What it does |
|---|---|
| Check | Confirms the app key, site address and database connection are set and that file storage is writable. |
| Database | Creates every table and fills in the default roles, permissions and settings. |
| Admin | Creates your administrator account. |
| Site | Sets your site name and logo. |
| License | Enter the key from your purchase email, or skip it to start a 3-day trial (see Licensing). |
| Done | Shows 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.


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.
| Variable | What it is |
|---|---|
APP_KEY | Encryption key for sessions and encrypted settings. Generate it once with npm run key and never change it on a live site. |
APP_URL | Public address of the site, for example https://example.com. Used for links in emails, sitemaps and canonical tags. |
APP_NAME | Your site name. The session cookie name is based on it. |
APP_TIMEZONE | Time zone for stored dates, for example Asia/Kolkata. UTC when empty. |
DATABASE_URL | MySQL or MariaDB connection, for example mysql://user:[email protected]:3306/vidoo. Not used on Cloudflare (D1). |
CRON_SECRET | A long random string that protects /__cron/run. |
SCHEDULER | on to run scheduled jobs inside the app (one VPS or Docker server only). |
PORT, HOST | Where the Node server listens. Defaults: 3000 on 127.0.0.1. |
TRUSTED_PROXIES | Addresses 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_COOKIE | true once the site runs on https. |
FILES_DRIVER | Empty 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_STYLE | The 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/runevery 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:
- Log in at
/admin/login. - Open System → Software License.
- 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.

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

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.

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.


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.

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
| Platform | What you get |
|---|---|
| Cloudflare Workers | Edge 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 Docker | The app's own caching settings below. Put Cloudflare (or another CDN) in front for static assets, and set TRUSTED_PROXIES. |
| Vercel | Vercel'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.

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
- 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
404with{"message":"API is disabled."}. - Open Developers → API Keys and click New api key. Give it a title (for example the app that will use it).
- Tick the permissions this key needs: View files & folders, View articles, View announcements and View pages. Give each key only what it needs.
- 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.

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.
| Endpoint | Returns | Permission |
|---|---|---|
/api/v1/files | Active 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 folder | View files & folders |
/api/v1/folders | Folders, 50 per page | View files & folders |
/api/v1/folders/{slug} | One folder with its subfolders and files | View files & folders |
/api/v1/articles | Published articles with category and author, 20 per page | View articles |
/api/v1/articles/{slug} | One article | View articles |
/api/v1/announcements | Announcements, 20 per page | View announcements |
/api/v1/pages/{slug} | One page | View 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
- Open Developers → Webhooks and click New endpoint.
- Enter a name and the URL on your server that should receive the events, and tick the events you want (or Select all).
- Save, then reveal the Signing secret (it starts with
whsec_) and copy it into your receiver. Use Send test to send awebhook.testevent.

Events you can choose:
| Event | Sent when |
|---|---|
order.created | A customer placed an order (paid or awaiting payment) |
order.paid | An order became fully paid (gateway, wallet or staff) |
payment.succeeded | A payment was booked against an invoice |
file.purchased | A paid order included a file |
subscription.activated | A paid order included a download package |
subscription.expired | A member's download package ran out |
wallet.deposit.completed | A paid order added money to a member's wallet |
customer.created | Someone signed up, or staff added a member |
ticket.created / ticket.replied | A member opened a support ticket, or a public reply was added (internal notes are not sent) |
service.order.updated | A service order was completed, or rejected and refunded |
file.created / file.deleted | A 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:

VPS and Docker
- Replace the app files with the new release (keep your
.envand the storage folder). - Install the packages and restart:
On Docker, runnpm ci --omit=dev pm2 restart vidoo-storedocker compose up -d --buildinstead. - Open System → Updates and run the database migrations (or run
npm run migrateon 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.
- Back up the 3.x database and the
storage/appfolder. - Install 4.0.0 on the platform you want, and point
DATABASE_URLat 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). - Copy
APP_KEYandAPP_NAMEfrom the 3.x.envexactly. They keep members signed in and let 4.0.0 read the encrypted settings (SMTP and gateway passwords). - Start the app, sign in at
/admin/loginwith your existing admin account and run the migrations from System → Updates. - 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
.envand 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. UpdateAPP_URLif the domain changed. - Re-check your license under System → Software License if you're also changing domains.
Troubleshooting
| Problem | What to check |
|---|---|
| The app does not start, or shows a setup problem page | A 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 Nginx | The Node app is not running or listens on another port. Check pm2 status and that proxy_pass matches PORT. |
| Database connection refused | Check 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.x | APP_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 mismatch | The license already bound to a different domain. Contact support to re-bind it. |
| Scheduled tasks never run | No 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 Vercel | The Hobby plan limits Vercel Cron to daily. Use an external pinger. |
| Uploads fail with S3 storage | Check 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 VPS | Raise client_max_body_size in Nginx. |
| Emails never arrive | Test your SMTP settings using the "Send test email" button under Settings → Email (SMTP). |
| Every visitor shares one IP, or the login lockout hits everyone | The 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:cloudflareandnpm run deploy:cloudflare. - Settings in environment variables; scheduler with
SCHEDULER=onor/__cron/run. - Cloudflare only:
npm run setup:cloudflareone-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.