Nviti Commerce Module — Feature Overview
This document describes how the Nviti Commerce module works end-to-end: how vendors set up their shop, how customers interact with it on WhatsApp, and how orders, payments, and notifications flow through the system.
1. What Is the Commerce Module?
Nviti Commerce is a WhatsApp-native shopping platform embedded inside the Nviti conversational AI product. It allows businesses (vendors) to sell products directly through WhatsApp conversations — no website or separate e-commerce store required.
Customers browse a product catalog, add items to a cart, check out, pay, and track orders — all within a WhatsApp chat window using interactive buttons, list messages, and WhatsApp Flows (multi-screen forms).
2. Shop Setup (Vendor Side)
2.1 Enabling Commerce
A vendor enables commerce in Workspace → Shop Settings → General tab by toggling "Enable Order Processing" on. This activates the commerce command interception layer in the WhatsApp webhook pipeline.
2.2 Shop Settings
Shop Settings is a comprehensive configuration panel with 10 tabs:
| Tab | What It Controls |
|---|---|
| General | Order processing toggle, assigned AI agent, currency, default payment method, store name, bank account details (for Paystack) |
| Main Menu | Header text, body text, labels for Browse/Cart/Checkout/Track Order buttons |
| Catalogue | Category list headers, product list body text, Add to Cart CTA, search placeholder |
| Cart | Empty cart message, View Cart/Checkout/Clear button labels, post-add instructions |
| Checkout | Start Checkout CTA, flow body text, Confirm/Cancel buttons, payment method header/body |
| Orders | Confirmation messages (supports {id} and {total} placeholders), Track/History CTA labels, default status note, empty history message |
| Flow: Form Labels | Field labels for all flow forms — Full Name, Phone, Address, City, State, Delivery Method, Variant, Quantity, etc. |
| Flow: Catalogue | Screen titles and headings for the catalogue shopping flow |
| Flow: Checkout | Screen titles and headings for the standalone checkout flow |
| Flow: Cart | Empty cart / cart cleared screen titles and body messages |
| Flow: Order History | Done screen message |
All settings have sensible defaults and can be customized per-vendor. Settings are stored in the company_settings table, falling back to defaults from commerce.php config.
2.3 Bank Account & Paystack Subaccount
In the General tab, vendors enter:
- Settlement Bank (dropdown populated from Paystack's Nigerian bank list)
- Account Number (10-digit NUBAN)
- Account Name (auto-filled after account number is entered)
On save, the system creates or updates a Paystack Subaccount behind the scenes. The subaccount code is stored in Company.metadata and used later to split payments when customers pay by card.
2.4 Products
Products are managed in Workspace → Products (Filament resource). Each product has:
- Name (translatable), SKU, Slug
- Price, optional Discount price
- Stock Quantity or Unlimited Stock toggle
- Category assignment
- Weight (for shipping calculations)
- Image (displayed in WhatsApp Flows)
- Variants (size, color, etc.) with their own pricing
- Activation toggle — only activated, in-stock products appear in the WhatsApp shop
2.5 Categories
Categories organize the product catalog. They are managed in Workspace → Categories. Only categories containing active, in-stock products are shown to customers.
2.6 Shipping
Shipping vendors can be configured with name, phone, delivery estimation, and pricing. If no shipping vendors are configured, a flat "Standard Delivery" rate is used (configurable in Shop Settings). Delivery time estimates are location-based (Lagos: 1 day, nearby states: 2 days, elsewhere: 5 days).
3. The WhatsApp Shopping Experience
3.1 How Customers Access the Shop
Customers interact with the vendor's WhatsApp Business number. The commerce system intercepts specific text commands and button taps to provide a shopping experience. There are three interaction modes:
Mode 1: Text Commands
Customers type natural-language shortcuts that are intercepted before the AI processes them:
| Command | Action |
|---|---|
menu, help, browse, shop, products |
Opens the main shopping menu |
cart, view cart, my cart |
Shows the current cart |
checkout |
Starts the checkout flow |
orders, my orders, order history |
Opens order history |
order status, track order |
Opens order status tracker |
ORD-XXXXXX or similar patterns |
Looks up a specific order |
Mode 2: Interactive Buttons & Lists
WhatsApp interactive messages provide tappable buttons and list items. When a customer taps a button, the system routes the callback to the appropriate handler:
| Button ID Prefix | Action |
|---|---|
menu_ |
Main menu navigation (browse, cart, checkout, orders) |
add_ |
Add product to cart |
remove_ |
Remove item from cart |
update_ |
Update item quantity |
view_cart |
View shopping cart |
clear_cart |
Clear all cart items |
cat_ |
Browse a category |
prod_ |
View product details |
page_ |
Paginate product lists |
checkout_ |
Start/continue checkout |
confirm_ |
Confirm order |
cancel_ |
Cancel checkout |
pay_ |
Select payment method |
Mode 3: WhatsApp Flows (Multi-Screen Forms)
WhatsApp Flows are rich, app-like forms rendered inside WhatsApp. The commerce module provisions 5 flow types:
-
Catalogue Flow — The primary shopping flow with 6 screens:
- Product List (search, browse, add to cart)
- Product Detail (image, description, variant, quantity)
- Checkout Cart (select items to check out)
- Delivery Details (address, delivery method)
- Order Summary (items, fees, total)
- Order Success (confirmation)
-
Cart Flow — 3 screens for viewing and managing the cart
-
Checkout Flow — 3 screens for standalone checkout (delivery → payment → confirmation)
-
Order Status Flow — 2 screens for viewing a single order's details and line items
-
Order History Flow — 2 screens for browsing/searching all past orders
3.2 The Shopping Journey (Step by Step)
Here's the complete customer journey from browsing to receiving an order:
1. CUSTOMER SENDS "menu"
└─► Main menu appears with buttons: Shop Now, My Cart, Checkout, Track Order, My Orders
2. CUSTOMER TAPS "Shop Now"
└─► Catalogue Flow opens with product list
├─► Search bar for finding products
├─► Product cards with prices
└─► Cart indicator in footer (shows item count + total)
3. CUSTOMER SELECTS A PRODUCT
└─► Product Detail screen shows:
├─► Product image
├─► Name, description, price
├─► Variant dropdown (if applicable)
├─► Quantity input
└─► "Add to Cart" button
4. CUSTOMER ADDS TO CART
└─► Confirmation message with cart summary
└─► Returns to product list with status message
5. CUSTOMER TAPS CHECKOUT FOOTER
└─► Checkout Cart screen: select which items to check out
6. CUSTOMER SELECTS ITEMS → PROCEEDS
└─► Delivery Details screen: name, phone, address, city, state, delivery method
7. CUSTOMER FILLS DELIVERY INFO
└─► Order Summary screen: items, delivery fee, total, "Complete Order" button
8. CUSTOMER COMPLETES ORDER
└─► Order is created in the database
└─► Stock is decremented
└─► Cart items are cleared
└─► Order confirmation message sent via WhatsApp
9. IF PAYMENT METHOD IS "CARD":
└─► Paystack payment link is generated
└─► Link is shortened
└─► Customer receives "Pay Now" button (CTA URL)
└─► Payment is split: vendor receives order total minus Nviti fee
10. CUSTOMER TRACKS ORDER
└─► Types "order status" or taps "Track My Order"
└─► Order Status Flow shows current status, items, delivery info
└─► Status updates (Processing → Shipped → Delivered) are pushed via WhatsApp
4. Cart System
4.1 How the Cart Works
Each WhatsApp visitor has their own cart, identified by their phone number and the vendor's company ID. The cart supports:
- Add to cart — with quantity and optional variant selection. Max 20 items (configurable).
- Update quantity — change quantity of existing items; setting qty to 0 removes the item.
- Remove items — by product ID or cart index.
- Clear cart — remove all items at once.
- Variant pricing — cart items can have different prices based on selected variant.
- Stock validation — checks stock before adding; decrements stock on order creation; restores stock on cancellation.
Cart data is stored in the carts table with fields: visitor_id, product_id, item name, price, qty, total, options (JSON for variant data), source, is_active flag.
4.2 Partial Checkout
The Catalogue Flow supports partial checkout — customers can select specific cart items to check out while leaving others in the cart. This is handled via selected_cart_ids in the checkout data.
5. Checkout Flow
5.1 Checkout State Machine
The checkout process is a state machine with 5 steps:
CART_REVIEW → DELIVERY_INFO → PAYMENT_METHOD → CONFIRMATION → COMPLETE
State is persisted in Visitor.metadata['checkout_state'] as JSON, including:
- Current step
- Cart snapshot
- Accumulated checkout data (name, address, payment method, etc.)
- Coupon code and discount
- Selected cart IDs (for partial checkout)
5.2 Totals Calculation
The calculateTotals() method computes:
- Subtotal — sum of cart item totals
- Shipping — flat rate from config + weight-based surcharge (100/kg over 10kg)
- VAT — if applicable
- Discount — from applied coupon (percentage or flat)
- Total — subtotal + shipping + VAT - discount
5.3 Coupon Support
Coupons can be applied during checkout:
- Percentage-based or flat discount
- Active/inactive toggle
- Expiry date
- Usage limit tracking
6. Orders
6.1 Order Creation
When checkout completes, OrderService::createFromCart() runs in a database transaction:
- Gets cart items (filtered by
selected_cart_idsif partial checkout) - Calculates subtotal, shipping, discount, VAT, total
- Creates an
Orderrecord with a unique UUID (ORD-XXXXXXXX) - Creates
OrdersItemrecords for each cart item - Decrements product stock (unless unlimited stock)
- Deletes purchased cart items
- Dispatches
OrderCreatedevent
6.2 Order Identification
Orders have multiple identifiers:
- UUID —
ORD-XXXXXXXXformat (primary) - Visitor Order ID —
{phone_digits}-{sequence}format (per-visitor sequential) - Display Order ID — formatted for customer-facing messages
6.3 Order Status Lifecycle
PENDING → PROCESSING → CONFIRMED → SHIPPED → DELIVERED
↓
CANCELLED ← (any pre-shipped status)
REFUNDED
FAILED
Status changes trigger OrderStatusChanged events which send WhatsApp notifications to the customer.
6.4 Order Management (Vendor Side)
Vendors manage orders through the Filament admin panel with:
- Order list with filtering by status, date, customer
- Order detail view with items, delivery info, payment status
- Status update actions
- Cancel order action (restores stock)
7. Payment Processing
7.1 Supported Payment Methods
| Method | How It Works |
|---|---|
| Cash on Delivery | No payment link generated. Customer pays on delivery. |
| Card (Paystack) | Payment link generated via Paystack. Customer pays online. Funds split between vendor and Nviti. |
7.2 Card Payment Flow (Paystack)
1. Customer selects "Card Payment" during checkout
2. Order is created → OrderCreated event fires
3. SendOrderNotification listener:
├─► Calls PaymentService::generateOrderPaymentLink()
│ ├─► Looks up vendor's Paystack subaccount code
│ ├─► Calculates Nviti platform fee via NvitiFeeService
│ ├─► Calls Paystack transaction/initialize API:
│ │ • amount = order total (in kobo)
│ │ • subaccount = vendor's subaccount code
│ │ • transaction_charge = Nviti fee (flat, in kobo)
│ ├─► Shortens the payment URL
│ └─► Stores link in Order.metadata
│
└─► Sends WhatsApp CTA URL message with "Pay Now" button
4. Customer taps "Pay Now" → Paystack checkout page
├─► Customer enters card details
├─► Paystack processes payment
├─► Funds split: Vendor receives (total - Nviti fee)
└─► Paystack webhook confirms payment (if configured)
7.3 Nviti Platform Fee
Nviti charges a platform fee on card payments. The fee uses progressive bands:
| Order Amount | Fee % |
|---|---|
| ₦0 – ₦50,000 | 1.0% |
| ₦50,001 – ₦100,000 | 0.85% |
| ₦100,001 – ₦150,000 | 0.75% |
| ₦150,001 – ₦300,000 | 0.65% |
| ₦300,001+ | 0.5% |
All fees are capped at ₦1,500 per order. The fee can be disabled globally via config.
7.4 Paystack Subaccount Management
When a vendor saves bank account details in Shop Settings:
- A Paystack Subaccount is created (or updated if one exists)
- The subaccount has
percentage_charge = 0(Nviti usestransaction_chargeinstead) - Subaccount code and ID are stored in
Company.metadata - Bank list is cached for 24 hours from Paystack's API
8. WhatsApp Notifications
8.1 Order Confirmation
When an order is placed:
- Card payment: Customer receives an interactive message with a "Pay Now" button linking to Paystack. Falls back to a text message with the payment link if interactive message fails.
- Cash on delivery: Customer receives a text confirmation with order ID and total.
8.2 Order Status Updates
When an order's status changes, the customer receives a WhatsApp notification:
- Interactive Flow (preferred): Opens an Order Status Flow with full order details, item list, and drill-down into individual products.
- Text fallback: Status summary with emoji indicators:
- 🔄 Processing
- ✅ Confirmed
- 🚚 Shipped
- 📦 Delivered
- ❌ Cancelled
- 💰 Refunded
Status notifications include the payment link for card orders that haven't been paid yet.
9. AI Integration
9.1 Structured Output
The commerce module integrates with Nviti's AI layer. When a customer sends a message that the AI determines is a commerce intent, it returns a structured output (CommerceStructuredOutputDTO) containing:
- The detected intent (browse, search, add to cart, etc.)
- Extracted data (product ID, quantity, search query)
- An optional action to execute
- An optional interactive message payload
- A confidence score
The StructuredOutputHandler processes this output and either:
- Executes the action and sends the result
- Sends an interactive message directly
- Sends a text response
9.2 Commerce Intents
The AI can detect 16 commerce intents:
| Intent | Description |
|---|---|
BROWSE_CATALOG |
Browse categories/products |
PRODUCT_SEARCH |
Search for products |
PRODUCT_VIEW |
View product details |
CART_ADD |
Add item to cart |
CART_REMOVE |
Remove item from cart |
CART_UPDATE |
Update cart item quantity |
CART_VIEW |
View cart |
CART_CLEAR |
Clear cart |
CHECKOUT_START |
Start checkout |
CHECKOUT_CONTINUE |
Continue checkout process |
ORDER_STATUS |
Check order status |
ORDER_HISTORY |
View order history |
APPLY_COUPON |
Apply a discount coupon |
CATALOGUE_BROWSE |
Browse the catalogue flow |
GENERAL_INQUIRY |
General commerce question |
SUPPORT |
Customer support request |
10. Architecture & Module Structure
app/Modules/Commerce/
├── Builders/
│ └── InteractiveMessageBuilder.php # WhatsApp message builder (lists, buttons, flows)
├── CommerceActionHandler.php # Central intent-to-action dispatcher
├── CommerceFacade.php # Laravel facade
├── CommerceServiceProvider.php # Service container bindings
├── Config/
│ └── commerce.php # All config: defaults, fees, settings definitions
├── Defaults/
│ └── ShopSettingDefaults.php # Flattens config into key-value settings
├── DTOs/
│ ├── CartDTO.php # Cart data transfer object
│ ├── CartItemDTO.php # Cart item DTO
│ ├── CheckoutDataDTO.php # Checkout form data
│ ├── CommerceStructuredOutputDTO.php # AI structured output
│ ├── OrderDTO.php # Order DTO
│ └── ProductDTO.php # Product DTO
├── Enums/
│ ├── CheckoutStep.php # CART_REVIEW → COMPLETE
│ ├── CommerceIntent.php # 16 intent types
│ ├── InteractiveMessageType.php # LIST, BUTTON, FLOW, etc.
│ ├── OrderStatus.php # PENDING → DELIVERED/CANCELLED
│ └── PaymentMethod.php # CASH, CARD, TRANSFER, WALLET
├── Events/
│ ├── CartAbandoned.php
│ ├── CartCleared.php
│ ├── CartUpdated.php
│ ├── CheckoutStarted.php
│ ├── OrderCreated.php
│ └── OrderStatusChanged.php
├── Exceptions/
│ ├── CartException.php
│ ├── CheckoutException.php
│ └── OrderException.php
├── Handlers/
│ ├── FlowResponseHandler.php # Handles WhatsApp Flow completions
│ ├── InteractiveMessageHandler.php # Routes button/text commands
│ └── StructuredOutputHandler.php # Processes AI commerce intents
├── Jobs/
│ ├── SendCartSummaryWithCheckoutJob.php
│ ├── SendPaymentLinkJob.php
│ └── SyncCommerceFormJob.php
├── Listeners/
│ └── SendOrderNotification.php # Order event → WhatsApp notification
├── Services/
│ ├── CartService.php # Cart CRUD + formatting
│ ├── CatalogService.php # Products, categories, search
│ ├── CheckoutFlowService.php # Multi-step checkout state machine
│ ├── CommerceFormService.php # WhatsApp Flow provisioning
│ ├── NvitiFeeService.php # Platform fee calculation
│ ├── OrderService.php # Order lifecycle management
│ ├── PaymentService.php # Paystack payment link generation
│ ├── PaystackSubaccountService.php # Vendor subaccount management
│ ├── ShippingService.php # Shipping cost calculation
│ └── Contracts/ # Service interfaces
└── Templates/
├── CartFlowTemplate.php # Cart viewing flow (3 screens)
├── CatalogueFlowTemplate.php # Main shopping flow (6 screens)
├── CheckoutFlowTemplate.php # Standalone checkout flow (3 screens)
├── HasSampleProducts.php # Shared sample data trait
├── OrderHistoryFlowTemplate.php # Order browser flow (2 screens)
└── OrderStatusFlowTemplate.php # Single order view flow (2 screens)
11. Data Flow Diagrams
11.1 Message Routing
WhatsApp Message
│
▼
ProcessWhatsappWebhookJob
│
├── Is it a button/list reply?
│ └── YES → InteractiveMessageHandler → Route by button ID prefix
│
├── Is it a flow completion (nfm_reply)?
│ └── YES → FlowResponseHandler → Route by flow token prefix
│
├── Is it a recognized text command (menu, cart, etc.)?
│ └── YES → InteractiveMessageHandler::handleTextCommand() → SHORT-CIRCUIT (no AI)
│
└── None of the above?
└── Pass to AI → StructuredOutputHandler (if commerce intent detected)
11.2 Payment Split Flow
Customer pays ₦100,000 via card
│
▼
Paystack receives ₦100,000
│
├── Vendor subaccount receives ₦99,150 (order total)
│
└── Nviti receives ₦850 (0.85% fee, via transaction_charge)
12. Key Technical Notes
- Currency: Nigerian Naira (NGN) by default. Configurable per vendor.
- Payment amounts: Paystack API expects amounts in kobo (multiply by 100).
- URL shortening: Payment links are shortened via
yorcreative/laravel-urlshortenerbefore sending to WhatsApp. Falls back to the full URL if shortening fails. - Flow provisioning: Commerce WhatsApp Flows are automatically provisioned when a vendor enables order processing. The JSON templates are synced to WhatsApp via the WhatsApp Business API.
- Multi-tenant: All data is scoped by
company_idandtenant_id. Products, orders, carts, and settings are isolated per vendor. - Stock management: Stock is decremented atomically during order creation using
lockForUpdate. Stock is restored on order cancellation. - Checkout timeout: Checkout sessions expire after 30 minutes (configurable).
- Bank account validation: Account name auto-fill is done via Paystack's account resolution API.
- Rate limiting: Product lists are paginated (20 items per page in flows, 10 in text-based browsing).
13. Test Coverage
| Test File | Tests | Assertions | Coverage |
|---|---|---|---|
NvitiFeeServiceTest.php |
18 | 45 | Fee bands, cap, kobo, breakdown, disabled state |
PaystackSubaccountServiceTest.php |
20 | 29 | Subaccount CRUD, bank list, caching, error handling |
CommerceTextCommandTest.php |
34 | — | Text command routing, workflow behavior |
InteractiveMessageBuilderTest.php |
15 | — | Message building for all types |
All commerce tests use Http::fake() to mock external APIs and RefreshDatabase for database isolation.