# Plan Purchase: Success/Failed Animations + Bug Fixes — INSTALLED

## 1) Run first (phpMyAdmin) — if not already done
   sql/upgrade.sql

## 2) Files changed this round
   auth/lib/callback.php     (rewritten — now correctly checks real txnStatus,
                               redirects with a flag for success/failed/pending)
   auth/dashboard.php        (added — SweetAlert2 "Plan Activated Successfully"
                               popup when redirected back after a successful purchase)
   auth/subscription.php     (added — SweetAlert2 "Payment Failed" / "Payment
                               Processing" popup when redirected back after a
                               failed/pending purchase)
   payment/create_order.php  (fixed — plan-purchase orders no longer get blocked
                               by the buyer's own transaction quota)
   payment2/pay_now.php, payment4/pay_now.php, payment6/pay_now.php,
   payment2/payment_status.php  (fixed — status bug: these were writing
                               'FAILED' but the rest of your app - dashboard,
                               header widget, cron, check_order.php - all use
                               'FAILURE'. Fixed to use 'FAILURE' everywhere so
                               failed-order counts and the new animations
                               actually work.)

## How it works now
- Buy a plan → real payment happens → callback.php checks the ACTUAL transaction
  status (not just assumes success):
  - **Success** → plan is activated for real (correct duration/limits from
    Admin → Subscription Plans) → redirected to Dashboard → green "Plan
    Activated Successfully!" popup.
  - **Failed** → redirected back to the Subscription page → red "Payment
    Failed" popup, no plan is touched.
  - **Still pending** → redirected back to Subscription page → blue "Payment
    Processing" popup (rare, but handles slow bank confirmations gracefully
    instead of leaving the user guessing).
- Also fixed: a user whose trial/plan transaction quota had run out could
  previously never buy a new plan (the quota check was blocking their own
  plan-purchase order). That's now bypassed correctly for plan purchases only.

## Reminder from before (unchanged, still applies)
- payment4/payment6's own payment_status.php (BharatPe/FreeCharge) use
  different provider logic and weren't touched.
- The "let admin/user manually flip any transaction to Success/Failed" request
  was intentionally not built - explained separately in chat.

Backup your DB and files before uploading, as always.

## Round 3 fix — FamPay ("cxr..." orders) showing "Payment Processing" instead of "Failed"

Root cause: payment9 (FamPay) is the merchant you actually have connected, but I
had only patched payment2/payment4/payment6 earlier - payment9 was missed.
On top of that, its "Payment Failed" screen (manual cancel ✕ button, and the
5-minute countdown timer) was 100% client-side JavaScript - it never told the
server the payment had failed, so the order stayed PENDING in the database
forever. That's why callback.php (correctly reading the real DB status) showed
"Payment Processing" instead of "Payment Failed".

Fixed:
- payment9/pay_now.php - page-load expiry check now marks the order FAILURE
  in the DB (same fix as payment2/4/6) and shows the Order Expired screen.
- payment9/mark_failed.php (new) - a small endpoint that records a real
  FAILURE in the DB. Never overwrites an existing SUCCESS.
- payment9/pay_now.php - both the manual cancel button and the 5-minute
  countdown timeout now actually call this endpoint (via sendBeacon, so it
  fires reliably even during the redirect) before showing the Failed screen.

Now the FamPay order's real DB status matches exactly what the customer was
shown on screen, so the correct animation appears afterward.

## Round 4 — "Customize Page" feature (new)

Added a new sidebar link, **Customize Page**, under Merchant Setting (right where
you circled, next to Subscription).

Files:
- auth/customize_page.php (new) — lets each user set:
  - Business Display Name (shown on their checkout page header)
  - Logo Image URL (paste a link to a hosted image - no file upload, keeps
    things simple and avoids extra upload-security work)
  - Brand Color (color picker) — recolors the header/buttons/highlights
  - Live preview box shows exactly how it'll look before saving
