PhoenixKitEcommerce

ElixirLicense: MIT

E-commerce module for PhoenixKit. Products, categories, shopping cart, checkout, shipping, CSV imports, and multi-language support with real-time LiveView UI.

Features

Installation

Add phoenix_kit_ecommerce to your dependencies in mix.exs:

def deps do
  [
    {:phoenix_kit_ecommerce, "~> 0.1.0"}
  ]
end

Then fetch dependencies:

mix deps.get

Note: For development or if not yet published to Hex, you can use:

{:phoenix_kit_ecommerce, github: "BeamLabEU/phoenix_kit_ecommerce"}

PhoenixKit auto-discovers the module at startup — no additional configuration needed.

Quick Start

  1. Add the dependency to mix.exs
  2. Run mix deps.get
  3. Add Oban queues to config/config.exs:
    config :my_app, Oban,
      queues: [shop_import: 5, shop_images: 5]
  4. Run mix phoenix_kit.update to generate migrations
  5. Enable the Shop module in Admin -> Modules
  6. Configure shop settings at /admin/shop/settings

Usage

Products

alias PhoenixKitEcommerce, as: Shop

# Create a product
{:ok, product} = Shop.create_product(%{
  title: "Wireless Headphones",
  slug: "wireless-headphones",
  status: "draft",
  price: Decimal.new("79.99"),
  currency: "EUR",
  product_type: "physical",
  weight_grams: 250
})

# Publish the product
{:ok, product} = Shop.update_product(product, %{status: "active"})

# Multi-language support
{:ok, product} = Shop.create_product(%{
  title: %{"en" => "Wireless Headphones", "uk" => "Бездротові навушники"},
  slug: %{"en" => "wireless-headphones", "uk" => "bezdrotovi-navushnyky"},
  price: Decimal.new("79.99"),
  currency: "EUR"
})

# Look up by slug in any language
product = Shop.get_product_by_any_slug("bezdrotovi-navushnyky")

Categories

# Create a category hierarchy
{:ok, electronics} = Shop.create_category(%{
  name: "Electronics",
  slug: "electronics",
  status: "active"
})

{:ok, audio} = Shop.create_category(%{
  name: "Audio",
  slug: "audio",
  status: "active",
  parent_uuid: electronics.uuid
})

# List categories for navigation menus
categories = Shop.list_menu_categories()

Product Options & Dynamic Pricing

# Options support fixed and percentage price modifiers
# Category-level option schema example:
option_schema = [
  %{
    "name" => "color",
    "type" => "select",
    "options" => ["Black", "White", "Red"],
    "price_modifier" => %{"Red" => %{"type" => "fixed", "amount" => "5.00"}}
  },
  %{
    "name" => "warranty",
    "type" => "select",
    "options" => ["1 Year", "3 Years"],
    "price_modifier" => %{"3 Years" => %{"type" => "percent", "amount" => "20"}}
  }
]

# Calculate final price with selected options
price = Shop.calculate_product_price(product, selected_specs)

Shopping Cart

# Get or create a cart (guest or authenticated)
{:ok, cart} = Shop.get_or_create_cart(user_uuid: user.uuid)
{:ok, cart} = Shop.get_or_create_cart(session_id: session_id)

# Add items
{:ok, cart} = Shop.add_to_cart(cart, product, %{quantity: 2, selected_specs: specs})

# Update quantity
{:ok, cart} = Shop.update_cart_item(cart, item_uuid, %{quantity: 3})

# Set shipping and payment
{:ok, cart} = Shop.set_cart_shipping(cart, shipping_method_uuid)
{:ok, cart} = Shop.set_cart_payment_option(cart, payment_option_uuid)

# Merge guest cart after login
{:ok, cart} = Shop.merge_guest_cart(session_id, user.uuid)

# Convert to order (integrates with Billing module)
{:ok, order} = Shop.convert_cart_to_order(cart)

Shipping Methods

{:ok, method} = Shop.create_shipping_method(%{
  name: "Standard Delivery",
  slug: "standard",
  price: Decimal.new("5.99"),
  currency: "EUR",
  free_above_amount: Decimal.new("50.00"),
  min_weight_grams: 0,
  max_weight_grams: 30000,
  estimated_days_min: 3,
  estimated_days_max: 5,
  countries_allowed: ["UA", "PL", "DE"],
  active: true
})

# Get methods available for a specific cart
methods = Shop.get_available_shipping_methods(cart)

CSV Import

# Import products from CSV (Shopify, Prom.ua, or generic format)
{:ok, log} = Shop.start_import(file_path, import_config)

# Import runs asynchronously via Oban worker
# Track progress in real-time at /admin/shop/imports/:uuid

Real-Time Events

Subscribe to shop events in your LiveViews:

def mount(_params, _session, socket) do
  PhoenixKitEcommerce.Events.subscribe_cart(user_uuid)
  {:ok, socket}
end

def handle_info({:cart_updated, cart}, socket) do
  {:noreply, assign(socket, :cart, cart)}
end

Settings

Key Type Default Description
tax_enabled boolean true Enable tax calculations
tax_rate number 20 Tax rate percentage
inventory_tracking boolean true Track product inventory
allow_price_override boolean false Allow per-product price overrides

Cart Status Workflow

Status Description
active Cart is in use
merged Guest cart merged into user cart after login
converted Cart converted to an order via checkout
abandoned Cart inactive past threshold
expired Session-based cart past 30-day expiry
active → converted (checkout)
       → merged (login)
       → abandoned (inactivity)
       → expired (30 days)

Permissions

The module declares permissions via permission_metadata/0:

Use Scope.has_module_access?/2 to check permissions in your application.

CSS Requirements

