php-commerce/config.php
Snider 2e5cd499b9 security: complete rate limiting and fraud service implementation (P1-040)
Add missing files from P1-040/P1-041 implementation:
- CheckoutRateLimitException for 429 responses when rate limit exceeded
- FraudAssessment data object for fraud scoring results
- FraudService for velocity checks and Stripe Radar integration
- Register services in Boot.php
- Add fraud detection configuration in config.php
- Add CouponServiceTest for input sanitisation

The CheckoutRateLimiter (already tracked) is now properly integrated with
the exception handling, and the FraudService provides defence-in-depth
with velocity-based and geo-anomaly detection.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 16:09:29 +00:00

522 lines
17 KiB
PHP

<?php
return [
/*
|--------------------------------------------------------------------------
| Currency Settings
|--------------------------------------------------------------------------
|
| Multi-currency configuration for commerce transactions.
|
*/
'currency' => env('COMMERCE_CURRENCY', 'GBP'),
'currencies' => [
// Base currency for reporting and internal calculations
'base' => env('COMMERCE_BASE_CURRENCY', 'GBP'),
// Supported currencies with display properties
'supported' => [
'GBP' => [
'name' => 'British Pound',
'symbol' => '£',
'symbol_position' => 'before', // 'before' or 'after'
'decimal_places' => 2,
'thousands_separator' => ',',
'decimal_separator' => '.',
'flag' => 'gb',
],
'USD' => [
'name' => 'US Dollar',
'symbol' => '$',
'symbol_position' => 'before',
'decimal_places' => 2,
'thousands_separator' => ',',
'decimal_separator' => '.',
'flag' => 'us',
],
'EUR' => [
'name' => 'Euro',
'symbol' => '€',
'symbol_position' => 'before',
'decimal_places' => 2,
'thousands_separator' => ' ',
'decimal_separator' => ',',
'flag' => 'eu',
],
'AUD' => [
'name' => 'Australian Dollar',
'symbol' => 'A$',
'symbol_position' => 'before',
'decimal_places' => 2,
'thousands_separator' => ',',
'decimal_separator' => '.',
'flag' => 'au',
],
'CAD' => [
'name' => 'Canadian Dollar',
'symbol' => 'C$',
'symbol_position' => 'before',
'decimal_places' => 2,
'thousands_separator' => ',',
'decimal_separator' => '.',
'flag' => 'ca',
],
],
// Exchange rate provider settings
'exchange_rates' => [
// Provider: 'stripe', 'ecb', 'openexchangerates', 'fixed'
'provider' => env('COMMERCE_EXCHANGE_RATE_PROVIDER', 'ecb'),
// API key for providers that require it (e.g., openexchangerates)
'api_key' => env('COMMERCE_EXCHANGE_RATE_API_KEY'),
// Cache duration in minutes (default: 60 minutes)
'cache_ttl' => env('COMMERCE_EXCHANGE_RATE_CACHE_TTL', 60),
// Update frequency in minutes for scheduled updates
'update_frequency' => env('COMMERCE_EXCHANGE_RATE_UPDATE_FREQUENCY', 60),
// Fixed rates (used when provider is 'fixed' or as fallback)
'fixed' => [
'GBP_USD' => 1.27,
'GBP_EUR' => 1.17,
'GBP_AUD' => 1.93,
'GBP_CAD' => 1.72,
],
],
// Auto-convert prices when no explicit currency price exists
'auto_convert' => env('COMMERCE_AUTO_CONVERT_PRICES', true),
// Default currency detection order: 'geolocation', 'browser', 'default'
'detection_order' => ['geolocation', 'browser', 'default'],
// Map countries to preferred currencies
'country_currencies' => [
'GB' => 'GBP',
'US' => 'USD',
'AU' => 'AUD',
'CA' => 'CAD',
// EU countries default to EUR
'AT' => 'EUR', 'BE' => 'EUR', 'BG' => 'EUR', 'HR' => 'EUR',
'CY' => 'EUR', 'CZ' => 'EUR', 'DK' => 'EUR', 'EE' => 'EUR',
'FI' => 'EUR', 'FR' => 'EUR', 'DE' => 'EUR', 'GR' => 'EUR',
'HU' => 'EUR', 'IE' => 'EUR', 'IT' => 'EUR', 'LV' => 'EUR',
'LT' => 'EUR', 'LU' => 'EUR', 'MT' => 'EUR', 'NL' => 'EUR',
'PL' => 'EUR', 'PT' => 'EUR', 'RO' => 'EUR', 'SK' => 'EUR',
'SI' => 'EUR', 'ES' => 'EUR', 'SE' => 'EUR',
],
],
/*
|--------------------------------------------------------------------------
| Payment Gateways
|--------------------------------------------------------------------------
|
| Configuration for payment gateways. BTCPay is the primary gateway,
| with Stripe available but hidden from the UI initially.
|
*/
'gateways' => [
'btcpay' => [
'enabled' => env('BTCPAY_ENABLED', true),
'url' => env('BTCPAY_URL', 'https://pay.host.uk.com'),
'store_id' => env('BTCPAY_STORE_ID'),
'api_key' => env('BTCPAY_API_KEY'),
'webhook_secret' => env('BTCPAY_WEBHOOK_SECRET'),
'default_payment_methods' => ['BTC', 'LTC', 'XMR'],
],
'stripe' => [
'enabled' => env('STRIPE_ENABLED', false), // Hidden initially
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'),
],
],
/*
|--------------------------------------------------------------------------
| Billing Settings
|--------------------------------------------------------------------------
|
| General billing configuration.
|
*/
'billing' => [
'invoice_prefix' => env('COMMERCE_INVOICE_PREFIX', 'INV-'),
'order_prefix' => env('COMMERCE_ORDER_PREFIX', 'ORD-'),
'default_tax_rate' => 20, // UK VAT
'invoice_due_days' => 14,
'auto_charge' => true,
'send_invoice_emails' => true,
],
/*
|--------------------------------------------------------------------------
| Dunning Settings
|--------------------------------------------------------------------------
|
| Failed payment recovery configuration.
|
*/
'dunning' => [
'enabled' => true,
// Exponential backoff: days after initial failure to schedule each retry
// [1, 3, 7] = retry at day 1, day 3, day 7 (total ~11 days of retries)
'retry_days' => [1, 3, 7],
// Days after subscription paused to suspend workspace entitlements
// Paused = billing stopped but workspace accessible
// Suspended = workspace features restricted
'suspend_after_days' => 14,
// Days after subscription paused to cancel entirely
// After cancellation, workspace may be downgraded to free tier
'cancel_after_days' => 30,
// Grace period before first retry (hours)
// Gives customer time to fix payment method before automated retries
'initial_grace_hours' => 24,
// Send email notifications at each dunning stage
'send_notifications' => true,
],
/*
|--------------------------------------------------------------------------
| Tax Settings
|--------------------------------------------------------------------------
|
| Tax calculation configuration. Supports UK VAT, EU OSS, US state taxes,
| and Australian GST.
|
*/
'tax' => [
'enabled' => true,
'validate_tax_ids' => true,
'validate_tax_ids_api' => env('COMMERCE_VALIDATE_TAX_IDS_API', true), // Call HMRC/VIES APIs
'digital_services' => true, // All our products are digital
// Business details for invoices
'business' => [
'name' => env('COMMERCE_BUSINESS_NAME', 'Host UK Ltd'),
'address_line1' => env('COMMERCE_BUSINESS_ADDRESS_1', ''),
'address_line2' => env('COMMERCE_BUSINESS_ADDRESS_2', ''),
'city' => env('COMMERCE_BUSINESS_CITY', 'London'),
'postcode' => env('COMMERCE_BUSINESS_POSTCODE', ''),
'country' => env('COMMERCE_BUSINESS_COUNTRY', 'United Kingdom'),
'vat_number' => env('COMMERCE_VAT_NUMBER'),
'company_number' => env('COMMERCE_COMPANY_NUMBER'),
'email' => env('COMMERCE_BUSINESS_EMAIL', 'support@host.uk.com'),
],
// UK VAT
'uk' => [
'rate' => 20,
'reverse_charge_b2b' => true,
],
// EU One-Stop Shop
'eu_oss' => [
'enabled' => true,
'registered_country' => 'GB', // We're registered in UK
],
// US State Taxes
'us' => [
'enabled' => true,
'nexus_states' => ['CA', 'NY', 'TX', 'FL', 'WA'], // States where we have nexus
],
// Australian GST
'australia' => [
'enabled' => true,
'rate' => 10,
'threshold' => 75000, // AUD threshold
],
],
/*
|--------------------------------------------------------------------------
| Subscription Settings
|--------------------------------------------------------------------------
|
| Configuration for recurring billing.
|
*/
'subscriptions' => [
'allow_proration' => true,
'proration_behaviour' => 'create_prorations', // or 'none'
'cancel_at_period_end' => true, // Grace period instead of immediate cancellation
'allow_pause' => true,
'max_pause_cycles' => 3,
],
/*
|--------------------------------------------------------------------------
| Checkout Settings
|--------------------------------------------------------------------------
|
| Configuration for the checkout process.
|
*/
'checkout' => [
'require_billing_address' => true,
'require_tax_id_for_b2b' => false,
'allowed_countries' => null, // null = all countries
'blocked_countries' => [], // Countries we don't serve
'session_ttl' => 30, // Minutes before checkout session expires
],
/*
|--------------------------------------------------------------------------
| Fraud Detection Settings
|--------------------------------------------------------------------------
|
| Configuration for fraud detection and prevention.
| Uses Stripe Radar for Stripe payments. BTCPay payments rely on
| blockchain confirmations for security.
|
*/
'fraud' => [
// Enable fraud detection
'enabled' => env('COMMERCE_FRAUD_DETECTION', true),
// Stripe Radar integration (requires Stripe Radar subscription)
'stripe_radar' => [
'enabled' => env('COMMERCE_STRIPE_RADAR', true),
// Block payments with risk level equal or above this threshold
// Options: 'highest', 'elevated', 'normal' (block highest only, elevated+, or all flagged)
'block_threshold' => env('COMMERCE_STRIPE_RADAR_BLOCK_THRESHOLD', 'highest'),
// Review payments at this risk level (manual review required)
'review_threshold' => env('COMMERCE_STRIPE_RADAR_REVIEW_THRESHOLD', 'elevated'),
// Store fraud scores on orders for analysis
'store_scores' => true,
],
// Velocity checks (rate limiting beyond checkout rate limiter)
'velocity' => [
'enabled' => env('COMMERCE_FRAUD_VELOCITY', true),
// Maximum orders per IP per hour
'max_orders_per_ip_hourly' => 5,
// Maximum orders per email per day
'max_orders_per_email_daily' => 10,
// Maximum failed payments per workspace per hour
'max_failed_payments_hourly' => 3,
],
// Geo-anomaly detection
'geo' => [
'enabled' => env('COMMERCE_FRAUD_GEO', true),
// Flag if billing country differs from IP country
'flag_country_mismatch' => true,
// High-risk countries (require manual review)
'high_risk_countries' => [],
],
// Actions on fraud detection
'actions' => [
// Log all fraud signals
'log' => true,
// Send notification to admin on high-risk orders
'notify_admin' => true,
// Automatically block orders above threshold
'auto_block' => true,
],
],
/*
|--------------------------------------------------------------------------
| Invoice PDF Settings
|--------------------------------------------------------------------------
|
| Configuration for invoice PDF generation.
|
*/
'pdf' => [
'driver' => 'dompdf', // dompdf or snappy
'paper' => 'a4',
'orientation' => 'portrait',
'font' => 'sans-serif',
'storage_disk' => 'local',
'storage_path' => 'invoices',
],
/*
|--------------------------------------------------------------------------
| Notification Settings
|--------------------------------------------------------------------------
|
| Email notifications for commerce events.
|
*/
'notifications' => [
'order_confirmation' => true,
'invoice_generated' => true,
'payment_received' => true,
'payment_failed' => true,
'subscription_created' => true,
'subscription_cancelled' => true,
'subscription_renewed' => true,
'refund_processed' => true,
'upcoming_renewal' => true,
'upcoming_renewal_days' => 7,
],
/*
|--------------------------------------------------------------------------
| Feature Flags
|--------------------------------------------------------------------------
|
| Toggle commerce features on/off.
|
*/
'features' => [
'coupons' => true,
'refunds' => true,
'trials' => true,
'setup_fees' => true,
'usage_billing' => env('COMMERCE_USAGE_BILLING', false),
],
/*
|--------------------------------------------------------------------------
| Usage-Based Billing
|--------------------------------------------------------------------------
|
| Configuration for metered/usage-based billing.
|
*/
'usage_billing' => [
// Whether to sync usage to Stripe automatically
'sync_to_stripe' => env('COMMERCE_USAGE_SYNC_STRIPE', true),
// Sync frequency in minutes (for scheduled jobs)
'sync_interval' => env('COMMERCE_USAGE_SYNC_INTERVAL', 60),
// Whether to aggregate events in real-time or batch
'realtime_aggregation' => true,
// Maximum events to process per batch
'batch_size' => 1000,
// Retention period for usage events (days)
'event_retention_days' => 90,
// Default aggregation type for new meters
'default_aggregation' => 'sum', // sum, max, last_value
// Notifications
'notifications' => [
// Alert when usage reaches percentage of included quota
'usage_threshold_alerts' => [50, 75, 90, 100],
// Send weekly usage summary
'weekly_summary' => true,
],
],
/*
|--------------------------------------------------------------------------
| Commerce Matrix (Multi-Entity Hierarchy)
|--------------------------------------------------------------------------
|
| Configuration for the permission matrix system.
|
| Entity types:
| - M1: Master Company (source of truth, owns product catalog)
| - M2: Facades/Storefronts (select from M1, can override content)
| - M3: Dropshippers (full inheritance, no management responsibility)
|
*/
'matrix' => [
// Training mode - undefined permissions prompt for approval
'training_mode' => env('COMMERCE_MATRIX_TRAINING', false),
// Production mode - undefined = denied
'strict_mode' => env('COMMERCE_MATRIX_STRICT', true),
// Log all permission checks (for audit)
'log_all_checks' => env('COMMERCE_MATRIX_LOG_ALL', false),
// Log denied requests
'log_denials' => true,
// Default action when permission undefined (only if strict=false)
'default_allow' => false,
],
/*
|--------------------------------------------------------------------------
| Entity Types
|--------------------------------------------------------------------------
|
| Configuration for commerce entity types.
|
*/
'entities' => [
'types' => [
'm1' => [
'name' => 'Master Company',
'can_have_children' => true,
'child_types' => ['m2', 'm3'],
],
'm2' => [
'name' => 'Facade/Storefront',
'can_have_children' => true,
'child_types' => ['m3'],
],
'm3' => [
'name' => 'Dropshipper',
'can_have_children' => true, // Can have own M2s
'child_types' => ['m2'],
'inherits_catalog' => true,
],
],
],
/*
|--------------------------------------------------------------------------
| SKU Configuration
|--------------------------------------------------------------------------
|
| Configuration for SKU lineage tracking.
|
*/
'sku' => [
// SKU format: {m1_code}-{m2_code}-{master_sku}
'separator' => '-',
'include_m1' => true,
'include_m2' => true,
],
];