- auth/header.php — added the sidebar link
- payment9/pay_now.php — the actual FamPay checkout page now pulls these
  three settings per-user and applies them (CSS --primary/--primary-dark
  variables, logo <img> src, and display name). Falls back to sensible
  defaults automatically if a user hasn't customized anything.
- sql/upgrade.sql — added checkout_logo_url, checkout_primary_color,
  checkout_business_name columns to `users` (run this - same file as before,
  just re-run it, IF NOT EXISTS makes it safe).

Important scope note: payment2/payment4/payment6 use a different, older
checkout template (no CSS variables, different structure) than payment9's -
so this customization currently only applies to the FamPay (payment9)
checkout page, which is the one you're actually using. If you want the same
logo/color customization on the other merchant types too, let me know and
I'll adapt it to their templates.

## Round 5 — Transactions "Failed" badge bug + Customize Page: file upload, QR/button toggles, theme fix

### Bug found: Transactions list never showed "Failed"
auth/transactions.php's status column only ever checked for `SUCCESS`; every
other status (including a real `FAILURE`) silently fell back to showing
"Pending". This was a pre-existing bug in the original code, unrelated to the
FamPay-specific fix from before. Fixed in both the per-user view and the
admin view - `FAILURE` now shows a red "Failed" badge.

If you still see "Pending" for an order you know failed, check that
payment9/mark_failed.php (from the last round) is actually uploaded - that's
what flips the real DB status to FAILURE in the first place; this round only
fixes how that status gets *displayed*.

### Customize Page - now much more complete
- **Logo**: choose EITHER a URL or upload a file directly (PNG/JPG/JPEG, max
  5MB). Uploaded files are validated (real image check, size, extension) and
  stored in /uploads/logos/, which has its own .htaccess blocking any script
  execution as a safety measure.
- **QR Code Visibility toggle** - turn the QR code on/off on the checkout page.
- **Google Pay / PhonePe / "Other UPI Apps" button toggles** - show or hide
  each payment method independently.
- Safety rule: you can't turn everything off - at least one payment method
  must stay visible, enforced both when you save and again as a hard
  fallback in the checkout page itself.
- **Theme fix**: the page was previously using hardcoded light-only inline
  colors (e.g. a fixed light-gray preview box), which looked inconsistent
  once your admin theme is in dark mode. Rewritten to use only the same
  card/form-control classes as the rest of your admin panel (same pattern as
  manage_subscription.php), so it now matches the rest of the dashboard
  automatically, light or dark.

Note: templates gallery / dark-mode-for-the-checkout-page / watermark toggle
(shown in the ZapUPI reference screenshots) were intentionally left out for
now - those would be cosmetic-only additions with no real backing
functionality on your current checkout template, and I'd rather build
functioning features than decorative ones that don't do anything. Happy to
add a proper multi-template system later if you want to invest in a second
or third checkout page design to switch between.

### New SQL columns (in sql/upgrade.sql, re-run it - safe/idempotent)
checkout_show_qr, checkout_show_googlepay, checkout_show_phonepe, checkout_show_other_upi

## Round 6 — Customize Page blank screen, "Valid for 1 (s)" text, fully automatic Pending→Failed

### Customize Page showing blank
Root cause: the page's database query explicitly selects the new
checkout_* columns by name. If sql/upgrade.sql (with ALL columns through
Round 5 - checkout_show_qr etc.) hasn't been run on your live DB yet, that
query fails, and calling ->fetch_assoc() on a failed query is a fatal PHP
error - which shows as a totally blank page when display_errors is off.

**Fix required on your end: re-run the latest sql/upgrade.sql in phpMyAdmin
- this is almost certainly why the page is blank.** I've also made the page
itself defensive now: if those columns are still missing, it'll show a clear
on-screen message telling you to run the SQL, instead of going blank.

### "Valid for 1 (s)" text
Fixed the pluralization logic - it now properly shows "Valid for 1 Day",
"Valid for 1 Month", "Valid for 3 Months", etc., based on the plan's actual
period_type and duration number.

