75 Commits

Author SHA1 Message Date
myron c6a382a378 Add copyright notice 2026-07-27 04:55:23 +00:00
myron f84a754370 Fix critical unauthenticated order tampering/IDOR in api/orders.php and reused named-param bug in admin order search 2026-07-06 07:51:53 +00:00
Myron Blair 3c8e4d1dbc Add in-admin refund processing via Square RefundPayment API
admin/order.php previously had no real refund action - the "refunded"
status option was purely cosmetic, only setting order_status without
touching payment_status or calling any payment API. Adds a Refund card
(full or partial amount, optional reason) that calls the real Square
Refunds API for orders paid via Square, updates payment_status
(refunded/partially_refunded) and order_status, logs the refund ID as an
order note, and restores any wallet_amount_used back to the customer
wallet on a full refund (mirroring how it was deducted on payment
success in markSquarePaymentResult). Also fixed the Payment sidebar card
to display square_payment_id (it only ever showed the old stripe_payment_intent
field, even for Square orders).

Tested in sandbox: full refund (wallet restore verified), and confirmed
Squares own over-refund rejection surfaces cleanly as a flash error
rather than a crash.
2026-07-05 15:30:15 +00:00
Myron Blair bdd0bd6afa Require admin auth on api/search-customers.php
Was callable anonymously, leaking customer email/phone/wallet balance/reward
points to anyone who could guess a search term. Gated behind AdminAuth,
matching the pattern used elsewhere (401 JSON response, not a redirect,
since this is an API endpoint called via fetch from admin/pos.php).
2026-07-05 15:23:33 +00:00
Myron Blair 9771d53b19 Show wallet/gift card discount line on order confirmation page
Total already reflected the discount correctly, but subtotal + shipping
did not reconcile with it visually since the discount was never shown as
its own line item - matches the same row already present on checkout.php.
2026-07-05 15:19:47 +00:00
myron e02436618c Merge square-migration: Stripe to Square payment processor migration, checkout wallet/gift-card redemption, and related bug fixes 2026-07-05 15:01:30 +00:00
Myron Blair cfbae6e945 Fix duplicate PDO named parameter in api/search-customers.php
Same bug class as the awardPoints() fix - :q was reused three times in one
WHERE clause, which fails under real (non-emulated) prepared statements.
Split into distinct :q1/:q2/:q3 placeholders each bound to the same value.

(The other flagged file, admin/import-export.php, turned out to be a false
positive from the earlier scan - the duplicate ":checked"/"::before" matches
were CSS pseudo-selectors inside a <style> block, not SQL placeholders.)
2026-07-05 15:01:01 +00:00
Myron Blair 30daef5f74 Wire up wallet balance and gift cards at checkout; fix Square gift card option; fix account/rewards.php CSS; fix real PDO/schema bugs found along the way
Feature: checkout.php now lets logged-in customers apply existing wallet
balance or redeem a gift card code (which tops up wallet first, then
applies) toward their order total. Deduction is deferred to payment
success (markSquarePaymentResult in includes/square.php), never at order
creation, so an abandoned checkout never loses real wallet money - mirrors
how loyalty points already work here, unlike stock which is decremented
eagerly. payment.php gains a second Square Gift Card tab (payments.giftCard()
SDK method) alongside the card form, both hitting the same
create-square-payment.php endpoint since Square treats both source types
identically.

New api/apply-wallet-credit.php validates/quotes an amount without writing
anything - actual spend happens only via markSquarePaymentResult(). The
gift-card-to-wallet transaction logic was extracted out of
api/redeem-gift-card.php into a shared loyalty()->redeemGiftCardToWallet()
so the Wallet page and checkout both call the same code.

Also fixed three unrelated pre-existing bugs surfaced while testing this:
- loyalty.php awardPoints() reused the same named PDO parameter (:points)
  twice in one UPDATE - fails under real prepared statements, meaning
  loyalty points (and the email sent right after them) were silently never
  awarded for any order tied to a logged-in customer.
- redeemGiftCardToWallet (formerly inline in redeem-gift-card.php) referenced
  a gift_cards.updated_at column that does not exist in the schema, and used
  invalid enum values (gift_card_transactions.type=redeem,
  wallet_transactions.type=gift_card) that do not match the actual enum
  definitions - gift card redemption has likely never worked at all.
