TrellisCart Documentation
Everything you need to install, configure, extend, and run TrellisCart — an open-source ecommerce platform by Trellis Group LLC.
Requirements
| Component | Minimum | Recommended |
|---|---|---|
| PHP | 8.0 | 8.2+ |
| MySQL / MariaDB | MySQL 5.7 / MariaDB 10.3 | MySQL 8.0+ |
| Web Server | Apache 2.4 / Nginx | Apache with mod_rewrite |
| PHP Extensions | PDO, pdo_mysql, json, mbstring | + sodium, curl, gd |
| HTTPS | Required for checkout and admin | Let's Encrypt |
| Disk Space | 50MB + uploads | SSD hosting |
What's Included
- Full storefront with product catalog, search, and category browsing
- Multi-step secure checkout with shipping and tax calculation
- Admin panel with orders, products, customers, reports, and content management
- PayPal Orders API v2 payment plugin with full webhook support
- FedEx, UPS, and USPS shipping rate plugins
- Zen-Cart importer for migrating existing stores
- Signed auto-update system with Ed25519 verification
- Role-based admin access control (staff, admin, superadmin)
- Zone-based tax rates by state and country
- Coupon and gift certificate system
Installation
Install TrellisCart on any standard PHP hosting environment in a few steps.
You will need: a web host with PHP 8.0+, a MySQL database, the ability to create database users, and HTTPS enabled on your domain.
Step-by-Step Installation
Create a database
In cPanel or your hosting panel, create a new MySQL database and a dedicated database user. Grant the user ALL PRIVILEGES on the new database. Write down the database name, username, and password.
Upload TrellisCart
Download the latest release ZIP from trelliscart.com. Upload and extract it to your server. The public/ directory should be your web root, or you can place TrellisCart in a subdirectory.
Run the installer
Visit https://yourstore.com/install/ in your browser. Enter your database credentials, store URL, and create your first admin account. The installer creates all database tables automatically.
Configure your store
Log into the admin panel at /admin/. Set your store name, email, currency, shipping methods, tax rates, and payment settings before accepting orders.
Delete the installer
After successful installation, delete or rename the install/ directory from your server. The installer is automatically locked after first use, but removal is best practice.
File Permissions
| Directory | Permission | Reason |
|---|---|---|
public/images/ | 755 | Web server needs write access for uploads |
storage/ | 755 | Cache, backups, and session storage |
includes/config.php | 640 | Protect credentials from other users |
| All other files | 644 | Web server read-only |
Do not accept live orders until you have configured and tested payment settings, tax rates, shipping rates, and email delivery. Use a test payment account first.
cPanel Shared Hosting
TrellisCart works on standard cPanel shared hosting. Common setup steps:
- Use MySQL Databases to create database and user
- Use File Manager or FTP to upload files
- Use MultiPHP Manager to set PHP version to 8.0+
- Use MultiPHP INI Editor to confirm
register_argc_argvis Off - Use SSL/TLS to enable HTTPS before going live
Configuration
TrellisCart stores its configuration in includes/config.php and in the database via the admin panel.
config.php
The includes/config.php file is created by the installer and contains server-level settings that cannot be changed from the admin panel.
PHP
The config file contains database credentials. Never commit it to a public repository. It is excluded by .gitignore by default.
Admin Panel Settings
All other settings are managed from the admin panel under Store → Configuration:
| Setting | Location | Description |
|---|---|---|
| Store Name | Configuration → General | Appears in page titles and emails |
| Store Email | Configuration → General | From address for all outgoing email |
| Currency | Configuration → General | Default currency code and symbol |
| Products Per Page | Configuration → General | Catalog pagination |
| Free Shipping Minimum | Store → Shipping | Order total for free shipping |
| Tax Settings | Store → Tax Rates | Zone-based or global tax rates |
| Mail Server | Configuration → Email | SMTP host, port, credentials |
Email Configuration
TrellisCart sends email via SMTP. Configure under Configuration → Email:
- Transport: SMTP (recommended) or PHP mail()
- Host: Your SMTP server hostname
- Port: 587 (TLS) or 465 (SSL) — avoid port 25 on shared hosting
- Username / Password: Your email account credentials
- From Address: Must match your sending domain for deliverability
Use the Send Test Email button to verify your settings before going live.
Upgrades
TrellisCart uses a signed update system. Every release ZIP is signed with Ed25519 and verified before installation.
The upgrade system creates an automatic backup before deploying. Store it securely. Test upgrades on a staging copy of your store first when possible.
Automatic Update Check
TrellisCart checks trelliscart.com/releases.json once per day for new versions. When an update is available, a notification appears in the admin dashboard.
Installing an Update
Go to Upgrade Center
In the admin panel, navigate to Admin → Upgrade Center. Available updates will be listed with version number and release notes.
Download and verify
Click Download Update. TrellisCart downloads the ZIP and its .sig manifest, verifies the SHA256 hash and Ed25519 signature. If verification fails, the update is rejected.
Install
Click Install Update. Files are extracted to a staging directory first, then deployed to live. Database migrations run automatically. A backup is created before any changes.
Manual Update
You can also manually place a release ZIP in storage/updates/. The ZIP must have a matching .sig manifest file alongside it. Without the manifest, the update is refused.
Rollback
If an update causes problems, go to Upgrade Center → Backup History and click Rollback on the backup taken before the update. The rollback restores files and database from the backup.
The Upgrade Center requires superadmin role. Staff and admin users cannot access it.
Products & Catalog
Manage your product catalog from the admin panel under Catalog → Products.
Adding a Product
Go to Catalog → Add Product
Fill in the product name, SKU, price, and description. SKU must be unique across your catalog.
Set pricing
Enter the regular price. Optionally set a sale price — if set, it displays as the active price with the regular price struck through.
Configure inventory
Enable stock tracking and enter the quantity. When stock reaches zero, the product shows as out of stock and cannot be added to cart.
Upload an image
Accepted formats: JPG, PNG, WebP, GIF. Maximum 5MB. Images are validated and renamed automatically on upload.
Assign to categories
Select one or more categories. Mark one as the primary category — this determines the product's canonical URL.
Product Fields Reference
| Field | Required | Notes |
|---|---|---|
| Name | Yes | Displayed on storefront and in orders |
| SKU | Recommended | Must be unique; used by importer for deduplication |
| Price | Yes | Base retail price |
| Sale Price | No | Active price when set; overrides regular price |
| Cost Price | No | Internal only; used for profit reports |
| Stock Qty | No | Required if Track Stock is enabled |
| Weight (lbs) | Recommended | Required for carrier rate calculation (FedEx, UPS, USPS) |
| Manufacturer | No | Brand; shown on product page and filterable |
| Sort Order | No | Lower number = appears first in listings |
Stock Behavior
Stock is not decremented when an order is placed. It decrements when:
- A PayPal payment is captured (via return URL or webhook)
- An admin manually changes order status from Pending to Processing or Shipped
This prevents abandoned carts from permanently reducing inventory.
Categories
TrellisCart supports unlimited nested category levels. Categories can have images, descriptions, and SEO settings.
Creating Categories
Go to Catalog → Categories and click Add Category. Set the name, parent category (for subcategories), and optionally upload a category image.
Category Images
Category images are displayed on the homepage category grid and category listing pages. Accepted formats: JPG, PNG, WebP, GIF. Images are stored in public/images/categories/.
Nesting
To create a subcategory, select a parent when creating the category. TrellisCart supports unlimited depth, but three levels (top → sub → sub-sub) is recommended for usability.
Category Slugs
Slugs are generated automatically from the category name. They must be unique. You can edit the slug when creating or editing a category. Changing a slug after products are linked may affect URLs.
Shipping
TrellisCart supports flat-rate shipping, free shipping thresholds, and live carrier rates from FedEx, UPS, and USPS.
Flat Rate Shipping
Configure flat rates under Store → Shipping:
- Standard: Default rate applied to most orders
- Expedited: Optional faster shipping option
- Overnight: Optional priority shipping option
- Free Shipping Minimum: Order total above which shipping is free
Carrier Plugins
Live carrier rates require API credentials from each carrier. Configure under Plugins → [Carrier] Shipping.
| Carrier | API Required | Credentials Needed |
|---|---|---|
| FedEx | FedEx Developer Account | Client ID, Client Secret, Account Number |
| UPS | UPS Developer Account | Client ID, Client Secret, Account Number |
| USPS | USPS Web Tools | User ID, Origin ZIP code |
Product weight in lbs must be set on each product for carrier rate calculation to work. Without weight, the carrier API cannot return accurate rates.
How Rates Are Calculated
At checkout, TrellisCart collects the customer's destination address and queries enabled carrier plugins. Available rates are displayed to the customer who selects their preferred method. The selected method and rate are stored on the order.
Tax Zones
TrellisCart supports per-state and per-country tax rates with a global fallback. Configure under Store → Tax Rates.
Zone-Based Tax
Enable Zone-Based Tax in the Tax Settings card to activate per-location rates. When enabled, the system looks up the customer's state and country at checkout and applies the matching rate.
Rate Lookup Order
- Exact state + country match (e.g., New Mexico + US)
- Country-wide rate with no state (e.g., all US states)
- Global catch-all rate (no country, no state)
- Global fallback rate from Settings
Adding a Tax Rate
Select country
Choose the country. The state dropdown populates automatically with all states or provinces for US and Canada.
Select state (optional)
Leave blank to apply to all states in the country. Select a specific state for a state-level rate.
Enter rate
Enter the combined rate as a percentage. For example, New Mexico: 5.125% state + 3.75% county = 8.875%. Enter 8.8750.
Multiple Rates (Stacking)
Multiple active rates for the same state are summed in priority order. Use this for separate state and county rates. Lower priority number = applied first.
Global Fallback
The global rate applies when no zone rate matches. Set it to 0 if you only collect tax in specific states.
Payment Setup
TrellisCart includes a PayPal Orders API v2 plugin. Card payments require a separate gateway plugin with client-side tokenization.
PayPal Setup
Create a PayPal Developer App
Go to developer.paypal.com → My Apps & Credentials → Create App. Get your Client ID and Client Secret.
Enter credentials in admin
Go to Plugins → PayPal Payments. Enter Client ID, Client Secret, and select Sandbox or Production mode.
Create a webhook
In PayPal Developer Dashboard, create a webhook pointing to https://yourstore.com/plugins/paypal/webhooks/paypal.php. Subscribe to: PAYMENT.CAPTURE.COMPLETED, PAYMENT.CAPTURE.DENIED, PAYMENT.CAPTURE.REFUNDED, CUSTOMER.DISPUTE.CREATED, CUSTOMER.DISPUTE.RESOLVED.
Enter Webhook ID
Copy the Webhook ID from PayPal Developer Dashboard and paste it into the Webhook ID field in TrellisCart PayPal settings. This enables signature verification on incoming webhooks.
TrellisCart rejects all PayPal webhooks if the Webhook ID is not configured. Without it, payment capture events cannot be processed automatically.
Order Payment Flow
- Customer selects PayPal at checkout
- TrellisCart creates a PayPal order and redirects customer to PayPal
- Customer approves payment on PayPal
- Customer is returned to your store and the capture is attempted
- On success: order status → Processing, stock decremented, coupon use incremented
- PayPal also sends a webhook confirming capture (idempotent — safe if both fire)
Card Payments
Raw card data is never collected or stored by TrellisCart. Card payments require a gateway plugin (Stripe, Authorize.net) that tokenizes card data on the client side before any data reaches your server. Orders placed via card stay in Pending status until the gateway confirms payment.
Coupons
Create discount codes under Catalog → Coupons. TrellisCart supports percentage, fixed amount, and free shipping coupon types.
Coupon Types
| Type | Effect | Example |
|---|---|---|
| Percent | Reduces subtotal by a percentage | SAVE10 → 10% off |
| Fixed | Reduces subtotal by a flat amount | SAVE5 → $5.00 off |
| Free Shipping | Sets shipping cost to $0.00 | FREESHIP → no shipping charge |
Coupon Options
- Minimum Order: Coupon only applies if subtotal meets this amount
- Max Uses: Limits total redemptions across all customers (0 = unlimited)
- Active: Toggle to enable or disable without deleting
Coupon Use Timing
Coupon use count increments only after payment is confirmed — not when the order is placed. This prevents abandoned orders from consuming coupon uses.
Zen-Cart Import
Migrate your existing Zen-Cart store to TrellisCart using the built-in importer. Go to Admin → Zen-Cart Import.
The importer is tested with Zen-Cart 1.5.x through 2.x. Database table prefix is configurable — common prefixes are zen_ and zen6b_.
What Gets Imported
| Data | Notes |
|---|---|
| Categories | Full tree with parent-child relationships preserved |
| Products | Name, SKU, price, description, weight, stock, manufacturer |
| Product Images | Copied from source path to TrellisCart; random safe filenames assigned |
| Category Images | Copied from same source images folder |
| Manufacturers / Brands | Brand names and URLs |
| Customers | Name, email, address — passwords are NOT migrated (security) |
| Orders | Past order history with line items |
| Reviews | Product reviews with ratings |
| Coupons | Active coupon codes |
Customer passwords are never imported. Zen-Cart uses a different hashing scheme. Customers will need to use the password reset flow to set a new password on their first login.
Running the Import
Connect to source database
Enter your Zen-Cart database host, port, name, username, password, and table prefix. Click Test Connection to verify.
Set image path (optional)
Enter the full server path to your Zen-Cart images/ folder (e.g. /home/user/public_html/zencart/images/). Required to copy product and category images.
Select data to import
Choose which data types to import. You can run the importer multiple times — existing records are matched by SKU and skipped if Skip Duplicates is enabled.
Run import
Click Run Import. Progress is shown in real time. A summary report is displayed when complete.
Re-running the Import
You can safely re-run the importer. With Skip Duplicates enabled, existing products (matched by SKU) are skipped. To do a full fresh import, clear the relevant tables in phpMyAdmin first.
Admin Users & Roles
TrellisCart uses role-based access control with three built-in roles. Manage users under Admin → Admin Users.
Roles
| Role | Access Level |
|---|---|
staff | View orders and catalog; upload media only |
admin | Edit orders, catalog, customers, coupons, content, and store settings |
superadmin | Full access including admin users, upgrades, imports, and plugins |
Staff Permissions
- View orders (read-only)
- View product catalog (read-only)
- Upload media images
Admin Permissions
Everything staff can do, plus:
- Edit orders and update order status
- Edit products, categories, manufacturers
- View and edit customers
- View reports
- Edit banners, newsletters, coupons, and gift certificates
- Change store settings
Superadmin Permissions
Everything admin can do, plus:
- Manage admin user accounts and roles
- Run upgrades and rollbacks
- Run Zen-Cart importer
- Manage and configure plugins
- Edit CMS pages (raw HTML)
- View activity logs
Assign the lowest role that meets each user's needs. Reserve superadmin for store owners and trusted developers only.
Managing Orders
View and manage all orders from Sales → Orders.
Order Statuses
| Status | Meaning |
|---|---|
| Pending | Order placed; payment not yet confirmed |
| Processing | Payment confirmed; being prepared for shipment |
| Shipped | Tracking number added; en route to customer |
| Delivered | Confirmed delivered |
| Cancelled | Order cancelled before fulfilment |
| Refunded | Payment refunded |
Stock and Coupon Accounting
When an admin changes an order from Pending → Processing (for COD or bank transfer orders), TrellisCart automatically decrements stock and increments coupon use count. This only happens once — subsequent status changes do not re-decrement.
Adding a Tracking Number
Open an order, scroll to the Shipping section, and enter the tracking number and carrier. The customer receives an email notification with a link to track their shipment.
Issuing a Refund
PayPal refunds can be initiated from the order detail page. For other payment methods, process the refund through your payment provider and then manually update the order status to Refunded.
Saved Carts
Abandoned carts are visible under Sales → Saved Carts. You can view cart contents and send a recovery email to the customer.
Email Setup
TrellisCart sends transactional emails for orders, account creation, and password resets.
Emails Sent Automatically
| Trigger | |
|---|---|
| Order Confirmation | Payment confirmed (status → Processing) |
| Order Status Update | Admin changes order status with notify enabled |
| Welcome Email | New customer account created |
| Password Reset | Customer requests password reset |
| Cart Recovery | Admin sends recovery email from Saved Carts |
SMTP Configuration
Go to Configuration → Email and set:
- Transport: SMTP
- Host: Your SMTP server (e.g.
smtp.yourdomain.com) - Port: 587 for TLS, 465 for SSL
- Encryption: TLS (recommended) or SSL
- Username / Password: Your email credentials
- From Name / Address: What customers see as the sender
Set up SPF, DKIM, and DMARC DNS records for your sending domain. Without these, transactional emails may land in spam folders.
Media & Images
Upload and manage images from Content → Media.
Upload Restrictions
- Allowed types: JPG, PNG, WebP, GIF (validated by file content, not extension)
- Maximum size: 5MB per file
- SVG: Not allowed (SVG can contain JavaScript)
- Filenames: Randomized on upload — original filename is not used
Image Locations
| Type | Path |
|---|---|
| Product images | public/images/products/ |
| Category images | public/images/categories/ |
| Banner images | public/images/ |
| General media | public/images/ |
The public/images/ directory has an .htaccess rule that prevents PHP execution. Even if a malicious file were uploaded, it cannot be executed.
Plugin System
TrellisCart plugins live in the plugins/ directory. Each plugin is a self-contained folder with its own PHP files, admin settings, and frontend handlers.
Plugin Structure
plugins/
└── myplugin/
├── myplugin.php ← Main plugin file (required)
├── admin/
│ └── settings.php ← Admin settings page
├── includes/
│ └── class-api.php ← Plugin classes
└── webhooks/
└── handler.php ← Webhook endpoint
Main Plugin File
The main plugin file (myplugin.php) must define plugin metadata and register the plugin with TrellisCart:
PHP'0', 'api_key' => '', 'api_mode' => 'sandbox', ];
Accessing TrellisCart Functions
All TrellisCart core functions are available in plugins after including bootstrap:
PHPrequire_once dirname(__DIR__, 2) . '/includes/bootstrap.php';
Plugin Settings Page
Plugin admin pages go in admin/settings.php inside your plugin folder. Require admin access at the top:
PHPrequire_once dirname(__DIR__, 3) . '/includes/bootstrap.php'; tc_require_admin('admin'); // or 'superadmin'
Storing Plugin Settings
Use the tc_configuration table to store plugin settings. All core config functions work for plugins:
PHP// Read a setting $apiKey = tc_config('myplugin_api_key', ''); // Save a setting tc_update(tc_table('configuration'), ['config_value' => $newValue], 'config_key=?', ['myplugin_api_key'] );
Every plugin admin page must call tc_require_admin() or tc_require_permission(). Every POST handler must verify the CSRF token with tc_csrf_verify(). Never skip these checks.
PHP Function Reference
Core functions available throughout TrellisCart and in plugins after loading bootstrap.
Database
Execute a SELECT query and return all rows as an associative array. Returns an empty array if no rows found.
$products = tc_rows(
'SELECT * FROM ' . tc_table('products') . ' WHERE is_active=? ORDER BY sort_order',
[1]
);
Execute a SELECT query and return the first row, or null if not found.
$product = tc_row(
'SELECT * FROM ' . tc_table('products') . ' WHERE product_id=?',
[$productId]
);
Execute a SELECT query and return the first column of the first row.
$count = tc_val('SELECT COUNT(*) FROM ' . tc_table('orders') . ' WHERE status_id=?', [1]);
Insert a row and return the new auto-increment ID.
$orderId = tc_insert(tc_table('orders'), [
'customer_id' => $customerId,
'status_id' => 1,
'total' => 49.99,
]);
Update rows matching the WHERE clause. Returns number of rows affected.
tc_update(tc_table('orders'),
['status_id' => 2],
'order_id=?', [$orderId]
);
Return the full table name with the configured prefix. Always use this instead of hardcoding table names.
$table = tc_table('products'); // Returns e.g. "tc_products"
Configuration
Read a configuration value from the database. Returns default if not found.
$storeName = tc_config('store_name', 'My Store');
$taxRate = (float) tc_config('tax_rate', '0');
Authentication
Redirect to admin login if not logged in, or show 403 if role is insufficient. Call at the top of every admin page.
tc_require_admin(); // Any logged-in admin
tc_require_admin('admin'); // Admin or superadmin
tc_require_admin('superadmin'); // Superadmin only
Show 403 if the logged-in admin does not have the specified permission.
tc_require_permission('orders_edit');
tc_require_permission('catalog_edit');
tc_require_permission('upgrade_manage');
Generate and verify CSRF tokens. Include in every form and verify in every POST handler.
// In form:
echo '';
// In POST handler:
if (!tc_csrf_verify($_POST['csrf_token'] ?? '')) {
die('Invalid request');
}
Helpers
HTML-escape a value for safe output. Always use when outputting user or database data in HTML.
echo e($product['name']); // Safe output
Strip HTML tags and trim whitespace from input. Use for plain text fields.
$name = tc_sanitize($_POST['name'] ?? '');
Redirect to a URL. Relative paths (starting with /) are prepended with the store base URL automatically.
tc_redirect('/account/'); // Relative
tc_redirect('https://example.com'); // Absolute
Return the store's base URL without trailing slash. Use for building links.
$url = tc_base() . '/catalog/search.php?q=' . urlencode($query);
Write an entry to the admin activity log.
tc_admin_log('plugin_action', 'Processed payment for order #' . $orderId);
Cart
Idempotent order finalization after confirmed payment. Updates status to Processing, decrements stock, increments coupon use. Safe to call multiple times — skips if already finalized.
// In a payment plugin after successful capture:
tc_finalize_paid_order($orderId, $captureTransactionId);
Hooks & Events
TrellisCart's hook system lets plugins respond to store events without modifying core files.
The hook system is designed for extensibility in future versions. Core integration points are available now via direct plugin file inclusion. A formal event dispatcher is planned for v1.1.
Current Integration Points
| File | Purpose |
|---|---|
plugins/paypal/return.php | PayPal return URL — payment capture on return from PayPal |
plugins/paypal/cancel.php | PayPal cancel URL — customer cancelled on PayPal |
plugins/paypal/webhooks/paypal.php | PayPal webhook endpoint — receives payment events |
plugins/fedex/rate.php | FedEx rate query — called during checkout shipping step |
plugins/ups/rate.php | UPS rate query |
plugins/usps/rate.php | USPS rate query |
Adding a Shipping Plugin
See the Shipping Plugins guide for the interface a shipping plugin must implement.
Adding a Payment Plugin
See the Payment Plugins guide for the interface a payment plugin must implement.
Shipping Plugins
Build a shipping rate plugin by creating a rate.php file in your plugin folder that returns available shipping methods.
How Shipping Plugins Work
- During checkout, TrellisCart scans enabled plugins for a
rate.phpfile - Each
rate.phpreceives destination info and cart contents and returns available rates - All rates from all enabled plugins are merged and displayed to the customer
rate.php Interface
PHP'mycarrier_ground', 'label' => 'My Carrier Ground (3-5 days)', 'cost' => 7.99, ], [ 'id' => 'mycarrier_express', 'label' => 'My Carrier Express (1-2 days)', 'cost' => 14.99, ], ];
Rate Object Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique method identifier stored on order |
label | string | Displayed to customer at checkout |
cost | float | Shipping cost in store currency |
estimated_days | int | Optional delivery estimate |
Payment Plugins
Payment plugins handle the checkout payment step. TrellisCart never stores raw card data — all payment plugins must use client-side tokenization or redirect flows.
Do not build payment plugins that collect card numbers, expiry dates, or CVV codes on your server. Use gateway-hosted fields (Stripe Elements, Authorize.net Accept.js) or redirect flows (PayPal) only.
Payment Flow
- Customer selects payment method at checkout step 2
- Customer reviews order at checkout step 3 and clicks Place Order
- Order is created in Pending status
- Customer is redirected to gateway or gateway-hosted fields collect a token
- On successful payment: call
tc_finalize_paid_order($orderId, $captureId) - Customer is shown order confirmation
Using tc_finalize_paid_order()
This function handles everything after a confirmed payment: status update, stock decrement, coupon increment. It is idempotent — safe to call from both the return URL and a webhook.
PHP// After confirming payment with your gateway: $success = tc_finalize_paid_order($orderId, $gatewayTransactionId); if ($success) { tc_redirect('/checkout/confirmation.php'); } else { // Already finalized or order not found tc_redirect('/checkout/confirmation.php'); }
Webhook Handlers
Payment webhooks must independently verify the request comes from the gateway before taking any action. Never trust a webhook payload without cryptographic verification.
PHP// Example: verify webhook signature before processing $rawBody = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_SIGNATURE'] ?? ''; if (!myGateway_verifySignature($rawBody, $signature, $webhookSecret)) { http_response_code(400); exit; } $event = json_decode($rawBody, true); // Process event...
Security Guide
Security practices built into TrellisCart and steps you should take before going live.
Built-In Security Features
| Feature | Details |
|---|---|
| Password hashing | Argon2ID with high memory and time cost |
| Session security | Session regeneration on login, secure + HttpOnly + SameSite cookies |
| CSRF protection | Session-bound token required on all POST requests |
| SQL injection | All queries use PDO prepared statements with parameterized values |
| XSS protection | All output escaped with e(); CMS content strip_tags'd |
| File upload validation | finfo MIME detection + getimagesize + 5MB limit + random filenames |
| PHP execution in uploads | Blocked by .htaccess in image directories |
| Admin brute force | IP + username lockout with exponential backoff |
| Update signing | Ed25519 + SHA256 mandatory on all updates |
| Security headers | CSP, HSTS, X-Content-Type-Options, X-Frame-Options |
| Role-based access | Granular permissions per admin action and page |
Before Going Live
- Enable HTTPS and redirect all HTTP traffic to HTTPS
- Set
TC_DEBUGtofalseinconfig.php - Use a strong, unique
SECRET_KEY(32+ random characters) - Change all default passwords immediately
- Delete or rename the
install/directory - Configure the updater public key in
UpgradeManager.php - Set up the PayPal Webhook ID before accepting PayPal payments
- Restrict admin access by IP in your server config where possible
- Ensure
storage/backups/is not web-accessible - Test order flow with a PayPal sandbox account before going live
Do not post security vulnerabilities publicly. Report them privately through the official contact method at trelliscart.com/security. See SECURITY.md in the release package for the full policy.
Password Policy
- Customer passwords: minimum 12 characters
- Admin passwords: use a password manager to generate long random passwords
- Never reuse passwords across services
- Admin accounts are locked after 5 failed login attempts with exponential backoff
Sensitive File Locations
These files contain sensitive information and must be protected:
| File | Contains | Protection |
|---|---|---|
includes/config.php | Database credentials, secret key | Not web-accessible; excluded from git |
bin/private.key | Release signing private key | Excluded from git; store offline |
storage/backups/ | Database dumps, file backups | Not web-accessible via .htaccess |
Best Practices
Recommendations for running TrellisCart reliably and securely in production.
Development Practices
- Never develop directly on a live store — use a staging environment
- Test all payment flows with sandbox accounts before going live
- Use version control for any customizations
- Keep
config.phpout of version control - Use
TC_DEBUG = trueonly in development — never in production
Plugin Development
- Always call
tc_require_admin()at the top of admin pages - Always verify CSRF tokens on POST requests with
tc_csrf_verify() - Use
tc_table()for all table names — never hardcode - Use prepared statements — never string concatenation for SQL values
- Escape all output with
e() - Log plugin actions with
tc_admin_log() - Use
tc_finalize_paid_order()for all payment confirmations - Never store API keys in PHP files that could be committed to git
Operations
- Back up the database daily — use the Upgrade Center backup tool or your host's backup service
- Monitor the admin activity log for unusual actions
- Review low stock alerts regularly
- Test email delivery monthly — send a test email from Configuration → Email
- Check for TrellisCart updates regularly — security patches are released as updates
- Review admin user list quarterly — remove inactive accounts
Performance
- Optimize product images before upload — aim for under 200KB per image
- Enable PHP OPcache on your server for significant performance improvement
- Use a CDN for static assets (images, CSS, JS) on high-traffic stores
- Keep the products-per-page setting reasonable (12–24) to avoid slow catalog pages
Release Signing
TrellisCart uses Ed25519 asymmetric cryptography to verify update packages. Every update ZIP must be signed before it can be installed.
How It Works
- You generate an Ed25519 keypair once using
bin/generate-keypair.php - The public key goes into
UpgradeManager.php— this is what stores use to verify - The private key stays with you, offline, never shared
- For each release, you run
bin/sign-release.phpwhich creates a.sigmanifest - The manifest contains the ZIP's SHA256 hash and an Ed25519 signature over that hash
- When a store installs an update, it verifies the hash and signature before extracting anything
Generating Your Keypair
BASHphp /path/to/trelliscart/bin/generate-keypair.php
This creates bin/private.key and bin/public.key. Copy the public key output and paste it into includes/classes/UpgradeManager.php at the RELEASE_PUBLIC_KEY constant.
Back up bin/private.key to a secure offline location immediately. If you lose it, you cannot sign future releases. Never commit it to git — it is excluded by .gitignore by default.
Signing a Release
BASHphp /path/to/trelliscart/bin/sign-release.php /path/to/trelliscart-1.0.0.zip
This creates trelliscart-1.0.0.zip.sig. Upload both the .zip and the .zip.sig to your download server.
Updating releases.json
After signing, add the SHA256 hash to your releases.json file and upload it to trelliscart.com/releases.json:
JSON{ "latest": { "version": "1.0.1", "download_url": "https://trelliscart.com/downloads/trelliscart-1.0.1.zip", "sha256": "paste-sha256-hash-here", "min_php": "8.0.0" } }
What Happens Without a Signature
If a ZIP is placed in storage/updates/ without a matching .sig file, the upgrade system refuses to install it and shows an error. This is intentional — unsigned updates are never installed.
Database Schema
TrellisCart uses a prefixed table structure. The default prefix is tc_. All tables use InnoDB with utf8mb4 charset.
Core Tables
| Table | Description |
|---|---|
tc_configuration | Key-value store settings (store name, email, etc.) |
tc_admin_users | Admin accounts with role and hashed password |
tc_customers | Customer accounts |
tc_address_book | Customer saved addresses |
tc_customer_groups | Customer groups with discount percentages |
Catalog Tables
| Table | Description |
|---|---|
tc_products | Product master data |
tc_categories | Category tree with self-referential parent_id |
tc_products_to_categories | Many-to-many product/category link |
tc_manufacturers | Brand/manufacturer records |
tc_product_images | Additional product images |
tc_option_names | Product option names (Size, Color, etc.) |
tc_option_values | Option values with price modifiers |
tc_product_attributes | Product/option value links |
tc_specials | Time-limited sale prices |
tc_featured | Featured product list |
tc_reviews | Customer product reviews |
Order Tables
| Table | Description |
|---|---|
tc_orders | Order master with billing, shipping, totals, payment |
tc_order_items | Line items for each order |
tc_order_history | Status change log per order |
tc_order_statuses | Status definitions (Pending, Processing, etc.) |
tc_saved_carts | Abandoned cart recovery data |
tc_coupons | Coupon codes and usage tracking |
tc_gift_certificates | Gift certificate balances |
System Tables
| Table | Description |
|---|---|
tc_tax_rates | Zone-based tax rates by country/state |
tc_tax_classes | Tax class definitions |
tc_zones | Geographic zone definitions |
tc_countries | Country codes and names |
tc_currencies | Currency definitions and rates |
tc_banners | Storefront banner content |
tc_pages | CMS page content |
tc_newsletter_subscribers | Email newsletter signups |
tc_admin_activity_log | Admin action audit trail |
tc_upgrades | Update and rollback history |
tc_db_migrations | Applied database migration tracking |
File Structure
Overview of the TrellisCart directory layout.
trelliscart/
├── index.php ← Entry point (includes public/index.php)
├── page.php ← CMS page renderer
├── includes/
│ ├── bootstrap.php ← Loaded by every page; sets up DB, session, auth
│ ├── config.php ← Database credentials and constants (not in git)
│ ├── functions/
│ │ ├── database.php ← tc_rows(), tc_row(), tc_insert(), etc.
│ │ ├── helpers.php ← e(), tc_sanitize(), tc_redirect(), etc.
│ │ ├── cart.php ← Cart functions and tc_finalize_paid_order()
│ │ ├── auth.php ← tc_require_admin(), RBAC, login functions
│ │ └── mail.php ← Email sending via SMTP
│ └── classes/
│ ├── UpgradeManager.php ← Signed update system
│ └── ZenCartImporter.php← Zen-Cart migration tool
├── admin/ ← Admin panel pages
│ ├── login.php
│ ├── index.php ← Dashboard
│ ├── orders/
│ ├── catalog/
│ ├── customers/
│ ├── reports/
│ ├── store/
│ ├── content/
│ ├── system/
│ └── upgrade.php
├── public/
│ ├── index.php ← Storefront homepage
│ ├── css/
│ ├── js/
│ └── images/
│ ├── products/ ← Product images (PHP execution blocked)
│ └── categories/ ← Category images
├── account/ ← Customer account pages
├── cart/ ← Cart page
├── catalog/ ← Search, category, product pages
├── checkout/ ← Checkout steps 1-4
├── plugins/
│ ├── paypal/
│ ├── fedex/
│ ├── ups/
│ └── usps/
├── templates/
│ ├── storefront/ ← Header, footer, product card templates
│ └── admin/ ← Admin sidebar, topbar templates
├── install/
│ ├── index.php ← Web installer
│ ├── schema.sql ← Full database schema
│ └── migrations/ ← Incremental schema updates
├── storage/
│ ├── backups/ ← Update backups (not web-accessible)
│ ├── updates/ ← Uploaded update ZIPs
│ ├── staging/ ← Temporary extraction during updates
│ └── cache/ ← Rate limiting and config cache files
└── bin/
├── generate-keypair.php ← One-time Ed25519 keypair generator
└── sign-release.php ← Sign a release ZIP for distribution
Key Configuration Files
| File | Purpose | In Git? |
|---|---|---|
includes/config.php | DB credentials, store URL, secret key | No — excluded by .gitignore |
bin/private.key | Release signing private key | No — excluded by .gitignore |
bin/public.key | Release signing public key | Optional — safe to commit |
storage/version.txt | Current installed version | No |
Changelog
Version history for TrellisCart.
v1.0.0 — Initial Release
April 2026 — Trellis Group LLC
Features
- Full storefront with category browsing, product pages, search, and featured products
- Multi-step checkout with address, shipping method selection, payment, and confirmation
- Complete admin panel — orders, products, categories, customers, reports, content, and settings
- PayPal Orders API v2 plugin with full webhook signature verification
- FedEx, UPS, and USPS live shipping rate plugins
- Zone-based tax rates by state and country with stacking support
- Coupon system — percent, fixed, and free shipping types with max-use limits
- Gift certificate system
- Zen-Cart importer — products, categories, customers, orders, reviews, coupons, and images
- Signed update system with Ed25519 + SHA256 mandatory verification
- Staged update extraction with rollback support
- Role-based admin access control — staff, admin, superadmin
- Granular per-action AJAX permissions
- Admin brute-force protection with IP + username lockout
- Customer login rate limiting
- Full security header suite — CSP, HSTS, X-Content-Type-Options, X-Frame-Options
- File upload validation via finfo + getimagesize + size limits
- Idempotent order finalization — safe for webhook + return URL simultaneous capture
- CMS pages and banner management
- Newsletter subscriber management
- Admin activity log
- Saved cart recovery with email sending
- Low stock reporting
- Sales and product reports
Security
- Argon2ID password hashing throughout
- Session regeneration on login (admin and customer)
- CSRF protection on all POST requests
- PDO prepared statements for all database queries
- Raw card data never collected or stored
- PayPal webhook signature verification mandatory
- Update signing mandatory — unsigned updates refused
- PHP execution blocked in upload directories