### Pending orders that are truly stale now auto-flip to Failed - no page revisit needed
Previously, an order only got marked FAILURE when the *customer's browser*
reported it (via cancel button, countdown timeout, or reopening the same
expired link). If a customer opened a payment link and then just closed the
tab without doing anything, that order could stay "Pending" forever, since
nothing ever told the server.

Now, both auth/transactions.php and auth/dashboard.php run a small
self-healing check every time you load them: any order still marked PENDING
whose payment link is older than 5 minutes gets automatically flipped to
FAILURE right there, before you even see the list. So the behavior is now
exactly as you described:
- Freshly generated link → shows Pending for its first 5 minutes.
- Customer cancels or it visibly times out before that → Failed immediately
  (previous round's fix).
- Nobody does anything and 5 minutes pass → Failed automatically the next
  time you open Transactions or the Dashboard, even without anyone touching
  the payment page again.

## Round 7 — Full color customization + 10 templates on Customize Page, and a real Test/Sandbox Mode across ALL merchant types

### Customize Page now matches the Dashboard's dark theme, plus full color control
- Rebuilt with the exact same dark "glassmorphism" theme/classes as auth/dashboard.php
  (dashboard-card, card-header, card-body, container-fluid) so it now looks
  consistent with the rest of your admin panel.
- **10 ready-made color templates** (Emerald, Royal Blue, Sunset Orange, Midnight Purple,
  Rose Gold, Ocean Teal, Crimson Red, Forest Green, Graphite Dark, Luxury Gold) -
  picking one fills in all 5 color fields at once, which you can then fine-tune.
- **5 independent color pickers**: Primary, Secondary/Accent, Success, Page
  Background, and Text - covering essentially every visible part of the FamPay
  checkout page, with a live preview box.
- Logo upload (file or URL), business name, and the QR/pay-button toggles from
  the previous round are all still here.

### Real Test/Sandbox Mode - now on ALL 8 merchant types (payment2 through payment9)
- New **API Mode** toggle on the API Details page: **Live** or **Test**.
- In Test Mode, every new payment link you generate is flagged internally as a
  test order (never touches real money, real UPI, or your real revenue stats).
- The customer-facing payment page shows a clear **"TEST MODE" banner** plus a
  floating **Test Action** button (bottom-right) - exactly like your ZapUPI
  reference screenshots. Tapping it opens Success / Failed / Timeout options.
- Picking one immediately marks that test order with the chosen outcome AND
  calls your real webhook URL with that status (with an extra `test_mode=true`
  field), so you can verify your own site/app's integration end-to-end without
  needing a real transaction.
- **Safety**: the simulate-endpoint (includes/test_action_submit.php) refuses
  to act on any order that isn't explicitly flagged as a test order at creation
  time - it's structurally impossible to use this to fake a real/live payment.
- Test orders are automatically excluded from your Dashboard's real revenue/
  success-rate numbers, and get a small "TEST" badge in the Transactions list
  so they're never confused with real payments.
- This is wired into ALL 8 provider folders (payment2, payment3, payment4,
  payment5, payment6, payment7, payment8, payment9) via one shared, reusable
  widget - so it works no matter which merchant type generated the link.

### New files
- includes/test_action_widget.php - the banner + floating button + modal
- includes/test_action_submit.php - the safety-gated simulate endpoint

### New SQL columns (sql/upgrade.sql, or sql/upgrade_new_only2.sql +
sql/upgrade_new_only3.sql if you want to run only what's new since last time)
- users: checkout_secondary_color, checkout_success_color, checkout_bg_color,
  checkout_text_color, checkout_template, api_mode
- orders: is_test

## Round 8 — Full checkout customization unified across payment2, payment3, payment9

### What's done
- **payment9 (FamPay)**: fixed a gap where 4 of the 5 Customize Page colors
  (Secondary, Success, Background, Text) were being saved but never actually
  applied anywhere - only Primary Color worked. All 5 now wire into the real
  CSS variables on the checkout page.
- **payment2 and payment3**: turned out to already share the exact same modern
  template design as payment9 (same fonts, same CSS variable system) - they
  were just missing the Customize Page wiring entirely. Added the full set:
  business name, logo, all 5 colors, and the QR/Google Pay/PhonePe/Other-UPI
  visibility toggles - identical behavior to payment9 now.

### Important - found while doing this, please read
- **payment2 and payment3 turned out to be near-duplicate files** - both
  fetch the UPI ID from the `paytm_tokens` table. Since payment2 is mapped to
  "PhonePe" in create_order.php, this means PhonePe merchants have likely
  never actually worked correctly (it's pulling Paytm's UPI ID/table instead
  of a PhonePe one) - this predates my changes, I didn't introduce it, but I
  also haven't fixed it since I don't want to guess at what PhonePe-specific
  logic should look like without your input.
- **payment4, payment5, payment6, payment7, payment8 were NOT touched this
  round.** payment4 and payment6 use a working but different/simpler template
  (no Customize Page wiring, no CSS variable system). payment5, payment7, and
  payment8 use an even older structure where PHP logic is interleaved with
  HTML rather than separated - I want to map those out carefully before
  editing them, since a rushed change could break real payment collection for
  whichever of those merchant types you actually have connected.

### What I'd suggest next
Tell me which of these merchant types (PhonePe/payment2, Bharatpe/payment4,
Googlepay/payment5, FreeCharge/payment6, SBI/payment7, Mobikwik/payment8) you
actually use or plan to use, and I'll prioritize bringing those up to the same
modern template + full Customize Page control next, carefully, one at a time.

## Round 9 — Your new payment page design applied to payment9 (FamPay) and payment3 (Paytm)

You sent over a fully custom-built payment page design (ex.php) - a much more
polished, modern checkout UI. I integrated it for real, wiring it to your
actual backend instead of the example data it originally contained.

### What changed
- **payment9/pay_now.php** and **payment3/pay_now.php** now use your new
  design in full - header, hero amount card, QR card (desktop + mobile
  layouts), Pay-via-App buttons, loading/verification/result sheets, and the
  Test Mode floating button + dialog (which your design already included!).
- All of it is wired to REAL data: real order amount, real UPI ID, real
  merchant name/logo/colors from Customize Page, real countdown timer, real
  payment status polling, real cancel handling, and real Test Mode simulation
  through the endpoints built in earlier rounds.
- Customize Page's 5 colors now map onto your new design's 8-color system
  (color1/2 = Primary + its dark shade, color3 = Secondary, color4 = your
  Background color, color5/6 = auto-generated dark header shades, color7 =
  fixed white for card readability, color8 = Primary again, which is what
  actually drives your buttons/links in this template).
- Found and fixed a real bug on payment3 in the process: it was missing the
  "mark expired links as Failed" fix from a much earlier round - added.

### What I could NOT wire up (and why)
Your design includes a "VPA Collect Request" input and a "Enter UTR to
verify" input. Both call a **collect-request / bank-verification API** that
your FamPay and Paytm integrations don't have (they only know how to check
status via Gmail-notification parsing, not initiate a collect request or
verify an arbitrary UTR against the bank). Wiring these to fake success would
be dishonest, so **I've kept both sections hidden** rather than pretending
they work. If you get real API access for either of those (from FamPay/Paytm
or a PSP), tell me and I'll wire them in properly.

