A sophisticated Laravel 12 API service that automatically monitors competitor prices and applies intelligent price matching rules for Mumzworld's e-commerce platform across UAE and KSA markets.
- Docker & Docker Compose
- Git
-
Clone the Repository
git clone <repository-url> cd mumzworld-price-matching-service-v2
-
Install Dependencies
composer install
-
Environment Configuration
cp .env.example .env
Required Environment Variables:
# Database DB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE=price_matching_db DB_USERNAME=sail DB_PASSWORD=password # Redis (Queues & Coordination) REDIS_HOST=redis REDIS_PASSWORD=null REDIS_PORT=6379 # Price Matcher Configuration PRICE_MATCHER_CRAWL_TTL=3600 PRICE_MATCHER_MATCH_LOCK_TTL=3000 PRICE_MATCHER_DISPATCH_LOCK_TTL=3900 PRICE_MATCHER_DEFAULT_MARGIN_CAP=0.15 # External Crawler API CRAWLER_API_BASE_URL=http://crawler-service:3000 CRAWLER_API_TIMEOUT=30 CRAWLER_API_RETRY_ATTEMPTS=3 # Queue Configuration QUEUE_CONNECTION=redis HORIZON_BALANCE=auto
-
Start Docker Services
docker-compose up -d
-
Run Database Migrations
docker-compose exec app php artisan migrate -
Seed Database with Price Matching Data
docker-compose exec app php artisan db:seed --class=PriceMatchingDataSeeder -
Start Laravel Horizon (Queue Processing)
docker-compose exec app php artisan horizon -
Verify Installation
# Check queue workers docker-compose exec app php artisan horizon:status # Check imported data docker-compose exec app php artisan tinker >>> App\Models\MumzworldProduct::count() >>> App\Models\Competitor::count()
- Framework: Laravel 12 (API-only)
- Runtime: PHP 8.3
- Database: MySQL 8.0
- Cache/Queues: Redis 7.0
- Queue Management: Laravel Horizon
- Containerization: Docker & Docker Compose
- External Integration: REST API crawler service
app/
βββ Console/Commands/ # Artisan commands for price matching
βββ Http/Controllers/ # API controllers (if needed)
βββ Jobs/ # Queue jobs for crawling & matching
βββ Models/ # Eloquent models for price data
βββ Services/ # Business logic services
βββ Providers/ # Service providers
database/
βββ migrations/ # Database schema migrations
βββ seeders/ # CSV data import seeders
docs/
βββ input-data/ # CSV data files for seeding
βββ price-matching-locks.md # Lock mechanism documentation
graph TB
subgraph "Horizon Supervisors"
CS[Crawler Supervisor<br/>40 workers<br/>crawls queue]
MS[Match Supervisor<br/>10 workers<br/>matching queue]
RS[Report Supervisor<br/>4 workers<br/>reports queue]
end
subgraph "Job Types"
DCJ[DispatchCrawlCompetitorJob]
DMJ[DispatchCrawlMumzworldJob]
EPJ[ExecutePriceMatchingJob]
GRJ[GeneratePriceMatchingReportJob]
end
CS --> DCJ
CS --> DMJ
MS --> EPJ
RS --> GRJ
sequenceDiagram
participant S as Scheduler
participant DC as DispatchCrawlsCommand
participant Q as Queue System
participant R as Redis
participant C as CrawlerAPI
participant PM as PriceMatcherService
participant DB as Database
S->>DC: Hourly trigger
DC->>DB: Get all SKUΓCountry products
DC->>R: Set expected crawls
DC->>Q: Dispatch external competitor jobs
Q->>C: Crawl competitor URLs
C-->>Q: Return competitor prices
Q->>DB: Store competitor prices
Q->>R: Mark crawl complete
R->>R: Check if all crawls done
R->>Q: Dispatch price matching job
Q->>PM: Execute price matching
PM->>DB: Get business rules
PM->>PM: Apply matching logic
PM->>DB: Log price match decision
PM->>Q: Dispatch report generation
Q->>DB: Generate CSV reports
graph LR
subgraph "Dispatch Lock (65min)"
DL[Prevents duplicate<br/>SKU dispatching<br/>per hour]
end
subgraph "Match Lock (50min)"
ML[Prevents concurrent<br/>price matching<br/>same SKU]
end
subgraph "Redis Keys"
DK["dispatch:lock:{sku}:{country}"]
MK["match:lock:{sku}:{country}"]
end
DL --> DK
ML --> MK
| Table | Purpose |
|---|---|
competitors |
External competitors (Amazon, Noon) + Internal (Mumzworld) |
mumzworld_products |
Product catalog with cost/selling prices |
competitor_product_urls |
URLs to crawl for each SKUΓcompetitorΓcountry |
competitor_prices |
Crawled competitor pricing data |
mumzworld_price_snapshots |
Live mumzworld price snapshots |
| Table | Purpose |
|---|---|
price_match_exceptions |
SKUs excluded from price matching |
margin_caps |
Minimum margin requirements per SKU |
price_up_constraints |
SKUs blocked from price increases |
preferred_competitor_overrides |
Priority competitor selection |
price_match_logs |
Complete audit trail with price direction tracking |
erDiagram
competitors ||--o{ competitor_product_urls : has
competitors ||--o{ competitor_prices : stores
mumzworld_products ||--o{ competitor_product_urls : maps_to
mumzworld_products ||--o{ price_match_logs : generates
competitor_prices ||--o{ price_match_logs : influences
- Exception Check: Skip if SKU in exceptions list
- Candidate Selection: Choose preferred competitor OR cheapest discounted price
- Price-Up Guard: Block increases if SKU in price-up constraints
- Margin Cap Validation: Ensure margin β₯ minimum required margin
- Price Application: Update if all checks pass
- External Competitors (
is_internal = false): Amazon, FirstCry, Noon, etc. - Internal Competitors (
is_internal = true): Mumzworld UAE/KSA for live price validation
# Dispatch crawl jobs for all products
php artisan price-matcher:dispatch-crawls
# Dispatch for specific SKUs/countries
php artisan price-matcher:dispatch-crawls --sku=SKU123 --country=uae
# Generate reports for specific batch
php artisan price-matcher:generate-report {batch_id}
# Check lock status (debugging)
php artisan price-matcher:check-locks
php artisan price-matcher:check-locks SKU123 uae# Import all CSV data
php artisan db:seed --class=PriceMatchingDataSeeder
# Import specific data types
php artisan db:seed --class=CompetitorSeeder
php artisan db:seed --class=MumzworldProductSeeder
php artisan db:seed --class=CompetitorProductUrlSeederThe system imports data from the following CSV files in docs/input-data/:
| File | Purpose | Format |
|---|---|---|
Price Match SKU - UAE + KSA.csv |
Product catalog | SKU,Name,Brand,Country,Currency,Cost,Selling price |
uae-sku-urls.csv |
UAE competitor URLs | Mumzworld AE,amazon UAE,First cry uae,Noon UAE |
ksa-sku-url.csv |
KSA competitor URLs | SKU,Name,Amazon SA,Firstcry SA,Nahdionline SA,Noon sa |
mumzworld-sku-country-url.csv |
Mumzworld URLs | SKU,Name,Country,mumzworld url |
exceptions.csv |
Price match exceptions | SKU,Name,Brand,Country,Exception |
margin-caps.csv |
Margin requirements | SKU,Name,Brand,Country,Margin Cap |
price-up.csv |
Price increase blocks | SKU,Name,Brand,Country,Price Up Exception |
matched_prices.csv: Successful price matches with direction analysisblocked_by_exception.csv: SKUs excluded from matchingmargin_violations.csv: Failed margin cap validationsprice_up_blocks.csv: Blocked price increasessummary.csv: High-level metrics and performance
Access Horizon dashboard at: http://localhost/horizon
- Monitor job throughput
- View failed jobs
- Track queue metrics
- Manage supervisor processes
Adjust worker counts in config/horizon.php:
'crawler-supervisor' => [
'maxProcesses' => 40, // Increase for more crawl throughput
],
'match-supervisor' => [
'maxProcesses' => 10, // Scale for price matching load
],To add new countries (e.g., Egypt):
- Add competitors:
mumzworld_egy,amazon_egy, etc. - Update seeder data with new country URLs
- No code changes required - system auto-scales
- Redis coordination minimizes database locks
- Chunked processing prevents memory issues
- Lock-based deduplication prevents wasted work
- Horizon auto-balancing optimizes resource usage
Built with β€οΈ @ Mumzworld