This module implements css_sources/0 returning [:phoenix_kit_ecommerce], so PhoenixKit's installer automatically adds the correct @source directive to your app.css for Tailwind scanning. No manual configuration needed.

Architecture

lib/
├── phoenix_kit_ecommerce.ex                    # Main context (PhoenixKit.Module behaviour)
└── phoenix_kit_ecommerce/
    ├── mix_tasks/
    │   ├── phoenix_kit_ecommerce.install.ex    # Install mix task
    │   └── phoenix_kit_ecommerce.deduplicate_products.ex  # Dedup utility
    ├── events.ex                  # PubSub event broadcasting
    ├── translations.ex            # Multi-language utilities
    ├── slug_resolver.ex           # Multi-language slug lookup
    ├── schemas/
    │   ├── product.ex             # Product schema
    │   ├── category.ex            # Category with nesting
    │   ├── cart.ex                # Shopping cart
    │   ├── cart_item.ex           # Cart line items
    │   ├── shipping_method.ex     # Shipping options
    │   ├── shop_config.ex         # Key-value config store
    │   ├── import_config.ex       # Import profiles
    │   └── import_log.ex          # Import tracking
    ├── options/
    │   ├── options.ex             # Option management context
    │   ├── option_types.ex        # Type system & validation
    │   └── metadata_validator.ex  # Metadata validation
    ├── import/
    │   ├── import_format.ex       # Format behaviour
    │   ├── format_detector.ex     # Auto-detect CSV format
    │   ├── csv_parser.ex          # CSV parsing
    │   ├── csv_validator.ex       # CSV validation
    │   ├── csv_analyzer.ex        # CSV analysis
    │   ├── shopify_csv.ex         # Shopify format parser
    │   ├── shopify_format.ex      # Shopify format implementation
    │   ├── prom_ua_format.ex      # Prom.ua format implementation
    │   ├── product_transformer.ex # CSV row -> product
    │   ├── option_builder.ex      # Option creation from CSV
    │   └── filter.ex              # Keyword filtering
    ├── services/
    │   ├── image_downloader.ex    # Download images from URLs
    │   └── image_migration.ex     # Batch image storage
    ├── workers/
    │   ├── csv_import_worker.ex   # Oban: async CSV import
    │   └── image_migration_worker.ex # Oban: batch image processing
    └── web/
        ├── routes.ex              # Route definitions
        ├── shop_web.ex            # Web module config
        ├── helpers.ex             # Template helpers
        ├── shop_catalog.ex        # Public: catalog page
        ├── catalog_category.ex    # Public: category browse
        ├── catalog_product.ex     # Public: product detail
        ├── cart_page.ex           # Public: cart
        ├── checkout_page.ex       # Public: checkout
        ├── checkout_complete.ex   # Public: order confirmation
        ├── user_orders.ex         # Public: order history
        ├── user_order_details.ex  # Public: order details
        ├── dashboard.ex           # Admin: overview
        ├── products.ex            # Admin: product list
        ├── product_form.ex        # Admin: product editor
        ├── product_detail.ex      # Admin: product detail
        ├── categories.ex          # Admin: category list
        ├── category_form.ex       # Admin: category editor
        ├── shipping_methods.ex    # Admin: shipping list
        ├── shipping_method_form.ex # Admin: shipping editor
        ├── carts.ex               # Admin: cart analytics
        ├── settings.ex            # Admin: settings
        ├── options_settings.ex    # Admin: global options
        ├── imports.ex             # Admin: import list
        ├── import_configs.ex      # Admin: import profiles
        ├── import_show.ex         # Admin: import details
        ├── test_shop.ex           # Admin: testing UI
        ├── option_state.ex        # Client option state
        ├── components/
        │   ├── shop_layouts.ex    # Layout wrappers
        │   ├── shop_cards.ex      # Product cards
        │   ├── catalog_sidebar.ex # Filter sidebar
        │   ├── filter_helpers.ex  # Dynamic filters
        │   └── translation_tabs.ex # Multi-lang editing
        └── plugs/
            └── shop_session.ex    # Guest cart session

Database Tables

Table Description
phoenix_kit_products Product catalog (UUIDv7 PK)
phoenix_kit_categories Hierarchical categories
phoenix_kit_carts Shopping carts (guest + user)
phoenix_kit_cart_items Cart line items with price snapshots
phoenix_kit_shipping_methods Shipping options and constraints
phoenix_kit_shop_configs Key-value shop configuration
phoenix_kit_import_configs CSV import profiles
phoenix_kit_import_logs Import job tracking and progress

Routes

Public:

Path Description
/shop Product catalog with filtering
/shop/category/:slug Category browse
/shop/product/:slug Product detail page
/cart Shopping cart
/checkout Checkout flow
/checkout/complete/:uuid Order confirmation

Admin:

Path Description
/admin/shop Dashboard & statistics
/admin/shop/products Product management
/admin/shop/categories Category management
/admin/shop/shipping Shipping methods
/admin/shop/carts Cart analytics
/admin/shop/imports CSV import jobs
/admin/shop/settings Shop configuration
/admin/shop/settings/options Global option schemas
/admin/shop/settings/import-configs Import profiles

All public routes support localized variants via public_live_locale_routes/0.

Development

mix deps.get       # Install dependencies
mix test           # Run tests
mix format         # Format code
mix credo --strict # Static analysis (strict mode)
mix dialyzer       # Type checking
mix docs           # Generate documentation
mix precommit      # Compile + format + credo + dialyzer
mix quality        # Format + credo + dialyzer

Troubleshooting

Shop not appearing in admin

CSV imports not processing

Guest cart not persisting

Images not downloading during import

License

MIT -- see LICENSE for details.