- account/rewards.php was missing the  line that loads
  account.css, unlike every other account/*.php page, so its sidebar/layout
  rendered unstyled.
2026-07-05 14:53:47 +00:00
Myron Blair 11edf3394f Add Square payment processor, gated behind PAYMENT_PROCESSOR flag (default: stripe)
Consolidates payment processing onto the same Square account already used by
tomtomgames.com and parkerslingshotrentals.com. Collapses the two prior parallel
Stripe flows (hosted Checkout + embedded Elements) into a single Square Web
Payments SDK flow, since Payments API is synchronous and removes the original
reason for two paths.

- includes/square.php: squareApi() cURL helper (mirrors the pattern already
  used on parkerslingshotrentals.com), markSquarePaymentResult() as the single
  source of truth for order completion shared by the sync response, webhook,
  and reconciliation poll - fixes a pre-existing bug where loyalty points were
  only ever awarded from the polling endpoint, never from the webhook.
- api/create-square-payment.php replaces api/create-payment-intent.php;
  api/create-checkout-session.php deleted (no Square equivalent - single flow).
- api/webhook.php rewritten for Squares signature scheme and event types.
- api/payment-status.php repurposed to reconciliation-only fallback.
- payment.php branches on PAYMENT_PROCESSOR so Stripe and Square code coexist
  deployed while dormant - flipping one config constant is the cutover/rollback.
- admin/payments.php: added a Square settings card alongside the existing
  (now legacy-labeled) Stripe card.
- db/schema.sql + live DB: added square_payment_id/square_order_id columns,
  stripe_* columns kept for historical orders.

Not yet cut over - PAYMENT_PROCESSOR still defaults to stripe in
config-secrets.php (outside this repo). Sandbox testing still needed before
flipping to square/production.
2026-07-05 05:27:19 +00:00
Myron Blair a0b8bf2a09 security: remove stray !install!! deploy folder, block .py files, move secrets out of webroot
- Delete !install!!/ (schema.sql, migrations, fix_tjj.py) - unreferenced leftover
  deploy artifacts, fix_tjj.py was publicly downloadable (200) since the folder
  name did not match the existing /install/ block pattern
- .htaccess: add .py to blocked extensions, explicitly block the !install!!/ path
- config/config.php: replace with a require shim pointing to config-secrets.php
  outside the web root (Stripe/CyberMail keys no longer sit in a web-servable file)
2026-07-05 05:18:50 +00:00
myron a4ebf26a48 Harden .git/config/includes/db block: RedirectMatch not honored, use mod_rewrite REQUEST_URI matching 2026-07-04 15:57:18 -05:00
myron 186ac0cb6d security: fix unauthenticated file upload/RCE risk, TLS bypass, XSS, and broken gift-card/review columns
- admin/api/upload-splash.php had NO admin-auth check (included the public
  customer header, not admin/includes/header.php) and both upload endpoints
  trusted the client-supplied MIME type and filename extension, so an
  attacker could name a file "shell.php", spoof Content-Type: image/png,
  and get PHP written into a web-reachable uploads/ directory. Added
  AdminAuth check and centralized real-content validation (getimagesize +
  server-side extension mapping) in a new handleImageUpload() helper used
  by both admin/upload-image.php and admin/api/upload-splash.php.
- Removed CURLOPT_SSL_VERIFYPEER => false from the CyberMail email calls
  in includes/email.php and includes/functions.php (MITM risk on the API
  key). Rewrote functions.php's sendEmail() as a thin wrapper around
  Email::send() so there's one implementation instead of two that could
  drift (this is what had the second copy of the TLS bypass).
- Escaped customer-controlled fields (customer_name, tracking info, reset
  URL) before interpolating into outbound HTML emails — name is free text
  from registration/checkout with no length/char restriction, so it was
  stored-HTML-injectable into every transactional email.
- Fixed api/redeem-gift-card.php referencing a nonexistent `balance` column
  on gift_cards (actual column is current_balance) — gift card redemption
  was completely broken, always returning "no remaining balance".
- Fixed api/submit-review.php inserting into nonexistent `content`/`status`
  columns on reviews (actual columns are `comment`/`is_approved`) — review
  submission was crashing on every request.
- Hardened .htaccess: block /db/*, /.git/*, and *.sql. Live site currently
  serves db/schema.sql and the full .git directory (including .git/config,
  which contains a GitHub PAT with push access) over HTTP — the existing
  config/includes RedirectMatch rules are also not being enforced live,
  see report for details; this needs a server-level fix too.
- Cleaned up README's leftover install instructions pointing at a deleted
  create-admin.php with a documented default password.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 14:22:50 -05:00
myron 06260ed192 fix: move loadCustomerEmails to module scope, wire into switchCTab, add emails to tab loop; drop redundant ?: [] in customer-emails.php 2026-06-23 16:35:11 +00:00
myron 145397ab81 sync: updated admin, assets, new images, composer config, customer email API 2026-06-21 03:46:45 +00:00
myron 3d9b648f74 Rewrite returns policy to match Google Merchant Center requirements
30-day window, defective + non-defective returns, exchanges accepted,
free prepaid return label, no restocking fees, 5-day refund processing.
US only. Removes old three-tier responsibility model.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 01:02:30 +00:00
myron 67caf9ad3b Use product name as-is for merchant feed title
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 00:56:07 +00:00
myron 69f09a257c Fix doubled category suffix in merchant feed titles
Product names already include 'Whole Bean Coffee' / 'Ground Coffee' so
appending the category label was doubling the suffix. Now only appends
if the product name doesn't already contain 'coffee'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 00:52:46 +00:00
myron a2bc87e947 Update admin nav logo to new PNG design
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 20:03:24 +00:00
myron afc8750b67 Increase nav logo height to 70px for readability
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 19:54:25 +00:00
myron cf1f090e25 Crop website logo to content — remove surrounding whitespace
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 19:48:09 +00:00
myron 712ec8c584 Update site logo to new horizontal design
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 19:46:25 +00:00
myron 1112fa2ab6 Revert site logo to original — new logo kept for GMC only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 19:35:10 +00:00
myron 60fc302443 Add cache-busting version string to logo img tags
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 19:31:07 +00:00
myron 530bb8e4ce Replace logo-icon.png with new logo (was broken HTML file)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 19:29:31 +00:00
myron 51ca171060 Replace logo with new 1500x1500 transparent PNG design
Fresh Tom's Java Jive logo with coffee cup, Tom's Java Jive text,
Artisan Coffee Roaster, and www.tomsjavajive.com. Bump logo display
height to 65px to suit square aspect ratio.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 19:28:09 +00:00
myron b11a274986 Fix stale contact info in privacy page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 19:01:58 +00:00
myron 73143577e3 Fix contact form 500 — use sendEmail() not cybermailSend()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 18:50:59 +00:00
myron c1c926a416 Fix contact info — correct email, phone, and ZIP throughout
sales@tomsjavajive.com, (817) 266-2022, Weatherford TX 76088 applied to
contact page, header schema, returns page, and FAQ. Fix Quick Links
list style so links render properly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 18:49:24 +00:00
myron 15bcef262f Google Merchant Center trust signal improvements
- Add contact.php (was 404, linked from footer and returns page)
- Add shippingDetails and hasMerchantReturnPolicy to product schema
- Add priceValidUntil to product Offer schema
- Improve merchant feed descriptions (use DB description when present)
- Add handling/transit times and return_policy_label to feed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 18:46:21 +00:00
myron 5637b6d7f5 CSS modularization Phase 2: account, cart, checkout
Extract account/cart/checkout styles into dedicated CSS files; remove inline styles and orphaned style blocks from HTML. Wire $extraHead on all account pages, cart.php, and checkout.php.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:51:57 +00:00
myron 771e1a15b1 Modularize CSS into page-specific files
Split style.css into home.css (hero, features, newsletter, splash) and
products.css (product grid/cards). Each page loads only what it needs
via $extraHead. style.css now contains only truly shared styles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:32:57 +00:00
myron f54148dbe7 Condense footer from 5 columns to 3
Merged Company + Support into a single Help column, removed Sub
Categories section. Tightened grid gap, padding, and list spacing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:23:32 +00:00
myron 01dc6f90fe Remove Weatherford TX location references from merchant feed 2026-06-14 23:49:11 +00:00
myron 94d19d27f1 Add Google Search Console verification meta tag 2026-06-14 23:10:07 +00:00
myron b504981b89 Add Google Merchant Center product feed and GSC meta tag placeholder
- merchant-feed.php: RSS 2.0 feed with all active products; includes
  title, description, image_link, price, availability, brand, shipping,
  google_product_category for each item; URL to submit in Merchant Center
- header.php: placeholder GSC meta tag (replace PASTE_GSC_CODE_HERE with
  verification content value from Search Console)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 23:01:53 +00:00
myron 873a0962c6 SEO overhaul: product schema, dynamic sitemap, favicon, og-image fix
- product.php: set metaTitle, metaDescription, canonicalUrl, ogImage,
  ogType=product, productSchema (JSON-LD with price/availability/reviews),
  and breadcrumbs variables for header.php to consume
- sitemap.php: dynamic XML sitemap generated from DB — includes all 30
  active products + static pages; robots.txt now points here
- header.php: fix favicon links (favicon.ico in root + icon-192.png);
  fix productSchema output (was double-encoding via json_encode)
- robots.txt: point Sitemap directive to /sitemap.php

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 22:50:25 +00:00
myron f89362528a Fix loyalty system: load tiers from DB, award points on payment
- LoyaltyProgram now loads tiers from loyalty_tiers DB table in constructor
  with fallback to hardcoded defaults if table is empty
- awardPoints() accepts order_id param with duplicate-prevention check so
  points cannot be double-awarded for the same order
- Inserts balance_after into loyalty_transactions for accurate history
- payment-status.php: award points after Stripe checkout session or
  PaymentIntent confirmed as paid
- create-checkout-session.php: award points in demo mode payment path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:58:37 +00:00
myron b6d0319be7 Fix admin order page blank redirect after status/note update
Missing ob_start() meant HTML was output before POST handler ran,
so header() redirects silently failed after saving changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:50:53 +00:00
myron bdba57166d Condense shop page: merge hero + filters into one compact bar
Combined the large hero section and two-row filter section into a single
compact dark header bar. Category and type pills are inline with a divider,
search/sort sit in the header row. Reduced section top padding.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:48:45 +00:00
myron 6e4d2734bb Fix console warnings: remove SW registration, fix meta tag, clean manifest
- Remove service worker registration from footer (SW is already self-unregistering)
- Add mobile-web-app-capable meta to fix deprecation warning
- Remove missing icon references from manifest (only 192/512 PNGs exist)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:38:15 +00:00
myron 714ef13897 Fix Stripe checkout: wrong column name stripe_checkout_session → stripe_session_id
DB column is stripe_session_id but code was writing to stripe_checkout_session,
causing a 500 on checkout and breaking payment status checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:35:35 +00:00
myron ecf5a7ab67 Fix cart quantity update: reload page to reflect accurate totals
Partial DOM update was missing item row totals, shipping recalc, and
grand total. Reload ensures all numbers are always correct.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:29:31 +00:00
myron ca2f612421 Fix add-to-cart on home page: wrong class and data attribute
Button used add-to-cart/data-id instead of add-to-cart-btn/data-product-id.
Added inline onclick to match shop and product pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:26:47 +00:00
myron 7dfec466a1 Disable service worker and clear all caches
SW was caching shop pages and JS files, serving stale versions without
the inline onclick handler. Replacing with self-unregistering SW.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:23:36 +00:00
myron 935b838c8f Fix cart: start session in functions.php so API endpoints persist cart
All api/*.php files include functions.php but none called session_start(),
so $_SESSION writes were lost after each request. Cart appeared to work
(API returned cart_count:1) but nothing was ever saved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:18:23 +00:00
myron 6e86031576 Fix add-to-cart: bust main.js SW cache, add inline onclick to shop buttons
Service worker was caching main.js (cache-first strategy) so event listeners
may not have been running. Added filemtime version param to main.js like CSS.
Also added inline onclick to shop page buttons so they work regardless of
whether event delegation is functional.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:11:35 +00:00
myron e389254a7c Auto-bust Cloudflare CSS cache using filemtime version param
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:04:09 +00:00
myron 5c0927af39 Fix ambiguous column error on shop page JOIN query
Prefixed is_active, category, product_type_id, name, description, and ORDER BY columns with table alias p to resolve ambiguity with the product_types JOIN.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 19:56:47 +00:00
myron e6ca96b75c Make footer Shop links dynamic from products categories
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 19:50:51 +00:00
myron 29656bd4d7 Add product count column to product types admin page
Shows how many active products are linked to each type, linked to the filtered products list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 19:50:29 +00:00