E-Shop
Full e-commerce functionality for your Larapen site. Sell physical and digital products with a complete shopping cart, checkout flow, coupon system, order management, and merchant feed generation.
Product Management
Create products with variants, gallery images, sale prices, stock tracking, and SEO metadata. Supports internal, external, and digital product types.
Cart & Checkout
Session-based cart for guests, database-backed for authenticated users. Automatic cart merging on login. Guest checkout support.
Order Management
Complete order lifecycle with status tracking, payment status, stock management, email notifications, and digital download delivery.
Coupons & Discounts
Percentage or fixed-amount coupons with minimum order thresholds, usage limits, date ranges, and maximum discount caps.
Merchant Feeds
Generate product feeds for Google, Bing, Facebook, Amazon, TikTok, Yandex, and Baidu with customizable field mappings.
Structured Data
Automatic schema.org/Product JSON-LD output on product pages for rich search results. Supports variants, pricing, and availability.
Use Cases
Online Store
Sell physical products with inventory tracking, shipping calculations, and coupon discounts. Customers browse categories, add to cart, checkout, and track their orders.
Digital Product Sales
Sell downloadable files (eBooks, templates, software). Digital products skip shipping, and buyers receive time-limited download links after payment.
Affiliate / External Products
List products that link to external retailers. External products display a “Buy Now” button linking to the external URL instead of the internal cart flow.
Multi-Channel Commerce
Use the merchant feed system to syndicate your product catalog to Google Shopping, Facebook Commerce, Amazon, TikTok Shop, and other platforms.
Requirements
- Larapen CMS v1.0.0 or later
- PHP 8.3+
- MySQL 8.0+
- At least one payment gateway add-on (e.g. Stripe) for processing payments
Installation
Step 1: Place the Add-on
Copy or symlink the shop folder into your Larapen "extensions/addons" directory:
Step 2: Activate the Add-on
Go to Admin → Add-ons → Installed Add-ons and activate E-Shop.
Step 3: Run Migrations
This creates the following tables: shop_products, shop_product_variants,
shop_carts, shop_cart_items, shop_orders,
shop_order_items, shop_transactions, shop_coupons,
shop_product_feed_data, shop_category_feed_data,
shop_merchant_feed_platforms, and shop_merchant_feed_field_mappings.
Product categories use the core categories table with categorizable_type = 'product'.
Step 4: Set Permissions
The add-on registers 19 permissions (see Permissions). Assign them to admin roles via Admin → Users → Roles & Permissions.
Step 5: Configure
Navigate to Admin → Shop → Settings and configure currency, tax, shipping, and checkout options. See Configuration.
Configuration
All settings are managed in Admin → Shop → Settings (stored in the settings table, group shop).
Defaults are defined in config/shop.php.
General
| Setting | Description | Default |
|---|---|---|
shop_order_prefix |
Prefix for generated order numbers (e.g. ORD-20260307-00000001). | ORD |
shop_products_per_page |
Number of products per page in the catalog listing. | 12 |
shop_sidebar_position |
Sidebar position on listing pages: left, right, or none. |
right |
shop_grid_columns |
Number of product columns per row in grid view (1–4). | 3 |
shop_default_view_mode |
Initial product listing display mode: grid or list. |
grid |
shop_guest_checkout |
Allow customers to checkout without creating an account. | true |
Tax
| Setting | Description | Default |
|---|---|---|
shop_tax_enabled |
Enable tax calculation on orders. | false |
shop_tax_rate |
Tax rate as a percentage (e.g. 20 for 20%). | 0 |
shop_tax_inclusive |
Whether product prices already include tax. When enabled, the tax amount is extracted from prices rather than added on top. | false |
Shipping
| Setting | Description | Default |
|---|---|---|
shop_shipping_enabled |
Enable shipping charges on orders with physical products. | true |
shop_free_shipping_threshold |
Subtotal threshold for free shipping. Set to 0 to disable. | 0 |
shop_shipping_flat_rate |
Flat shipping rate applied when the order does not qualify for free shipping. | 10 |
Digital Products
| Setting | Description | Default |
|---|---|---|
shop_digital_max_downloads |
Maximum number of times a digital product can be downloaded per purchase. Set to 0 for unlimited. | 5 |
shop_digital_expiry_days |
Number of days after purchase before the download link expires. | 30 |
Stock & Notifications
| Setting | Description | Default |
|---|---|---|
shop_low_stock_threshold |
Stock quantity at which products are flagged as low stock. | 5 |
shop_notify_admin_on_order |
Send an email notification to admin users when a new order is placed. | true |
shop_notify_customer_on_order |
Send an order confirmation email to the customer. | true |
Environment Variables
Admin: Settings
The settings page (Shop → Settings) is organized into sections:
- General: Order prefix, products per page, sidebar position, grid columns, default view mode, shop page title/subtitle/breadcrumb label.
- Tax: Enable/disable tax, tax rate percentage, tax-inclusive pricing toggle.
- Shipping: Enable/disable shipping, flat rate, free shipping threshold.
- Checkout: Guest checkout toggle, require phone number, require shipping address.
- Digital Products: Max downloads per purchase, download link expiry in days.
- Stock: Low stock threshold.
- Notifications: Admin notification on new order, customer order confirmation.
Admin: Products
The Products page (Shop → Products) manages your product catalog.
Products List
A paginated table showing:
- Featured image (thumbnail)
- Name and SKU
- Category
- Price (with sale price if applicable)
- Stock quantity
- Status (draft / published / archived)
- Checkout type (internal / external)
Per-item actions: Edit, Delete. Bulk delete with checkbox selection.
Creating & Editing Products
The product form includes the following sections:
Basic Information
- Name (translatable): product title displayed in the catalog.
- Slug (translatable): URL-friendly identifier. Auto-generated from name if empty.
- Short Description (translatable): summary shown on listing pages.
- Description (translatable): full product description with WYSIWYG editor.
- Category: select from product categories.
- SKU: stock keeping unit identifier.
- Status: Draft, Published, or Archived.
- Published At: schedule publishing date.
Pricing
- Price: regular product price.
- Sale Price: optional discounted price (must be less than regular price to take effect).
- Currency: set from the site’s default currency setting.
Inventory
- Manage Stock: toggle stock tracking. When disabled, the product is always “in stock.”
- Stock Quantity: current inventory count.
Checkout Type
- Internal: standard add-to-cart checkout flow.
- External: product links to an external URL. Cannot be added to cart. Stock tracking is disabled.
Images
- Featured Image: primary product image (uses the media library).
- Gallery Images: additional product images displayed on the detail page.
SEO
- Meta Title (translatable)
- Meta Description (translatable)
Feed Data
- Brand, GTIN, MPN, Condition, Weight: used in merchant feed generation and schema.org structured data.
- Custom Labels (0–4): for merchant feed segmentation.
Product Variants
Products can have multiple variants, each with its own:
- Name (e.g. “Large, Red”)
- SKU
- Price (overrides product price if set)
- Stock Quantity
- Attributes (JSON: key-value pairs for size, color, etc.)
- Active toggle
Variants are synced on product save. Removed variants are automatically deleted.
Digital Products
When Is Digital is enabled:
- A Digital File upload field appears. Files are stored on the configured disk (default:
local) under thedigital-productspath. - Shipping is automatically excluded for digital-only orders.
- After payment, order items receive a time-limited download link.
- Download attempts are tracked via
download_countand enforced againstshop_digital_max_downloads. - Download links expire after
shop_digital_expiry_days.
External Products
When Checkout Type is set to External:
- An External URL field appears.
- The product cannot be added to the cart.
- The front-end displays a “Buy Now” button linking to the external URL.
- Stock management, digital file, and inventory fields are automatically disabled.
Admin: Categories
Product categories use the unified categories table with categorizable_type = 'product'.
Categories are managed via Shop → Categories.
- Name (translatable) and Slug (translatable, auto-generated).
- Description (translatable) and Meta Title / Meta Description (translatable).
- Parent Category: supports hierarchical nesting.
- Position: ordering value.
- Is Active toggle.
- Featured Image via the media library.
- Google Product Category: feed data for merchant feed generation (via
shop_category_feed_data).
Admin: Orders
The Orders page (Shop → Orders) provides a list of all customer orders.
Orders List
A paginated, filterable table showing:
- Order Number (format:
ORD-YYYYMMDD-00000001) - Customer name & email
- Items count
- Total (formatted with currency)
- Status badge (pending, processing, completed, cancelled, refunded)
- Payment Status badge (pending, paid, failed, refunded)
- Date
Per-item actions: View, Delete.
Order Detail
The order detail page (Shop → Orders → {order}) shows:
- Order summary card: order number, date, status, payment status, payment method.
- Customer info: name, email, phone, billing address, shipping address.
- Order items table: product name, SKU, price, quantity, total, digital badge, download info.
- Financial summary: subtotal, discount, tax, shipping, total.
- Coupon code (if applied).
- Transactions list: gateway, transaction ID, amount, status, type (payment / refund).
- Notes from the customer.
Status Updates
Admin can update order status and payment status via dropdown selectors:
- Update Status: sends
PATCH admin/shop/orders/{id}/status - Update Payment Status: sends
PATCH admin/shop/orders/{id}/payment-status
Order Statuses
| Status | Description |
|---|---|
pending |
Order placed but not yet processed or paid. |
processing |
Order is being prepared / fulfilled. |
completed |
Order has been fulfilled and delivered / downloaded. |
cancelled |
Order was cancelled. Stock is automatically restored. |
refunded |
Payment has been refunded to the customer. |
Payment Statuses
| Status | Description |
|---|---|
pending |
Awaiting payment. |
paid |
Payment received and confirmed. |
failed |
Payment attempt failed. |
refunded |
Payment has been refunded. |
Admin: Coupons
The Coupons page (Shop → Coupons) manages discount codes.
Coupon Fields
| Field | Description |
|---|---|
code |
Unique coupon code entered by the customer at checkout. |
type |
percentage or fixed discount. |
value |
Discount amount (percentage points or fixed currency amount). |
min_order_amount |
Minimum order subtotal required for the coupon to apply. |
max_discount |
Maximum discount cap (useful for percentage coupons on large orders). |
max_uses |
Total number of times this coupon can be used across all customers. |
max_uses_per_user |
Maximum uses per individual user. |
starts_at |
Coupon becomes valid after this date. |
expires_at |
Coupon expires after this date. |
is_active |
Enable/disable the coupon. |
Validation Rules
A coupon is considered valid when all of the following are true:
is_activeistrue.- Current date is after
starts_at(orstarts_atis null). - Current date is before
expires_at(orexpires_atis null). used_countis less thanmax_uses(ormax_usesis null).
A coupon is applicable to an order when it is valid AND the order subtotal meets min_order_amount.
The discount calculation never exceeds the order subtotal.
Admin: Merchant Feeds
The Merchant Feeds page (Shop → Merchant Feeds) manages product feed generation for e-commerce advertising platforms.
Supported Platforms
| Platform | Format | Key Settings |
|---|---|---|
| Google Merchant Center | XML | Merchant Center ID, Target Country, Content Language |
| Bing / Microsoft | XML | Merchant ID, Store ID |
| Facebook / Meta Commerce | XML | Commerce Account ID, Catalog ID |
| Amazon Product Ads | XML | Seller ID, Marketplace ID, Default Category |
| TikTok Shop | CSV | TikTok Shop ID |
| Yandex.Market | YML | Shop Name, Company Name |
| Baidu Commerce | XML | Merchant ID |
Per-Platform Management
For each platform, admin can:
- Enable/Disable: toggle feed generation on or off.
- Configure Settings: merchant ID, API keys (encrypted), currency, cache TTL, include variants, include out-of-stock products.
- Customize Field Mappings: map platform-specific feed fields to product data properties. Override default mappings, set default values, and apply transforms.
- Generate Feed: manually trigger feed generation.
- Preview Feed: view a sample of the generated output.
- Validate Feed: check the feed for structural errors.
Artisan Command
Generates feeds for all enabled platforms. Can be scheduled via Laravel’s task scheduler for automatic periodic regeneration.
Feed URLs
Generated feeds are accessible at:
For example: /feeds/products/google returns the Google Merchant Center XML feed.
Front-end: Product Catalog
Routes
| Method | URL | Route Name | Description |
|---|---|---|---|
| GET | /{locale}/shop |
shop.index.localized |
Product listing page |
| GET | /{locale}/shop/category/{slug} |
shop.category.localized |
Category filtered listing |
| GET | /{locale}/shop/product/{slug} |
shop.product.localized |
Product detail page |
Non-localized variants (without {locale}) are also registered.
Product Listing Features
- Category filtering via URL or sidebar links.
- Search across product name, description, and SKU.
- Sort by: newest, price (low/high), name, popularity (view count).
- Price range filtering with min/max parameters.
- Grid / List view toggle.
- Pagination with configurable page size.
Product Detail Page
- Product image gallery with featured image.
- Price display with sale badge and discount percentage.
- Variant selector (if variants exist).
- Stock availability indicator.
- Add to Cart button (or “Buy Now” link for external products).
- Related products section (same category).
- Schema.org JSON-LD structured data (via
<x-shop-json-ld>component).
Front-end: Shopping Cart
Routes
| Method | URL | Route Name | Description |
|---|---|---|---|
| GET | /{locale}/shop/cart |
shop.cart.localized |
View cart |
| POST | /{locale}/shop/cart/add |
shop.cart.add.localized |
Add item to cart |
| POST | /{locale}/shop/cart/update |
shop.cart.update.localized |
Update item quantity |
| POST | /{locale}/shop/cart/remove |
shop.cart.remove.localized |
Remove item from cart |
| POST | /{locale}/shop/cart/clear |
shop.cart.clear.localized |
Clear entire cart |
| POST | /{locale}/shop/cart/coupon |
shop.cart.coupon.localized |
Apply coupon code |
| DELETE | /{locale}/shop/cart/coupon |
shop.cart.coupon.remove.localized |
Remove applied coupon |
Cart Behavior
- Guest carts: stored in the database, identified by a session-based ID (
shop_cartsession key). - Authenticated carts: stored by
user_id. When a user logs in, their session cart is automatically merged into their user cart. - Stock validation: adding items checks available stock. If the requested quantity exceeds stock, an error is returned.
- Max quantity: configurable per-item limit (default: 99).
- External products cannot be added to the cart; an exception is thrown with a translatable message.
- Cart count is shared with all views via a View Composer.
Cart Totals
The CartService calculates:
- Subtotal: sum of (price × quantity) for all items.
- Discount: coupon discount (percentage or fixed), capped at
max_discount. - Tax: calculated on (subtotal - discount). Supports inclusive pricing (embedded tax extraction).
- Shipping: flat rate, waived for digital-only orders or when subtotal meets free shipping threshold.
- Total: subtotal - discount + tax + shipping.
Front-end: Checkout
Routes
| Method | URL | Route Name | Description |
|---|---|---|---|
| GET | /{locale}/shop/checkout |
shop.checkout.localized |
Checkout form |
| POST | /{locale}/shop/checkout |
shop.checkout.process.localized |
Process checkout |
| GET | /{locale}/shop/checkout/success/{orderNumber} |
shop.checkout.success.localized |
Order success page |
Checkout Flow
- Cart validation: redirects to cart page if empty.
- Guest check: if guest checkout is disabled and user is not authenticated, redirects to login.
- Form display: billing info (name, email, phone), billing address, shipping address, order notes, payment method selector.
- Form submission via
CheckoutRequestvalidation. - Order creation: the
OrderService::createFromCart()method:- Creates the order record with all totals.
- Creates order items from cart items.
- Generates download links for digital products.
- Decrements stock for managed-stock products.
- Increments coupon usage count.
- Sends admin and customer email notifications.
- Payment processing: the order implements the
Payableinterface, allowing the corePaymentServiceto route to the selected payment gateway. - Success page: displays order confirmation with order number and details.
{prefix}-{YYYYMMDD}-{zero-padded ID}.
The prefix is configurable via shop_order_prefix.
Front-end: My Orders
Routes
| Method | URL | Route Name | Description |
|---|---|---|---|
| GET | /{locale}/shop/my-orders |
shop.orders.localized |
List user’s orders (auth required) |
| GET | /{locale}/shop/my-orders/{orderNumber} |
shop.orders.show.localized |
Order detail (auth required) |
| GET | /{locale}/shop/my-orders/{orderNumber}/download/{itemId} |
shop.orders.download.localized |
Download digital product (auth required) |
Digital Downloads
For digital products, the download is allowed when all conditions are met:
- The order item is marked as
is_digitalwith adownload_link. - The order has been paid (
payment_status = 'paid'). - The download link has not expired (
download_expires_atis in the future). - The download count has not reached the maximum (
shop_digital_max_downloads).
Each successful download increments download_count.
Front-end: Order Tracking
/{locale}/shop/order/track
Description
Guest order tracking. Customers enter their order number and billing email to view order status without logging in.
Query Parameters
order_number |
Required | The order number (e.g. ORD-20260307-00000001) |
email |
Required | Billing email used during checkout |
Payment Flow
- Customer selects a payment method on the checkout page.
- The checkout controller creates the order via
OrderService::createFromCart(). - The
PaymentServiceroutes the order to the selected gateway. - The gateway processes the payment (redirect to external page, card form, etc.).
- On success, the gateway calls
order->markAsPaid()which sets status to completed and payment status to paid, then clears the cart. - On failure, the gateway calls
order->markPaymentFailed(). - Customer is redirected to the success URL or back to checkout.
Transactions
Payment gateways create Transaction records to track payment events:
- Type:
paymentorrefund - Status:
pending,completed, orfailed - Gateway info: gateway name, gateway transaction ID, amount, currency
- Metadata: JSON field for gateway-specific data
Updating
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
Step 3: Clear Caches
Step 4: Rebuild Assets
Troubleshooting
Products not appearing in the catalog
- Ensure the product status is Published (not Draft or Archived).
- Check
published_at: if set, it must be in the past. - Verify the product has at least a name set for the current locale.
Add to Cart fails: “Insufficient stock”
- Check that
stock_quantityis greater than the requested quantity. - If the item is already in the cart, the combined quantity must not exceed available stock.
- Disable Manage Stock on the product to always treat it as in-stock.
Cannot add external product to cart
This is by design. External products (checkout_type = 'external') redirect to an external URL.
They cannot be added to the internal cart. On the front-end, a “Buy Now” link is shown instead of the Add to Cart button.
Coupon not applying
- Verify the coupon
is_activeistrue. - Check
starts_atandexpires_atdates. - Confirm the order subtotal meets
min_order_amount. - Check if
used_counthas reachedmax_uses.
Checkout fails: no payment gateways available
- Install and activate at least one payment gateway add-on (e.g. Stripe).
- Cash on Delivery (COD) is available as a built-in option when the shop is active.
- Verify the payment gateway is configured correctly in its settings.
Digital download returns 403 or “Download not available”
- Confirm the order
payment_statusispaid. - Check if the download has expired (
download_expires_at). - Check if the maximum download count has been reached.
- Verify the digital file exists on the configured storage disk at the stored path.
Merchant feed returns empty or 404
- Ensure the platform is enabled in Shop → Merchant Feeds.
- Run
php artisan shop:generate-feedsto regenerate feeds. - Check that published products exist with
is_digital = falseorinclude_out_of_stockis enabled. - Verify the feed URL:
/feeds/products/{platformKey}(e.g./feeds/products/google).
Tax calculation seems incorrect
- If using tax-inclusive pricing, the tax is extracted from (not added to) the subtotal. For a 20% rate on a $120 subtotal: tax = $120 - ($120 / 1.20) = $20.
- For tax-exclusive pricing, tax is calculated as: (subtotal - discount) × rate / 100.
- Verify the
shop_tax_ratesetting is the correct percentage (e.g. 20 for 20%, not 0.20).
Cart not merging after login
- The session cart is identified by the
shop_cartsession key. If the session was cleared before login, the guest cart cannot be found. - Merging happens in
CartService::mergeSessionCart()on the first cart access after login.