Align commerce module with the monorepo module structure by updating all namespaces to use the Core\Mod\Commerce convention. This change supports the recent monorepo separation and ensures consistency with other modules. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
8.8 KiB
Commerce Module Review
Updated: 2026-01-21 - Rate limiting, idempotency keys, and expired order cleanup implemented
Overview
The Commerce module is a comprehensive billing and subscription management system that handles:
- Order creation and checkout flows (single purchases and subscriptions)
- Multi-gateway payment processing (BTCPay primary, Stripe secondary)
- Subscription lifecycle management (create, pause, cancel, renew)
- Dunning system for failed payment recovery (retry, pause, suspend, cancel stages)
- Invoice generation and PDF creation
- Coupon/discount management
- Tax calculation (UK VAT, EU OSS, US state taxes, Australian GST)
- Refund processing
- Permission matrix for multi-entity hierarchies (M1/M2/M3 model)
- Webhook handling with deduplication and audit logging
The module follows the modular monolith architecture with Boot.php registering services, routes, commands, and Livewire components.
Production Readiness Score: 94/100 (was 92/100 - checkout protection and cleanup added 2026-01-21)
The module is well-architected with solid fundamentals. All critical issues fixed in Wave 4.
Critical Issues (Must Fix)
-
Webhook signature verification bypassed in tests - VERIFIED: Production implementation properly calls
$this->gateway->verifyWebhookSignature()and returns 401 on failure. Already secure. -
Missing database indexes on webhook_events table - VERIFIED: Migration already has
$table->unique(['gateway', 'event_id'])index. Already correct. -
Order.workspace scope uses wrong field - FIXED:
scopeForWorkspacenow usesorderable_typeandorderable_idfor polymorphic relations. -
Invoice workspace relationship missing for User orderables - FIXED: Added
getWorkspaceIdAttribute()accessor andgetResolvedWorkspace()method to Order model. Webhook controllers updated to use these. -
Missing transaction isolation in webhook handlers - FIXED: Both
BTCPayWebhookControllerandStripeWebhookControllernow wrap processing inDB::transaction(). -
BTCPay gateway chargePaymentMethod - FIXED:
CommerceService::retryInvoicePayment()now correctly checks$payment->status === 'succeeded'and handles BTCPay pending payments appropriately.
Recommended Improvements
-
Add rate limiting per customer on checkout - DONE:
CheckoutRateLimiterservice implemented with sliding window rate limiting. Uses workspace/user/IP hierarchy for throttle keys. 5 attempts per 15 minutes. -
Add idempotency keys to order creation - DONE:
idempotency_keyfield added to orders table. Migration2026_01_21_120000_add_idempotency_key_to_orders_table.phpcreated. Used in CommerceService and CheckoutPage. -
Extract CouponValidationResult to dedicated file - DONE: Extracted to
Data/CouponValidationResult.php. -
Add scheduled job for expired order cleanup - DONE:
CleanupExpiredOrderscommand (commerce:cleanup-orders) created. Cancels pending orders older than configurable TTL. Supports--dry-runand--ttloptions. Chunked processing with logging. -
Add observability/metrics - Consider adding metrics for: payment success rate, dunning recovery rate, webhook processing time, gateway errors.
-
Standardise error responses in API controllers -
CommerceControllermethods should return consistent JSON error structures. -
Add model factories for Commerce models - DONE: Created OrderFactory, InvoiceFactory, SubscriptionFactory, CouponFactory, and PaymentFactory in
Database/Factories/. -
Implement webhook retry mechanism - Failed webhooks (logged with status
failed) have no automatic retry. Consider dead-letter queue or scheduled retry. -
Add cancellation feedback collection - Store cancellation reasons/feedback when users cancel subscriptions for product insights.
-
Tax ID validation is config-gated but implementation unclear - Config has
validate_tax_ids_apibut actual HMRC/VIES API integration not visible in TaxService.
Missing Features (Future)
-
Usage-based billing - Config has
features.usage_billing => falseas placeholder for metered billing. -
Multi-currency support - Currently defaults to GBP. Config exists but full multi-currency handling not implemented.
-
Credit notes - No model for credit notes when issuing partial refunds or adjustments.
-
Payment method management UI -
PaymentMethodsLivewire component exists but no visible add/remove card flow. -
Subscription upgrade preview API -
previewUpgradeendpoint exists but no corresponding frontend to show prorated amounts. -
Bulk coupon generation - CouponService can generate codes but no bulk creation for marketing campaigns.
-
Affiliate/referral tracking -
RewardAgentReferralOnSubscriptionlistener exists but full referral system incomplete. -
Warehouse/inventory system - Models exist (
Warehouse,Inventory,InventoryMovement) but services not implemented. May be for physical goods expansion.
Test Coverage Assessment
Current Coverage:
CheckoutFlowTest.php- Basic checkout flow testingCompoundSkuTest.php- SKU parsing and buildingContentOverrideServiceTest.php- Content override systemCouponServiceTest.php- Comprehensive coupon validation and calculationDunningServiceTest.php- Thorough dunning lifecycle testingProcessSubscriptionRenewalTest.php- Renewal job testingRefundServiceTest.php- Refund flow with mocked gatewaySubscriptionServiceTest.php- Subscription CRUD operationsTaxServiceTest.php- Tax calculation scenariosWebhookTest.php- Extensive webhook handling for both gateways
Coverage Gaps:
- No unit tests (Unit/ directory empty)
- No tests for
PermissionMatrixServicedespite complex hierarchy logic - No tests for
InvoiceServicePDF generation - No tests for
ProductCatalogServiceandWarehouseService - No integration tests for actual gateway API calls
- No tests for admin Livewire components
- No tests for web checkout Livewire components
- Missing edge case tests for concurrent webhook delivery
- No load/stress testing for high-volume scenarios
Test Quality:
- Tests use Pest with good describe/it structure
- Proper use of RefreshDatabase trait
- Good mocking of external dependencies
- Notification and Event faking used appropriately
Security Concerns
-
Webhook signature verification - Properly implemented using HMAC-SHA256 for BTCPay and Stripe's built-in verification. Signatures are masked in logs.
-
No explicit input validation on order metadata - The
metadataarray passed to orders is stored directly. Consider schema validation to prevent injection of malicious data. -
Invoice number generation uses sequential IDs - Pattern
INV-{year}-{sequence}is predictable. Not a vulnerability but consider if invoice numbers should be harder to enumerate. -
PDF generation with user data - Invoice PDFs include user-provided billing names/addresses. Ensure DomPDF or Snappy has XSS protections enabled.
-
Coupon code brute-forcing - No rate limiting on coupon validation. Attackers could enumerate valid codes.
-
Permission matrix training mode -
training_modeconfig allows undefined permissions to prompt for approval. Ensure this is NEVER enabled in production (COMMERCE_MATRIX_TRAINING=false). -
API authentication - Provisioning API uses
commerce.apimiddleware but implementation not visible. Verify bearer token validation is secure. -
No audit logging for admin actions - Coupon creation, refund processing, subscription cancellation by admins should be logged for compliance.
Notes
Architecture Observations
- Clean separation between gateway-specific logic (PaymentGatewayContract implementations) and business logic (Services)
- Good use of Laravel events for subscription lifecycle (SubscriptionCreated, SubscriptionUpdated, etc.)
- Dunning service follows best practices with configurable retry intervals and grace periods
- Webhook logging with deduplication prevents replay attacks and aids debugging
Configuration
- Comprehensive config file covering all aspects of billing
- Environment variable support for sensitive values (API keys, secrets)
- Feature flags allow gradual rollout of capabilities
Code Quality
- Consistent use of type hints and return types
- Proper use of database transactions for multi-step operations
- Good error handling in critical paths
- UK English spelling used consistently (colour, organisation, centre)
Dependencies
barryvdh/laravel-dompdffor PDF generation- Stripe PHP SDK (implied by StripeGateway)
- BTCPay Server API client (custom implementation)
Migration State
- 11 migrations covering all models
- Latest migrations dated 2026-01-21 (pause_count and webhook_events)
- No down() methods visible - verify rollback capability