# Anonymous Checkout — Findings & Lockdown Plan

**Date:** July 3, 2026
**Status:** IN PROGRESS — step 2 (remove `access checkout` from anonymous) done on
LOCAL July 5; deploy to TEST/PROD via `cim -y`. Steps 1, 3, 4 open. Note: step 1 is
NOT config-only — `CCSoccerCheckoutFlow::getSteps()` defines no `login` step, so it
needs a small code change too (see SESSION_HANDOFF July 5 evening section).
**Trigger:** Caleb observed that Wayne bought "Team Fee — SLO Friendly 2026" while
not logged in (also recorded in SESSION_HANDOFF item 34, order 61 — Anonymous, no
account tied to the order).
**Priority:** Address before opening public season registration and emailing the list.

---

## The question

Is the cart/checkout available to anonymous (not-logged-in) visitors, or is that
Team Fee product a one-off exception?

## The answer: anonymous checkout is possible, and it is systemic

Two settings combine to allow it (verified in `config/sync`):

1. **The `login` checkout pane is disabled.** In
   `commerce_checkout.commerce_checkout_flow.ccsoccer.yml`, the `login` pane is
   `step: _disabled`. In Drupal Commerce this pane is what forces a shopper to log
   in or register during checkout. Disabled = checkout never demands authentication.
2. **The anonymous role can shop and check out.** `user.role.anonymous.yml` grants
   both `access checkout` and `view commerce_product`.

So a not-logged-in visitor can view products and complete checkout as a guest. That
is how the Team Fee purchase went through with no account attached.

## Why registration is still (mostly) protected — but fragilely

Season / tournament / jersey products are **not** anonymously buyable today, but for
a per-product reason rather than a global gate:

- They are added to the cart only through custom routes (`/register/season/...`,
  `/register/tournament/...`, jersey add) that all require `_user_is_logged_in: TRUE`.
- Their product view displays do **not** render an add-to-cart form (only the
  `default` product type and the leftover test type do).
- `OrderCompleteSubscriber` refuses to create a registration for an anonymous
  customer.

**The fragility:** nothing enforces login at checkout itself. The only thing stopping
an anonymous season purchase is that the season product happens not to expose an
add-to-cart button. Any product that **does** expose the standard add-to-cart form
becomes anonymously purchasable. Today that means:

- **Team Fee** (as sold to Wayne).
- Any plain **`default`** product type.
- The leftover **`security_metrics_test_product_ty`** test product type — still in
  `config/sync` and it renders add-to-cart. (Checklist marked the test product done,
  but the type config remains.)

## Invite flow is not affected

An invited new player clicks the email, creates their account/login, and only then
does the product land in their cart and they check out — so they are authenticated by
checkout time. Locking down anonymous checkout does **not** break invites.

## Why this matters for bots / malicious actors

1. **Orphaned orders** — payments with no account attached (like order 61), a
   reconciliation headache.
2. **Card testing (carding) — the bigger risk.** An open anonymous checkout wired to
   the **live** Authorize.net gateway is a classic bot target: submit many stolen
   card numbers through the payment step to find live ones. They do not need a free
   product — abusing the gateway alone can rack up per-transaction fees, cause
   chargebacks, and get the merchant account flagged or frozen. This surface gets
   probed exactly when the site goes public.

---

## Recommended lockdown (in priority order)

**1. Re-enable the `login` checkout pane; require login, guest checkout off.**
The primary systemic fix. Because that pane only appears for anonymous shoppers, it
does not change the normal logged-in flow or the invite flow. Forces authentication
before payment for any cart.
- Risk: **needs testing** — it is a custom checkout flow, so confirm the login step
  slots in cleanly ahead of the custom panes. Test on LOCAL → TEST (with a card on
  TEST) before PROD.

**2. Remove `access checkout` from the anonymous role.** Defense-in-depth. Since the
invite flow authenticates first, anonymous checkout serves no legitimate purpose here
once Team Fee is handled.
- Risk: **low.** Config-only; reversible. Verify a logged-in checkout still works.

**3. Remove the `security_metrics_test_product_ty` test product type + any product.**
It renders add-to-cart and is anonymously buyable — a test artifact that should not
ship.
- Risk: **low.** Delete the product/variation and the type config; export.

**4. Team Fee product.** Keep it unpublished (Caleb was already disabling it). Route
next year's team-fee purchase through a login-required flow (per item 34).
- Risk: **low** (already in hand).

### Suggested test plan for #1 (login pane)
1. On LOCAL: re-enable `login` pane at the first checkout step; set its config to
   require login (disable guest checkout). `drush cim` / rebuild.
2. Logged-out: add a product → checkout should now stop at a "log in or register"
   step, not payment.
3. Logged-in: normal season/tournament/jersey checkout still completes end to end.
4. Invite flow: invited new player creates account, product in cart, checkout works.
5. Repeat on TEST with a real card against the test gateway before touching PROD.

---

## Related open items
- SESSION_HANDOFF item 34 (productized team-fee flow should require login next year).
- Pre-launch checklist: geo-blocking (Smart IP + Country Block) and confirming the
  reCAPTCHA renders on public forms — both reduce bot volume and complement this
  lockdown. reCAPTCHA on the payment/checkout step would further blunt card testing.
