enodastore modification summary

1) PageController update
Modification: PageController dashboard route support
Date: 2026-11-04

This change updates the dashboard controller to support the billing and settings sections for store owners.

What changed:
- Reads the store and active plan information for the owner.
- Detects the requested dashboard section/tab.
- Renders the billing page when /dashboard/billing is requested.
- Renders the settings page when /dashboard/settings or /dashboard/settings/{tab} is requested.
- Keeps the fallback to the main dashboard when no specific section is requested.

Why:
- The previous controller only rendered a generic dashboard screen and did not handle the new owner settings/billing routes.
- This makes the owner panel sections reachable and consistent with the route structure.

2) Route additions
Modification: Route additions for settings and billing
Date: 2026-11-04

This change adds the required route entries for the owner settings experience.

Added routes:
- /dashboard/settings
- /dashboard/settings/{tab}
- /dashboard/{section}

Why:
- The app needs dedicated route handling so the settings tabs and billing screen load correctly.
- This prevents the dashboard fallback from swallowing the requested sections.

3) Billing page view
Modification: Billing page view
Date: 2026-11-04

This change creates the billing page template for the store owner area.

Included sections:
- Current plan summary card
- Renewal and days-left information
- Plan comparison list with upgrade actions
- Usage meters
- Payment history table

Why:
- The project needed a concrete billing screen matching the owner panel design flow.
- This gives the store owner an interface to review plan information and payment records.

4) Settings page view
Modification: Settings page view
Date: 2026-11-04

This change creates the owner settings page shell with tab navigation and a preview card.

Included sections:
- Top header and Save button
- Horizontal tab strip for settings sections
- General settings form layout
- Store preview block

Why:
- The app needed a reusable settings entry page to support the owner settings experience.
- The tabbed layout is a foundation for the later detailed sections (general, WhatsApp, payment, domain, etc.).

5) Dashboard section detection fix
Modification: Dashboard section detection for owner settings and billing routes
Date: 2026-11-04

This change fixes the dashboard controller so it resolves the requested section from the actual request path when route params are empty.

What changed:
- Reads the current request path before selecting a section.
- Maps /dashboard/settings to the settings view.
- Maps /dashboard/billing to the billing view.
- Keeps support for /dashboard/settings/{tab} and /dashboard/{section}.

Why:
- The previous controller only relied on URL params and could fall back to the generic dashboard when a direct settings or billing route was requested.
- This ensures the store owner sees the intended page instead of the dashboard landing screen.

6) Billing migration duplicate-column cleanup
Modification: Forward migration 010 removes duplicate billing aliases introduced by migration 009
Date: 2026-11-05

What changed:
- Added database/migrations/010_remove_duplicate_billing_columns.sql. It copies non-empty values from the new aliases to the existing canonical columns where the canonical value is empty/default, then drops the aliases.
- Kept the established billing names used by existing code: subscriptions.billing_cycle, starts_at and ends_at; payments.billing_cycle, utr_number and screenshot; invoices.invoice_number, subtotal and tax_rate.
- Kept the non-duplicate trial_ends_at and cancel_at_period_end subscription columns.
- Did not edit migrations 005-009.
- Applied migration 010 to the configured local MySQL database and confirmed the final DESCRIBE output.

Verification:
- tests/subscription_service_test.php: 24 assertions passed.
- tests/analytics_settings_test.php: 8 checks passed against its existing analytics fixture data in local MySQL.
- tests/smoke.php was run, but could not pass because this environment has no SMOKE_BASE_URL, owner credentials, settled invoice ID or payment screenshot. Its HTTP checks therefore report setup failures, not application regressions.
- The application has no explicit code references to trial_ends_at or cancel_at_period_end yet. They remain schema-only; their behavior was not added in this cleanup turn.

7) Subscription lifecycle, daily processing, and self-contained smoke test
Modification: Removed the last payments reason duplicate and implemented subscription expiry/cancellation lifecycle
Date: 2026-11-05

