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. Enable the Digital Products feature first (Shop → Settings → Digital Products: off by default); leaving it off keeps the whole storefront and admin panel in physical-product mode.
Affiliate / External Products
List products that link to external retailers. External products display a “Buy Now” button linking to the external store instead of the internal cart flow. A product can list up to five stores (affiliate links, each with its own provider name): with a single active link the button goes straight to it, with several the customer picks a store first.
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: Upload the Add-on
In the admin panel, go to Admin → Extensions → Add-ons and click the Upload Add-on button. Select the add-on’s ZIP file: the system extracts it automatically and the add-on appears in the installed add-ons list.
Step 2: Activate the Add-on
Find Shop in the list and click Activate. Its migrations, seeders (if any) and permissions are set up automatically.
Step 3: Configure
Navigate to Admin → Shop → Settings to configure currency, tax, shipping, and checkout options. See Configuration.
Purchase Code (License Key)
Shop is sold as a separate product, so it has its own purchase code (license key), distinct from the purchase code of the main application and from the one of every other add-on. You are asked for it when you activate Shop in Admin panel → Add-ons.
Our products are sold on three platforms. The way you receive a purchase code depends on where you bought the product.
| Platform / Marketplace | How you get the purchase code | Where to find it again |
|---|---|---|
| bedigit.com Store In-site purchase (Shop) |
Generated automatically when the order is paid, then sent by email, either in its own license email, or inside the order confirmation email. | My Account → My Licenses on bedigit.com |
| Gumroad | Created as soon as Gumroad notifies us of the sale, then sent in a separate email, in addition to the Gumroad receipt. | The license email, your Gumroad Library, and My Account → My Licenses on bedigit.com |
| Envato Market CodeCanyon |
Issued by Envato, not by us, and never sent by email: you download it yourself from your Envato account. | Envato account → Downloads → License certificate & purchase code |
1. bedigit.com Store (in-site purchase)
- As soon as the order’s payment status becomes Paid, a license key is generated automatically for every licensed item in the order (one key per purchased unit: buying 3 units gives 3 distinct keys).
- It is emailed to the address used on the order, either in a dedicated license email or inside the order confirmation email. Check your inbox and your spam / junk folder.
- The key stays available in your account under My Account → My Licenses. Keys are masked in the list; open the license detail page to reveal and copy the full key, see the domains it is activated on, and deactivate a domain to free an activation slot.
- The matching invoice is under My Account → My Orders.
2. Gumroad
- A Gumroad purchase produces two separate emails: the Gumroad receipt (sent by Gumroad, giving access to the files) and a license key email (sent by bedigit.com) that contains your purchase code.
- The license key email is generated as soon as Gumroad notifies us of the sale, so it normally arrives within seconds of the payment. Here too, check your inbox and your spam / junk folder.
- When the Gumroad product uses Gumroad’s own license-key feature, the same key also appears in your Gumroad receipt and under Library → your purchase on gumroad.com.
- Use the same email address on bedigit.com as on Gumroad: your keys are then linked to your account automatically and listed under My Account → My Licenses, even if you register after the purchase. You can also add a Gumroad key manually from My Account → My Gumroad Licenses.
3. Envato Market (CodeCanyon)
- Envato purchase codes are issued and delivered by Envato Market, never emailed by us, so there is nothing to look for in your spam folder: you retrieve the code from your Envato account.
- Log in to your Envato / CodeCanyon account, open the Downloads page, find the item, and choose License certificate & purchase code from the Download dropdown. The code is written in that certificate.
- An Envato purchase code looks like
12345678-90ab-cdef-1234-567890abcdef(8-4-4-4-12 characters). It never changes, and renewing item support does not issue a new one. - Official Envato article: Where Is My Purchase Code?
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 |
shop_payment_terms_override_enabled |
Override the global sale terms (Settings › General › Sale Terms) for the checkout and update-access renewal pages. When off, the global settings apply. | false |
shop_payment_terms_enabled |
While overriding: require buyers to accept the sale terms before paying. | global value |
shop_payment_terms_page_id |
While overriding: the published page whose content is shown in the terms accordion. | global value |
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_products_enabled |
Master switch for the whole digital-products feature (self-hosted downloads, versioned releases, update access, license linking, technical details). Toggled from the switch in the Digital Products tab header. When off, every digital-only field, product card, the “Create / Update license product” button and the Release Changelogs menu entry are hidden across the admin panel, and the storefront presents every product with the physical layout. Off by default so shops selling only physical products / services are never shown digital-only options. | false |
shop_catalog_mode |
Catalog Type (physical or digital): tunes the listing/detail presentation to a marketplace-style layout. Lives in the Digital Products tab and only takes effect while shop_digital_products_enabled is on, with the feature off the catalog is always presented as physical. |
physical |
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 |
shop_digital_update_access_months |
Default update-access window (in months) for updatable products: how long buyers keep downloading new versions after purchase. Overridable per product. | 6 |
shop_update_access_renewal_enabled |
Allow buyers to pay to extend their update-access window once it nears or passes expiry. | true |
shop_update_access_renewal_percent |
Default renewal price to extend update access, as a percentage of the product price. Overridable per product. | 50 |
shop_update_access_grace_days |
Days after update access ends during which buyers can still download before the gate applies (0 = no grace). | 0 |
shop_update_access_syncs_license |
Drive the linked license key’s Expires At from update access (requires the Licenses add-on). See Updatable Products. | true |
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
Optional variables for git-backed versioned products (all have sensible defaults):
| Variable | Description | Default |
|---|---|---|
SHOP_GIT_PROCESS_TIMEOUT |
Max seconds a single git command may run before it is aborted. Raise it for very large repositories or slow hosts. | 1800 |
SHOP_GIT_VERSION_LINK_TTL_HOURS |
How long a private per-version download link (issued to an expired-license buyer) stays valid. | 48 |
SHOP_PHP_BINARY |
Path to the PHP CLI binary used to launch background release builds on the sync queue. Only needed when it cannot be auto-detected (e.g. under mod_php). |
(auto) |
SHOP_GIT_HTTP_PROXY |
Proxy for git’s HTTP(S) transport. Empty means a direct connection. | (empty) |
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: A master Enable Digital Products switch in the card header (off by default) turns the whole feature on/off; when off, its fields are hidden here and every digital-only card, button and menu entry is hidden across the admin panel (and the storefront drops the marketplace layout). When on: Catalog Type, max downloads per purchase, download link expiry, update-access window and paid renewal options.
- 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:
When editing a published product, a View button (top-right, next to Back) opens its public storefront page in a new tab. It is hidden for drafts and future-scheduled products, which have no public page yet.
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 (unique across shop products). For a digital product sold with a license key, it is also the product identifier client apps send to the License API: see License Products.
- 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 one or more external stores (affiliate links). 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.
Technical Details
Digital products can advertise CodeCanyon-style stacks & compatibilities in a Technical Details card. It appears on the product form only when Is Digital is enabled, and on the storefront it is shown as a dedicated card in the product page’s right sidebar, only the fields you fill are displayed.
- Digital file type: what kind of product this is: Code / Script, Graphic / Template, Image / Photo, Video, Audio / Music, Document, or Other. The type tailors which fields are shown: e.g. Code exposes Languages, Frameworks and Compatible Browsers, while Graphic and Image show Resolution instead. It is also displayed as the Type row in the storefront Item Details card.
- Grouped tag lists: each field is a tag picker: Languages, Frameworks & Libraries, Compatible Browsers, Software Version, Resolution and Files Included. Pick an existing value from the suggestions or type a new one and press Enter.
- Auto-suggestions: the picker is seeded with common values and merges in every value already used by your other products, so your catalog stays consistent while you can still add anything new on the fly.
Updatable Products
Enable Updatable product (shown above the Digital File field) for products that ship free updates: software, themes, templates, and similar. When enabled:
- The per-order download limit is waived so buyers can always re-download the latest file.
- Two extra fields appear: Update access (window in months buyers keep receiving new versions; blank uses
shop_digital_update_access_months) and Update access renewal price (percentage of the product price to extend the window; blank usesshop_update_access_renewal_percent). - On their order page, buyers see their access status and, once it nears or passes expiry, can pay to extend update access.
License linking (with the Licenses add-on). When shop_update_access_syncs_license is on, the license key issued for an updatable purchase gets its Expires At set to the update-access window, and extending update access advances it. A key past its Expires At still validates for versions released on or before that date, only newer versions require a renewal. The verify API accepts an optional version parameter for this check.
License Products (Licenses add-on)
When the Licenses add-on is active, a digital product can be tied to a license product so that every paid order issues a license key for it.
The button appears as a key icon in the products list and as a License product card at the bottom of the product edit page (digital products only, and only while
Enable Digital Products is on). It reads Create license product or Update license product and requires the
licenses.products.create or licenses.products.edit permission.
- How the link is stored. The shop product is recorded as the license product’s
shopmarketplace listing (a row in the Licenses add-on’slicenses_product_marketplacetable:listing_id= the shop product,external_id= its SKU). The shop add-on itself stores nothing about the link: the formershop_products.license_product_idcolumn was removed by Licenses 1.0.13, which copied every existing link into that table. - Create makes a new license product from the shop product (name, short description, a unique slug based on the product slug) with the same SKU, then records the listing. Update refreshes the linked license product’s name and description and heals the listing.
- One SKU on both sides. The SKU of the shop product and of its license product are kept identical: when you save either product and one side has no SKU, it is filled from the other side. Two different SKUs are never overwritten silently.
- The button refuses to run (error toast) when:
- the shop product and its license product carry different SKUs: align one of them, then click again;
- the shop product SKU is already used by another license product;
- the product is not digital.
- License keys for orders. When an order is paid (or accepted offline), a key is issued for each order line whose shop product is listed as a license product,
provided the Shop marketplace is enabled in the Licenses settings (
licenses_shop_marketplace_enabled). Products without a listing issue no key.
product, and it is the SKU written in their addon.json / theme.json. If you change it on a product that customers already use, their
installations may no longer match the product, change the license product SKU and the shipped manifests at the same time, and run
php artisan licenses:doctor, which reports shop / license SKU mismatches (sku_mismatch) and sold products without a SKU (sku_missing).
Versioned Updates (Git)
Updatable products can be released straight from a git repository instead of a manual file upload. Toggle Versioned Updates (shown when Updatable is on) to reveal the git panel.
- Provider: GitHub, GitLab, Bitbucket, or a custom / private git server.
- Repository URL and optional Branch.
- Authentication: a Token (personal access token / app password, with an optional username) or an SSH key (paste the private key; add the matching deploy key to your repository). Secrets are stored encrypted and never shown again.
- Test connection verifies the repository is reachable with the current credentials (works before saving).
- Create archive builds a downloadable release from a chosen annotated tag (or the latest annotated tag). The tag picker is searchable and loads tags from the repository on demand. If the repository has no annotated tag, the latest commit is used instead.
Each annotated tag becomes a release with its real tag date. The most recent release is the file buyers download. The Releases list shows the latest few (with a total count), each with a delete button (trash icon) that removes that release and its file: deleting the current one clears the product’s file until you cut a new release.
Release archives are named <project>-v<version>.zip (e.g. larapen-v1.0.8.zip), taking the project name from the repository URL.
storage/logs/shop-git.log.
- With a real queue (
QUEUE_CONNECTION=database/redis): run a worker,php artisan queue:work, and it processes builds. - With the default
syncqueue: the build is launched as a detached background process automatically (no worker needed). If your PHP CLI binary cannot be auto-detected, setSHOP_PHP_BINARYin.env.
On the products list, git-backed updatable products show a New release button (tag icon) that cuts a fresh release from the latest annotated tag, replacing the previous file.
Release Changelogs
Every release carries a changelog. When a release is built from a tag, the changelog is generated automatically from the commit messages between that tag and the previous release (falling back to the tag’s own message). Releases are created without emailing anyone so you can review and edit the notes first.
- Manage them under Shop → Release Changelogs (the menu entry appears only while the Digital Products feature is enabled): a searchable, filterable list of every release across all products. Add / edit / delete entries, and manually record past releases (with their own version, date and notes) that predate the git integration.
- Edit the changelog of any release from that screen (or from the product’s release list) before publishing it.
- Send notification: a dedicated button emails subscribed buyers the changelog for that version. It only appears when the release has a downloadable file (so notifications always point to something to download).
- Public page: each digital, updatable product has a public changelog at
/shop/product/{slug}/changelog, linked by a Changelog button in the product page’s Item Details card.
Availability & Missing Files
A digital product can only be purchased when its downloadable file actually exists (uploaded, or a release has been built). When the file is missing (not uploaded yet, no built release, or the file was deleted) the shop protects buyers automatically:
- Storefront listings show a disabled Unavailable button, and the product page shows a “temporarily unavailable” notice instead of Buy / Add to Cart.
- Add to Cart, Buy Now and Checkout are blocked server-side with a friendly message, so an unavailable item can never be ordered.
- On My Orders, if a purchased item’s file is temporarily unavailable, the buyer sees a clear notice, the download button is hidden, and Extend update access is disabled (so they aren’t charged for access to a missing file).
External Products
When Checkout Type is set to External:
- An External links card appears: add up to 5 stores, each with a provider (affiliate) name, URL, link target (new/same tab) and an active switch. Links can be removed or temporarily disabled; at least one active link is required.
- The product cannot be added to the cart.
- The front-end displays a “Buy Now” button. With a single active link it goes straight to the store; with two or more it opens a “Choose where to buy” dialog listing the stores (the provider name, or the link’s domain when no name is set).
- Service plans sold through an external checkout get the same External links card on their create/edit page, and the same store picker on the storefront.
- 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: Reviews & Ratings
Customers rate products from 1 to 5 stars and leave a title and comment from the product page. Reviews are moderated on Shop → Reviews (approve, reject, reply, delete, bulk delete). Buyers of the product get a Verified purchase badge; the average rating and review count are denormalised on the product (rating_avg, rating_count) and shown on product cards, the product page and in the Product JSON-LD (aggregateRating + review) for star rich results. Listings can be sorted by Top rated.
Settings (Shop → Settings → Reviews & Ratings)
| Setting | Description |
|---|---|
shop_reviews_enabled | Master switch for the reviews block and form. |
shop_reviews_guest_allowed | Visitors without an account may review (name + e-mail; one review per e-mail). |
shop_reviews_require_purchase | Only customers with a paid order containing the product may review it. |
shop_reviews_auto_approve | Publish reviews immediately instead of holding them as pending. |
shop_reviews_show_on_cards | Show the star rating on listing cards. |
shop_reviews_per_page | Reviews per page on the product page. |
shop_review_captcha_enabled | (Settings → Security) Require a CAPTCHA on the review form. A honeypot field and a 10 requests/minute throttle are always active. |
Admin: Attributes & Faceted Filters
Shop → Attributes defines product attributes (Colour, Size, Material…) and their values. Attributes of type Colour swatches carry a hex colour per value. Products are tagged with values from the Attributes card of the product form. Filterable attributes appear in the shop sidebar as facets with live product counts (each facet is counted against the current result set with its own selection removed), next to the price range, customer rating and availability filters. Active filters are shown as removable chips above the grid.
Filter URL parameters
| Parameter | Description |
|---|---|
attr[<attribute-slug>][]=<value-slug> | Attribute values (OR within an attribute, AND across attributes). |
min_price / max_price | Price range, applied on the current (sale) price. |
rating=4 | Minimum average rating (4 = 4 stars & up). |
in_stock=1 | In-stock products only. |
sort=rating | Sort by average rating. |
The filter panel and each facet type can be switched off under Shop → Settings → Catalogue filters (shop_filters_enabled, shop_filter_price_enabled, shop_filter_stock_enabled, shop_filter_rating_enabled).
Admin: Tax Rules
Two tax modes are available (Shop → Settings → Tax, shop_tax_mode): Flat rate keeps the single shop-wide rate of earlier versions, and Tax rules applies location-based rules managed on Shop → Tax Rules.
Tax classes and rates
A tax class groups products taxed alike (Standard, Reduced, Zero…); one class is the default and products may pick another one from the product form. A tax rate belongs to a class (or to every class) and matches a country, optionally a state/region and a postcode (exact, 75* prefix or 1000-1999 range). Rates are applied in priority order; a compound rate is computed on top of the taxes with a lower priority. Rates flagged Taxes shipping also apply to the shipping amount. Tax-inclusive pricing is honoured: the net amount is derived first and the same fractions apply.
Location resolution
The applicable location is decided by shop_tax_basis: the customer's shipping address, billing address, or the store location (shop_tax_store_country / state / postcode). Before an address is entered, the checkout estimate uses, in order: the location remembered from a previous estimate, the customer's saved default address, the visitor's GeoIP country, and finally the store location. Changing the country, state or postcode at checkout recomputes the totals live (POST shop/checkout/estimate).
EU VAT (B2B)
With shop_tax_vat_enabled the checkout shows a VAT number field. Numbers are validated against the European Commission VIES service (results cached for a day). When shop_tax_reverse_charge_enabled is on, a valid number from an EU country other than the store's makes the order tax-exempt (reverse charge); shop_tax_vat_require_valid additionally blocks the checkout when VIES rejects the number. The VAT number, the per-rate breakdown and the exemption reason are stored on the order (vat_number, tax_breakdown, tax_exempt_reason) and each order line carries its tax_amount.
Front-end: Wishlist & Save for Later
A heart button on product cards and product pages adds the product to the visitor's wishlist. Logged-in customers own one list; guests get a session list that is merged into their account on login. The My Wishlist page (/shop/wishlist, also in the account menu) lets customers move items to the cart, remove them, and share the list through a private link (/shop/wishlist/{token}) that can be switched off at any time. Guest lists untouched for shop.wishlist.guest_retention_days (90 days) are pruned weekly by shop:prune-guest-wishlists.
In the cart, Save for later parks a line: it leaves the totals and the checkout but stays listed under Saved for later until it is moved back (at the current price) or removed. Saved lines survive a completed checkout.
Switches: shop_wishlist_enabled, shop_wishlist_guest_enabled, shop_save_for_later_enabled (Shop → Settings → Wishlist).
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).
Digital products render a dedicated marketplace-style layout (gallery, item-details card, changelog link, technical details) only while the Digital Products feature is enabled. With the feature off, every product, even one still flagged digital, uses the standard physical layout.
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.
Update Access & Versioned Downloads
For updatable products, the order page shows the buyer’s update-access status and, once it nears or passes expiry, an Extend button to pay for another window.
For git-backed products whose update access has lapsed, the buyer can still fetch the exact version their license covers (the newest release dated on or before their access end):
- They click Get version … on the order page. The archive is (re)built from its git tag in the background, so the request returns immediately.
- When it is ready, the buyer is emailed a private, time-limited download link that states its validity date (default 48h,
SHOP_GIT_VERSION_LINK_TTL_HOURS). - Expired links and their generated files are swept automatically (
shop:prune-version-downloads, daily).
Update Notifications
Buyers of an updatable product are subscribed by default to release notifications. A checkbox on the order page lets them unsubscribe. When a new version is released (and the admin chooses to notify), every subscribed buyer receives an email.
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
There are two ways to update this add-on: via the admin panel (recommended) or manually replacing files.
Method 1: Admin Panel Upload (Recommended)
- Download the latest
.zipfile of this add-on. - Go to Admin panel → Add-ons and click the Upload button.
- Select or drag the
.zipfile into the upload area. - A confirmation prompt will show the current and new version numbers. Click Replace to proceed.
- Go to Admin panel → System Update (
/admin/update) to apply any pending database migrations.
Method 2: Manual File Replacement
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
php artisan migrate
Pending migrations run once, so the command is safe to repeat.
Step 3: Clear Caches
php artisan config:clear
php artisan route:clear
php artisan view:clear
Step 4: Rebuild Assets
shop_products.license_product_id column into its
licenses_product_marketplace table.
Uninstallation
Switching an add-on off without losing anything is a deactivation: go to Admin panel → Add-ons, find Shop and click Deactivate.
- Its routes, views, admin menu entries and permissions stop being registered, and its front-end pages stop answering.
- Its database tables and all the data they hold are kept, and its files stay under
extensions/addons/shop/. Nothing is deleted. - The purchase code recorded at activation is kept too, so activating the add-on again does not ask for it.
- Deactivation is refused while another active add-on depends on this one: deactivate that add-on first.
Click Activate on the same card to switch it back on. Pending migrations are re-run, assets are republished, and the add-on picks up exactly where it left off.
Removing
Removing is permanent and destroys the add-on's data. The Remove button only appears on a deactivated add-on, so removal is always two steps:
- Deactivate Shop (see Uninstallation).
- Click Remove on its card and confirm the prompt.
The admin panel then, in one pass:
- runs the add-on's uninstall hook, if it ships one, while its code is still on disk;
- revokes the permissions declared in its
addon.json; - rolls back its migrations (this drops its database tables and every row they hold) and purges its entries from the
migrationstable, so a later reinstall migrates from scratch; - deletes its published assets:
public/addons/shop/,public/vendor/shop/andstorage/app/public/addons/shop/; - deletes the add-on directory
extensions/addons/shop/; - deletes its row in the
addonstable (the recorded purchase code goes with it) and clears the application cache.
Removal is refused, with an explanatory message and before anything is destroyed, when the add-on is still active, when another active add-on depends on it, or when the web server (PHP) user cannot delete extensions/addons/shop/. In that last case, give that user write permission on the directory and on its parent, then try again.
Deleting the folder over FTP or SSH is not equivalent: the add-on's tables, its entries in the migrations table and its addons row are all left behind, and its card stays in the list. Use Remove in the admin panel instead.
Troubleshooting
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 store.
They cannot be added to the internal cart. On the front-end, a “Buy Now” link (or a store picker when
the product lists several stores) 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.
“Create / Update license product” fails
- SKU mismatch: the shop product and its license product have different SKUs. Make them identical (on the shop product or in Licenses → Products), then click the button again.
- SKU already used: another license product already holds this SKU. Link that license product instead, or change one of the SKUs.
- The button only exists for digital products, with the Licenses add-on active and Enable Digital Products on.
No license key issued for a paid order
- Check that the product has a license product (see License Products).
- Check that the Shop marketplace is enabled in the Licenses settings (
licenses_shop_marketplace_enabled). - Update-access renewal orders never issue keys: they only extend the existing one.
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.
Shop v1.0.0: Part of the Larapen CMS platform.
© BeDigit. All rights reserved.