### Important dependency to be aware of
Your new design loads `https://panel.mgamer.online/assets/brite-dialog.css`
and `.js` for all its popup dialogs (cancel confirmation, errors, etc.) -
this is a *different* domain/project of yours than this FamPay gateway
(amer.mgamer.online). **If that panel.mgamer.online site or those specific
asset files ever go down or become private, every dialog on this checkout
page will silently stop working.** Worth keeping in mind, or let me know if
you'd rather I copy those two files onto amer.mgamer.online directly so this
gateway doesn't depend on another one of your projects staying online.

### Not touched this round
payment2, payment4, payment5, payment6, payment7, payment8 still use their
previous designs. Let me know which of those you actually need, and I'll
bring them up to this same new design next.

## Round 10 — Fixed the "cut" (cancel) button, and polished Customize Page's UI

### Cancel button ("X" in the header) now works reliably
Root cause: your new design's cancel confirmation and its "please wait"
loader both depended on an external library (`BriteDialog`, loaded from
`panel.mgamer.online/assets/brite-dialog.js`). If that script is blocked,
slow, or that domain has any issue, `BriteDialog` never gets defined -
clicking cancel would throw a silent JS error and do nothing.

Fixed in both payment9 and payment3: the cancel button now uses the
browser's built-in `confirm()` dialog instead - zero external dependency,
always works. After confirming, it shows the same "Cancelling Payment..."
processing state immediately (no more dead pause), then plays the exact same
red "Payment Failed" result animation as before. The genuinely broken real
payment-timeout handlers (when the 5-minute clock hits zero on its own) had
the same issue and got the same fix, plus now they actually mark the order
FAILURE in the database (previously that specific path didn't, even though
the manual cancel/timeout-test paths did).