What changed:
- Added database/migrations/011_remove_duplicate_payment_reason.sql. It copies a non-empty payments.reason into reject_reason when the canonical value is empty, then drops reason.
- Updated subscription settlement, Razorpay failure/cancel, manual rejection, owner payment history query, and billing view to use payments.reject_reason only.
- Implemented trial_ends_at in SubscriptionService::startTrial, defaulting to the global trial_days setting when no explicit day count is provided.
- Daily processing now uses trial_ends_at for trials, transitions expired trial/active subscriptions to grace and then expired, and expires a scheduled cancellation at period end without entering grace.
- Added cancelAtPeriodEnd and resumeSubscription methods and CSRF-protected owner POST routes. Billing shows the Cancel at period end / Resume controls.
- Scheduled downgrades update the active plan. Storefront product selection applies the lower plan limit; excess products remain stored and are not deleted.
- Reminder delivery now atomically claims reminder_7_sent_at or reminder_1_sent_at, resets a claim if mail delivery fails, and does not send duplicate reminders on repeated daily runs.
- The existing public/cron/daily.php entrypoint was already present; the updated SubscriptionService is used by it. It remains CLI-accessible and has its existing authenticated web mode.
- Expanded tests/subscription_service_test.php with fake-date and transactional real-MySQL lifecycle checks. Reminder delivery is injected in tests to avoid external email.
- Updated tests/smoke.php to create a uniquely named _test database, migrate and seed it, create a temporary owner and store through the HTTP flow, create payment/invoice/PDF and screenshot fixtures, start a temporary PHP server, and clean up database/files afterward. It reads supplied SMOKE_* values and uses generated values when missing.
- Added ENODASTORE_DB_* environment overrides in Config so the isolated HTTP server connects to the temporary database.
- Fixed two directly exposed billing-path defects: the billing view overwrote View::render's status argument with its local $status variable, and BillingController lacked its Lang import.

Verification:
- Migration runner output: "Database migrations completed."
- Live DESCRIBE payments contains reject_reason and no reason column; migration 011 is recorded.
- tests/subscription_service_test.php: 42 assertions passed, including trial expiry, grace expiry, cancel/resume/expiry, scheduled downgrade product visibility without deletion, and once-only 7-day/1-day reminders.
- public/cron/daily.php CLI output against the configured database: {"grace_started":0,"expired":0,"downgrades_applied":0,"reminders_sent":0}.
- tests/smoke.php self-provisioned its _test database and passed installer, login, store creation, add-product page, payment/invoice fixture, CSRF cancel/resume, storefront home, invoice download, and manual plan payment.
- At the time of this earlier run, two smoke checks failed because /{store_slug}/cart and /{store_slug}/checkout both pointed to StorefrontController::home. Item 8 records the fix and successful rerun.
- PHP syntax checks passed for all changed PHP files and the existing daily cron entrypoint.

Correction to item 6 above:
- trial_ends_at and cancel_at_period_end are no longer schema-only; both are implemented and covered by the tests described in item 7.

8) Storefront routing, cart/checkout, customer order PDF, and cron token gate
Modification: Replaced storefront cart/checkout home-handler aliases with store-scoped customer flow handlers.
Date: 2026-11-05

What changed:
- Fixed the previous route wiring where GET /{store_slug}/cart and GET /{store_slug}/checkout both called StorefrontController::home.
- Added explicit store-scoped handlers/routes for /{slug}, /{slug}/products, /{slug}/category/{category_slug}, /{slug}/product/{product_slug}, /{slug}/cart, /{slug}/checkout (GET and POST), /{slug}/order/{order_number}, /{slug}/order/{order_number}/pdf, and /{slug}/page/{page_slug}. Cart add/update are CSRF-protected POST routes.
- Added real product-list, product-detail, cart, checkout, page, and order-success views. Cart data is separated by store in the session; database reads and order writes are constrained by store_id.
- Added OrderService with locked product re-reads, server-side prices, stock validation/decrement, transactional order and order_items insertion, and plan-gated coupons. Client-supplied prices are ignored. Failed stock checks roll back without creating an order.
- Added reserved-word checks for product slugs in the product editor and page slugs on route resolution.
- Centralized the reserved route/global slug list in App\Core\ReservedSlugs and reject reserved product slugs on lookup as well as save.
- Added an mPDF customer order PDF with private storage and a store-scoped PDF response route. The generated PDF includes customer/order/item totals and Tamil content; a Windows Latha font is used when available.
- Added English and Tamil storefront labels.
- Web /cron/daily.php now reads CRON_TOKEN from the process environment or .env, rejects empty/missing/mismatched values with a generic 403 using hash_equals, and remains callable from CLI.
- Added tests/checkout_flow_test.php, which provisions and drops an isolated migrated MySQL _test database and exercises the flow over a local HTTP server; added tests/cron_access_test.php.
- Added composer.lock for the existing mPDF and PHPMailer requirements. The local PHP installation has GD and ZIP DLLs but they were disabled in php.ini; Composer install succeeded with those DLLs enabled for the install command. PyMuPDF was installed locally only to rasterize the generated PDF for the requested image inspection.

