21 Commits

Author SHA1 Message Date
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 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 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 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 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 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
myron da60888b72 Fix CSV import — convert tags/images to valid JSON before insert (mysql json_valid constraint) 2026-06-14 16:05:42 +00:00
myron 0a6c4508d5 Rebuild product image upload section — fix broken HTML, wrong textarea name, JS quote errors 2026-06-14 15:57:33 +00:00
myron 1609dea8fb Fix product image upload — remove header.php (outputs HTML), auth check directly 2026-06-14 15:48:19 +00:00
myron 5058da704a Fix product image upload — ob_end_clean() before JSON response, fix uploads dir permissions 2026-06-14 15:44:25 +00:00
myron 66cbd3f0d3 Remove prefilled placeholder email from admin login 2026-06-14 15:18:27 +00:00
myron 7eddf9e85d Fix remaining code review findings: email transport errors, metadata payload, from-name, pagination
- Email::send(): add curl_error() check so transport failures (timeout,
  DNS, TLS) return a diagnosable error string instead of Unknown error
- Email::send(): strip metadata key from options before array_merge so
  non-API fields are never sent to CyberMail endpoint
- Email::send() + sendEmail(): include from-name in From field using
  RFC 5322 "Name <email>" format so fromName DB setting takes effect
- email-log.php: replace unbounded page-link loop with a windowed
  paginator (first/last 2 pages + ±2 around current) with ellipsis
  gaps — prevents hundreds of anchors rendering at scale
2026-06-03 06:01:44 +00:00
myron 76bf967bd0 Switch email to CyberMail API; integrations page manages API key via DB 2026-05-29 18:49:15 +00:00
myron 6b71828199 Fix integrations.php: handle POST before HTML output to fix blank screen on save 2026-05-29 18:31:16 +00:00
myron 0d481f8feb Simplify admin login to email/password only; remove Google OAuth 2026-05-29 15:33:16 +00:00
myron e344e52d70 Add Google OAuth admin login; fix header comment; both auth methods active 2026-05-29 15:13:38 +00:00
myron 53f9f3e4da Replace all SendGrid references with CyberMail in admin UI 2026-05-29 15:06:43 +00:00
myron 996ca0d621 Initial commit 2026-05-22 12:52:44 +00:00