### Customize Page now has the polished icon-header + toggle-switch look
Every section now has a colored icon box + title + short description (the
"Custom Keys / Building your own payment page UI" style you'd shown me
earlier), and the 4 on/off settings (QR Code, Google Pay, PhonePe, Other UPI)
are now proper rounded slider toggles instead of plain checkboxes - matching
a modern settings page rather than a basic form.

## Round 11 — Styled cancel dialog restored, Register.php replaced with your new design

### Cancel confirmation dialog - now reliable AND good-looking
Last round's fix made the cancel button reliable but ugly (it fell back to
the browser's plain native "Cancel Payment? Cancel/OK" popup). Built a
proper custom-styled dialog instead - matching your reference screenshot
(orange warning circle, "Cancel Payment?" title, description, "No" / "Yes,
Cancel" buttons) - fully self-contained in this file's own CSS, so it can
never break due to an external dependency again. Applied to both payment9
and payment3.

### Register.php replaced with your new design
Used the file you uploaded as-is, with one addition: your version was
missing the "auto-activate Trial plan on signup" call from Round 1 - added
it back in (include for includes/subscription_lib.php, then
assignTrialPlan() right after a successful signup) so new users still get
their trial automatically with your new design.

If uploading this still shows the old design on your server, it's very
likely a caching issue rather than a code issue - check:
- Browser cache (hard refresh / incognito)
- Any CDN/Cloudflare cache in front of your domain
- PHP OPcache on your host, if `opcache.validate_timestamps` is off, the
  server can keep running the old compiled version until it's restarted
- That you're editing the actual root Register.php your server serves, not
  a copy in another folder

### Bigger item you raised - sitewide UI consistency
You mentioned that different pages across the panel still look inconsistent
with each other. Dashboard and Customize Page now share the same dark theme,
but Subscription, Transactions, Connect Merchant, API Details, and Manage
Profile still use the older light theme from before - that's very likely
what's causing the "different UI everywhere" feeling. Unifying the entire
admin panel under one consistent theme is a bigger job across many files,
and I want to do it deliberately rather than rush it and risk making some
page unreadable. Confirm you want that and I'll take it on as the next
focused piece of work.

## Round 12 — Two real bugs fixed: washed-out cancel dialog, and duplicate QR codes on desktop

### Cancel dialog was washed out / icon missing
My Round 11 CSS for the cancel dialog used variable names (--ink, --muted,
--amber, --surface, --primary as a static value) that belong to the OLD
template - this new template uses a completely different variable system
(--zap-c1..c8, --t3-on-c7, --t3-hint-color, --primary set dynamically via
JS). Since those old variables don't exist here, the browser couldn't
resolve text/icon colors properly, causing the washed-out look you saw.
Fixed to use the correct variable names (with safe fallback colors in case
a color hasn't loaded yet) - the dialog now renders properly with a visible
orange icon and readable text.

### Two QR codes showing on desktop/laptop
Found a real bug in the template's responsive CSS: the rule meant to hide
the "mobile" QR card on desktop screens was targeting an element ID
(`#t3-mobile-qr-wrap`) that doesn't actually exist anywhere in the page -
so that rule silently did nothing, and both the mobile-designed QR card and
the desktop-designed QR card ended up showing side-by-side on wide screens.
Fixed the CSS to target the correct element ID. Now only one QR code shows,
matching whichever layout (mobile or desktop) fits the screen.

Both fixes applied to payment9 and payment3.

### Still pending - sitewide theme consistency
Haven't forgotten the bigger "make every page match" request - let me know
you want to proceed and I'll take that on as focused, dedicated work next
(it touches many files, so I want to do it carefully rather than rush it in
alongside bug fixes).

## Round 13 — Sitewide UI consistency (the dark theme + toggle switches now apply everywhere)

Moved the dark "glassmorphic" theme (previously only on Dashboard and
Customize Page) into `auth/header.php`, which every admin page already
includes. This means:

- Every page (Subscription, Transactions, Connect Merchant, API Details,
  Manage Profile, etc.) now automatically gets the same dark background,
  the same card look, the same text colors - without editing each page's
  HTML individually. The plain Bootstrap `.card`/`.card-header`/`.card-body`
  classes those pages already use now render with the same dark glass look
  as Dashboard and Customize Page.
- Every checkbox styled via `.form-check-input` (the sliders/toggles) now
  has the same rounded, colored-when-on look sitewide, not just on
  Customize Page.
- Cleaned up a couple of small white patches (`bg-white` footers) that would
  have looked out of place against the new dark cards.

### Please check and report back
I can't visually render your live pages, so please click through Subscription,
Transactions, Connect Merchant, API Details, Manage Profile, Change Password,
and Simple Code after uploading, and let me know if any specific page or
element still looks off (e.g. hard-to-read text, a stray white box, a badge
that blends in) - I'll target-fix whatever you point out rather than guessing
further blind.

### Note
I made an editing mistake partway through this round (a misplaced CSS insert)
and caught + fixed it before finalizing - just flagging that header.php was
verified clean and lints correctly in the final version you're getting.

## Round 14 — URGENT FIX: reverted a bad global CSS change from Round 13

I made a mistake in Round 13. The rule that forced every `.card` on every
page to look dark also forced its TEXT to a light color - but several pages
(Connect Merchant, Transactions, Create Payment Link, and others using plain
Bootstrap cards) have their own separate styles that kept the card
BACKGROUND white. The result was white text on a white background -
looking like the whole page had gone blank, exactly what you saw in your
screenshots.

**Reverted that risky global override entirely.** Those pages are back to
their original white-card-with-dark-text look (just with the new dark page
backdrop behind them, which was already working fine). Kept only the two
genuinely low-risk pieces from Round 13:
- The toggle-switch look for checkboxes (just recolors the control itself,
  can't cause invisible-text problems)
- A one-line targeted fix for My Profile's heading specifically, which had
  its own separate bug (a hardcoded near-black color meant for a light
  background, now fixed to white just for that element)

### Lesson learned / how I'll approach this going forward
A single blanket CSS rule aimed at "every page" is risky because different
pages here were built at different times with different assumptions, and I
can't see how they render without you testing. Properly making every page
match Dashboard's look needs to be done **one page at a time** - checking
each page's existing styles first, then converting its specific markup to
the dark card style deliberately (the way I did for Customize Page
originally) - not a single global rule that assumes every page is the same
underneath.

If you still want full sitewide consistency, I'm glad to keep going, but
page by page with your confirmation after each one, rather than another big
blind pass.

**Please re-check My Profile, Connect Merchant, Transactions, and Create
Payment Link now** and confirm they're readable again.

## Round 15 — Fixed missing hamburger icon, made dark mode fully black, added a Light/Dark toggle button

### Missing hamburger (☰) menu button
Root cause: the sidebar toggle icon's text color was already set to a
light/white color everywhere (that was fine), but the top toolbar's own
BACKGROUND wasn't forced dark on every page - so on pages that didn't
already have a dark background, the white hamburger icon was invisible
(white-on-white), not actually gone. Fixed by forcing the top toolbar bar
itself to dark everywhere, so the icon (and notification bell, and profile
name) are always visible regardless of the page.

### Full black theme, done more carefully this time
Last round's card-darkening attempt broke Transactions and a couple of
other pages because I didn't account for the DataTables plugin (the
"Show 10 entries / Search" table library used on Transactions, Merchant
Connect, etc.) which draws its own separate light-colored table and
pagination markup outside of normal `.card` content. This time, every
`.card`, every table cell, every DataTables control (search box, page size
dropdown, pagination buttons) has its own explicit dark styling and text
color, and badges are excluded from the blanket text-color rule so their
own colored backgrounds stay readable.

### New: Light/Dark theme toggle button
Added a small sun/moon toggle button next to the profile menu, on every
page. Tapping it switches between:
- **Dark** (default) - the full black theme described above
- **Light** - a plain white-background, black-text fallback theme

The choice is remembered (saved in the browser) and applies instantly with
no flash of the wrong theme on page load. This exists specifically as a
safety net: if any particular page ever looks wrong in dark mode again, you
(or your users) have a one-tap way to switch to the plain, simple light
theme instead of being stuck looking at something broken.

### Please test thoroughly this time
Given the last two rounds each surfaced a real bug I couldn't see without
your screenshots, please click through every single page in the sidebar
after uploading - Dashboard, Connect Merchant, Payment Link, Transactions,
Subscription, Customize Page, Manage Profile, Change Password, API Details,
Simple Code, Website Manage, SMTP/WhatsApp, Manage Subscription, Create New
User, API User List - and try the Light/Dark toggle on a couple of them too.
Tell me exactly which page + what looks wrong if anything does, and I'll fix
that specific thing rather than another broad pass.

## Round 16 — Fixed the blank toggle button icon

The new Light/Dark toggle button was showing as an empty circle - the icon
class I used (`fa fa-sun-o`) doesn't actually exist in the icon font this
project uses (it only has the older Font Awesome 4 set, which never
included a sun icon). Replaced it with a proper inline SVG sun/moon icon
pair that doesn't depend on any icon font at all, so it always renders.

### Register / Login / Landing page consistency
Checked all three - Register.php, auth/index.php (login), and index.php
(public landing page) - and they already share the exact same dark theme,
same purple/pink gradient colors, and same glassmorphic card style as the
admin panel. Nothing needed changing there; they were already consistent.

## Round 17 — Fixed remaining white background leaking around cards

Found the exact cause of the white strip still visible around cards on
some pages (Connect Merchant, etc.): those pages nest several extra wrapper
divs inside `.card` (`.ibox`, `.ibox-body`, `.card-body`, `.main-panel`,
`.content`, `.container-fluid`) that carry their own white background from
the base theme. My earlier fix only darkened `.card` itself, not these
inner wrappers, so a white "frame" was showing through around the edges.
Forced all of them transparent now, so the dark card background shows
through cleanly everywhere with no white edges.

## Round 18 — Found and fixed the REAL hamburger menu bug

Now I understand exactly what you were describing. The hamburger button
that opens the slide-out menu wasn't just hard-to-see - it genuinely didn't
exist on any page except Dashboard. The JavaScript that actually CREATES
that button was written only inside dashboard.php, not in header.php (the
file every page shares). So Dashboard got its working hamburger + slide-out
menu, and every other page (Connect Merchant, Transactions, API Details,
etc.) never had it at all.

Moved that script (and a related one that force-hides a stuck loading
overlay) into header.php, and removed the now-duplicate copies from
dashboard.php so it doesn't run twice there. Now every single page gets:
- The hamburger button in the top bar
- Tapping it slides out the same sidebar menu Dashboard has, with all the
  navigation links (Connect Merchant, Payment Link, Transactions,
  Subscription, Customize Page, etc.)
- Tapping the backdrop or another link closes it again

This was a real, concrete bug - thank you for describing exactly what was
missing on Dashboard vs other pages, that's what let me find it.

## Round 19 — Two more real bugs found and fixed

### Hamburger icon was invisible (not missing) - wrong icon syntax
Confirmed the drawer itself works now (you got it open in your screenshot!)
but the button icon showed blank in its closed state. Found it: the icon
class I used (`fas fa-bars`) is Font Awesome 5/6 syntax, but this admin
panel bundles the older Font Awesome 4, which uses plain `fa fa-bars` (no
"fas" style prefix) - so the icon silently rendered as nothing. Fixed to
use the correct FA4 class. The hamburger icon should now actually be
visible in the top bar on every page, not just functional-but-invisible.

### Sidebar always highlighted "Dashboard" no matter what page you were on
Found a hardcoded `class="active"` on the Dashboard link in the sidebar
markup - it was never dynamic, so Dashboard always showed the glow/highlight
regardless of which page you'd actually navigated to. Removed the hardcoded
class and added a small script that checks the current page URL against
each sidebar link and highlights whichever one actually matches - so now
the "glow" correctly follows you to whatever page you're on (Connect
Merchant, Transactions, Subscription, etc.), matching what Dashboard looked
like before.

## Round 20 — Found the REAL reason the hamburger only worked on Dashboard

Your active-highlight fix worked (Connect Merchant correctly glows now
instead of Dashboard always glowing) - thank you for confirming that with
screenshots.

For the hamburger: I noticed Dashboard's closed header showed BOTH the
"NEOPAY" brand text AND the "AC" mini-logo stacked together, while Connect
Merchant only showed "AC" alone - a sign that these two pages were rendering
the sidebar/header in different layout MODES. There's an old, unused
per-account "sidebar layout" preference stored in the database from the
original admin theme (`$userdata['sidebar_layout']`) that toggles a
`sidebar-mini` collapsed mode - and my previous script only tried inserting
the hamburger button into the toolbar element, whose structure/visibility
apparently shifts under that collapsed mode on some pages, silently failing
the insertion there while working fine on Dashboard.

Fixed properly this time: the button no longer depends on the toolbar's
structure at all. It's inserted directly next to the "AC"/brand logo
(`.page-brand`), which is present and consistent on every single page
regardless of any layout mode, and forces the leftover `sidebar-mini` class
off immediately. This should now be genuinely independent of whatever
state each individual page happens to load in.

## Round 21 — Fixed: other users' FamPay payments not verifying (yours worked fine)

Found it in auth/fampay_mail_check.php - the code that reads a merchant's
Gmail inbox to confirm a FamPay payment arrived.

**Root cause:** it required the email body to contain the *exact* phrase
"successfully received". That matched your own test emails, but other
users' FamPay notification emails apparently word this slightly
differently (e.g. "payment received", "you have received") - so for them,
every email got silently skipped, and the payment stayed "unverified"
forever even though the money genuinely arrived.

Fixed three things together:
1. Now accepts several common phrasings ("successfully received", "you have
   received", "payment received", "money received", "received a payment",
   "received payment") instead of one exact string.
2. Broadened the reference-number (UTR) pattern to also catch "RRN",
   "Reference No/Number", and a couple of spacing variants - some accounts'
   emails may label this differently too.
3. Added a fallback: if the amount clearly matches and it's genuinely a
   "received" email, but no reference number could be parsed at all, it now
   still confirms the payment (using a generated placeholder reference)
   instead of blocking forever just because that one detail didn't parse.

This should fix verification for any user whose FamPay account sends
slightly different wording than yours, without weakening the actual
amount-matching check that keeps it safe from false positives.