Verification:
- tests/smoke.php: all 11 steps passed against its isolated temporary MySQL database, including the cart and checkout routes.
- tests/checkout_flow_test.php: 14 checks passed, covering catalog/category/page routes, reserved-slug rejection, MySQL order/order_items rows, server-side repricing, stock rejection including stale stock, Starter/Growth coupon gating, PDF validity/Tamil extraction, and WhatsApp URL construction.
- The checkout test rendered page 1 of the order PDF to storage/test-artifacts/checkout-order-page-91baa65e.png. Visual inspection confirmed a one-page portrait order summary with the store/order heading, customer name and phone, Tamil product row, quantity, unit price, and totals.
- tests/cron_access_test.php: 2 checks passed for empty and incorrect tokens returning only HTTP 403 / "Forbidden."
- tests/subscription_service_test.php: 42 assertions passed.
- tests/analytics_settings_test.php: 8 checks passed against a separately provisioned and dropped _test database.
- public/cron/daily.php CLI ran against an isolated migrated _test database and returned {"grace_started":0,"expired":0,"downgrades_applied":0,"reminders_sent":0}.
- PHP syntax checks passed on the final changed PHP file set.

Correction to item 7 above:
- The earlier cart and checkout failures were fixed in this item. They are no longer outstanding.

9) Fresh MySQL verification of the requested integration suites
Modification: Re-ran the subscription, analytics settings, smoke, checkout flow, and cron access tests against the local MySQL server.
Date: 2026-11-05

What changed:
- No application or test code changed in this verification pass.
- Started the existing local XAMPP MariaDB server for test execution. It reported MariaDB 10.11.16; PHP CLI reported 8.3.36.
- Stopped that MySQL process after the verification run.
- Ran analytics_settings_test.php only against a fresh, migrated, temporary MySQL database because that test deletes rows from its configured database tables. The temporary analytics database was dropped after the test.

Verification:
- tests/subscription_service_test.php: 42 assertions passed against the configured MySQL database. The test's lifecycle fixtures ran inside a transaction and were rolled back.
- tests/analytics_settings_test.php: all 8 checks passed against its isolated MySQL database.
- tests/smoke.php: all 11 HTTP checks passed against an isolated, self-provisioned MySQL test database.
- tests/checkout_flow_test.php: all 14 checks passed against an isolated MySQL test database; the PDF was generated, extracted, and rendered during the test.
- tests/cron_access_test.php: both HTTP token-gate checks passed.
- PHP syntax checks passed for all five PHP test files. No PHP source files were changed in this verification pass.
- Verified that the isolated analytics, smoke, and checkout test databases had been removed after their runs.

10) Manual payment review, Razorpay billing, and invoices
Modification: Completed and verified the owner payment submission, Super Admin review, Razorpay integration boundary, and private invoice flow.
Date: 2026-11-05

What changed:
- Added /superadmin payment queue, proof viewer, approve/reject actions, invoice download, role guards, CSRF checks, and audit outcomes. Manual submissions validate JPG/PNG/WebP content with finfo, enforce a 3 MiB limit, use random private filenames, and remain pending until review.
- Approval applies a subscription once, creates one invoice, and sends an owner email; repeat approval does not extend the subscription or duplicate the invoice. Rejection persists the reason and sends an owner email.
- Extracted Razorpay HTTP transport behind RazorpayClient. Orders use the selected MySQL plan quote, callbacks check the payment HMAC and captured amount, and signed payment.captured/payment.failed/order.paid webhooks are logged and replay-safe. Credentials use AES-256-GCM with APP_KEY; disabled methods are rejected server-side and one method must remain enabled.
- Added yearly ENO-YYYY invoice numbering, private owner/super-admin PDF downloads, seller/customer/plan/period/tax/total details, and locally bundled Noto Sans Tamil regular/bold faces. Invoice labels have English and Tamil entries. Static asset styling is now compiled locally; the Tailwind, Alpine, and web-font CDNs were removed from the shared page head.
- Localized Super Admin payment method and status labels in the pending queue using the existing English and Tamil billing dictionaries.
- Added tests/payments_test.php and docs/BILLING.md. Tax identifiers and tax rates are settings placeholders and must be replaced with verified business details before production invoicing.

Verification:
- Final validation rerun after the payment-queue localization change:
- tests/payments_test.php: 32 checks passed against a temporary migrated MySQL database. The test exercised real HTTP multipart submission and Super Admin approval/rejection; email delivery used a local mock SMTP server. Razorpay used a mock transport, including valid/invalid known HMACs, amount mismatch, replay, payment.failed/order.paid events, and disabled-method attempts.
- The final retained invoice page-1 render is storage/test-artifacts/invoice-page-a40afd3f.png. It is 715x1011, one page, with extractable English and Tamil labels, aligned totals, and no broken/missing Tamil glyphs; intermediate invoice diagnostics were removed.
- tests/subscription_service_test.php: 42 assertions passed.
- tests/analytics_settings_test.php: 8 checks passed against a fresh isolated database; it was dropped after the run.
- tests/smoke.php: all 11 HTTP checks passed against its isolated MySQL database.
- tests/checkout_flow_test.php: all 14 checks passed, including MySQL writes, pricing/stock enforcement, PDF extraction, and page-1 rasterization.
- tests/cron_access_test.php: both HTTP token checks passed.
- php -l passed for all 122 PHP files under app, config, public, tests, and cli; after the final view localization change, php -l was rerun on app/Views/superadmin/payments.php and passed.
- npm install completed with 0 reported vulnerabilities; npm run build:css produced public/assets/css/app.css. Browser inspection at 390x844 showed the login page had no horizontal overflow and loaded only local application CSS/JS and font assets.

Remaining limits:
- No real Razorpay credentials or network transaction were available; live order creation, hosted checkout, and webhook delivery were not exercised. docs/BILLING.md describes test-mode keys and tunnel setup.
- Razorpay Checkout requires its provider-hosted script at checkout. It is the only external script retained; other app runtime CDN dependencies were removed. A literal zero-external-script policy conflicts with the requested Razorpay Checkout integration.
- SMTP delivery was verified with mock SMTP, not a production mail provider. Tax identifiers remain configured placeholders until the operator supplies verified details.

11) Super Admin panel completion
Modification: Completed the database-backed Super Admin pages and owner support experience; added isolated MySQL/HTTP integration coverage and operator documentation.
Date: 2026-11-05

What changed:
- Added docs/SUPERADMIN.md with route coverage, setup guidance, security behavior, and test instructions.
- Completed live-data dashboard metrics/charts, store search/detail/actions, plan editing, support threads, announcements, settings, audit filters/exports, and health reporting.
- Added owner support pages and tenant-scoped ticket access, announcement dismissal checks, local Chart.js delivery, and daily-cron health audit records.
- Enforced Super Admin role checks and CSRF on mutating routes; maintenance mode prevents store-owner sign-in (including unverified owners) and dashboard access while keeping Super Admin access available.
- Added mailer injection for service tests so support mail-log behavior is exercised without contacting a real mail server. Corrected checkbox parsing so a forged non-enabled value cannot satisfy the at-least-one-payment-method guard.
- Fixed PHP built-in-server routing for existing static files and made the shared head tolerate theme arrays without a CSS URL.

Verification:
- tests/superadmin_test.php: 84 checks passed against an isolated migrated MySQL database, including live-data rendering for every Super Admin page, owner 403 checks, CSRF, tenant isolation, maintenance-mode sign-in, and local Chart.js HTTP delivery.
- tests/payments_test.php: 32 checks passed; subscription_service_test.php: 42 assertions passed; analytics_settings_test.php: 8 checks passed; smoke.php: all 11 steps passed; checkout_flow_test.php: 14 checks passed; cron_access_test.php: 2 checks passed. Each database integration suite used a temporary MySQL database.
- PHP syntax validation passed for all 128 PHP files under app, config, public, tests, and cli. Node syntax checks passed for the Super Admin script and local Chart.js.

Remaining limits:
- Support email behavior in the Super Admin integration suite uses an injected mock delivery callback; production SMTP delivery still requires operator-configured credentials and a real delivery check.
- No live Razorpay transaction or production email delivery was performed; see docs/BILLING.md.
- The separately requested storefront-theme screenshot/accessibility audit and themes_test.php/docs/themes/README.md remain unverified and outstanding.

12) Analytics and store-owner settings
Modification: Completed the analytics dashboard/report endpoints and store-scoped owner settings, account actions, notification hooks, and tenant-specific SEO routes.
Date: 2026-11-05

What changed:
- Added tenant-scoped analytics reports for 7/30/90 days, funnel stages, repeat/new customers, product/category and weekday/hour reporting, order/product CSV exports, five-minute report caching, and advanced_analytics plan gating.
- Visitor tracking stores daily HMAC identifiers derived from IP and user agent rather than raw values. Tracking resolves store context from the storefront session.
- Added store-scoped settings for WhatsApp, delivery, notifications, SEO, domain, and policies; account password/session/export/deletion workflows; owner mail notifications; order delivery fee enforcement; per-store sitemap and robots routes.
- Added docs/ANALYTICS_SETTINGS.md and extended isolated MySQL coverage in tests/analytics_settings_test.php and tests/checkout_flow_test.php.

Verification:
- Final full database-backed suite passed: subscription_service_test.php (42 assertions), analytics_settings_test.php (37 checks), payments_test.php (32 checks), checkout_flow_test.php (14 checks), smoke.php (11 HTTP steps), superadmin_test.php (84 checks), and cron_access_test.php (2 checks). Each integration test that creates a database dropped its temporary database.
- analytics_settings_test.php printed real seven-day JSON from its isolated MySQL data: 1 order, INR 200 revenue, 5 visits, 3 daily unique visitors, 20% conversion, product/category series, and a WhatsApp-click funnel count of 2.
- php -l passed for all 134 PHP files checked across the repository root, app, config, public, tests, and cli. JavaScript syntax checks and the local CSS build passed; the build noted outdated Browserslist data.
- Checkout integration verified the persisted delivery charge (INR 12.00) and total (INR 137.50). Notification mail delivery was exercised using mock mailers; no production SMTP send was attempted.

Remaining limits:
- Custom domains remain pending; DNS verification and SSL provisioning are not implemented. The referenced N5 requirements were not present in the repository.
- Live SMTP delivery was not verified. See docs/ANALYTICS_SETTINGS.md.

13) Landing, signup, onboarding, email, and legal pages status review
Modification: Verified the existing implementation and recorded remaining work for the requested marketing, onboarding, email, and legal experience.
Date: 2026-11-05

What exists (verified by source inspection):
- The public landing route queries active plans from the plans table and renders database-backed monthly pricing. The public navigation has a sticky header and English/Tamil language switch; the page has how-it-works content, WhatsApp explanation, FAQs, CTA, and legal/contact footer links.
- Signup validates user fields, creates a store-owner account, requires OTP verification, sends a welcome email, and routes verified users without a store to onboarding.
- Onboarding is a resumable four-step session flow. It checks reserved/available slugs, validates category and Indian WhatsApp number, offers available Starter themes, and creates a trial store and subscription in MySQL. The success page has copy-link and WhatsApp-share actions.
- Mailer writes to mail_log and can deliver through configured SMTP or PHP mail(). Existing templates cover welcome, subscription reminder, payment receipt/rejection, and password reset. New-order notifications are assembled by NotificationService and use the Mailer.
- Super Admin can edit Terms and Privacy content. Public Terms and Privacy read those settings; Refund currently uses static translated text.
- Stitch landing design exports exist under design/stitch_enodastore_ui_kit/stitch_enodastore_ui_kit. No public/media/ad.mp4 was present during inspection.

Pending (not implemented or tested in this review):
- Landing: implement a real gallery populated from active themes, add a WhatsApp-vs-store comparison, retain an editable testimonial placeholder without fabricated testimonials, add marketing SEO/Open Graph metadata, and use the specified Tamil hero copy in the Tamil translation. Confirm responsive performance and local-only runtime assets. Add video only if the requested file is supplied.
- Onboarding: extend the existing four steps to the requested five-step flow; include category-based theme preselection, theme selection, logo upload and E.164 WhatsApp input, optional first-product creation, and a live-store completion page with locally generated QR code and share controls. Add a persistent, dismissible/completable setup checklist to the owner dashboard.
- Email: provide bilingual English/Tamil HTML and plain-text templates for welcome, 3-day/1-day trial reminders, payment received/rejected, subscription expired, new-order notification, and password reset. Route all messages through Mailer and verify mail_log records and delivery behavior with mocks.
- Legal: make Refund editable in Super Admin, provide Indian SaaS Terms/Privacy/Refund starter templates clearly labelled for lawyer review, and render configured content safely.
- Tests: add an isolated-MySQL integration test covering signup/OTP as applicable, onboarding resume and store creation, and confirmation that landing pricing reflects plans-table rows. Run the requested tests and PHP lint after implementation; no such tests or lint were run as part of this status review.

Verification:
- This was a source-inspection/status-documentation update only. No application files were changed, no database-backed signup/onboarding flow was executed, and no new tests were run.
