Wp Woocommerce
ASecurityWooCommerce store practice for sites this plugin builds — the catalog, store and full tiers recorded as store.tier in .wp-create.json and the plugins each installs (and the ones deliberately avoided, with reasons), store-kit's catalog mode and Stripe keys kept as constants in wp-config.php, Stripe test mode, the HPOS, block-checkout and card-testing defaults a WP-CLI install gets wrong, and the woo-setup.php script /wp-woo-setup runs to bring a store in line with the store block. Use when cre...
- 6 stars
- 0 votes
- 0 copies
- 0 views
- Added October 5, 2026
Works with
Security analysis
100/100Pro scans all 4 files and shows the line behind each finding
npx -y skills add yojahny55/claude-wp-builder --skill wp-woocommerce --agent claude-codeAre you the author of Wp Woocommerce?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/yojahny55-wp-woocommerce)---
name: wp-woocommerce
description: WooCommerce store practice for sites this plugin builds — the catalog, store and full tiers recorded as store.tier in .wp-create.json and the plugins each installs (and the ones deliberately avoided, with reasons), store-kit's catalog mode and Stripe keys kept as constants in wp-config.php, Stripe test mode, the HPOS, block-checkout and card-testing defaults a WP-CLI install gets wrong, and the woo-setup.php script /wp-woo-setup runs to bring a store in line with the store block. Use when creating, configuring or auditing a WooCommerce store, choosing a store tier or a store plugin, turning on catalog mode, or asking why Stripe keys live in wp-config.php. Not for translating products (wp-polylang), or a product post type for a client who will never sell online (/wp-cpt).
user-invocable: false
---
# WooCommerce stores
A store is a recorded decision, like the i18n strategy and the demo mode. The `store` block in
`.wp-create.json` says what the store sells and how, `/wp-woo-setup` brings WooCommerce in line
with it, and every other command reads the block instead of guessing. This skill is the
knowledge behind those values. It acts on nothing: the command runs the scripts.
Read the block, never infer it: `store.tier` is the tier, `store.enquiry` the catalog's enquiry
channels, `store.checkout` (with `store.checkout_reason`) the checkout type, and
`store.payments.mode` the payment mode.
## The three tiers
| `store.tier` | What it is | Profile |
|---|---|---|
| `catalog` | Products and prices, nothing purchasable. Runs on WooCommerce with buying switched off by `store-kit`, so moving up a tier keeps every product, with no migration. `store.enquiry` records which enquiry channels apply — `form`, `where-to-buy`, `whatsapp` — and only `where-to-buy` works out of the box, as WooCommerce's External product type: no starter theme renders the enquiry form or the WhatsApp button. | `${CLAUDE_PLUGIN_ROOT}/templates/profiles/woo-catalog.json` |
| `store` | Cart, block checkout, Stripe in test mode, shipping, tax, SMTP, Turnstile, and cookie consent where the market needs it. | `${CLAUDE_PLUGIN_ROOT}/templates/profiles/woo-store.json` |
| `full` | `store` plus abandoned-cart email, email marketing, reviews, search and filters, swatches, wishlist, and Google and Meta feeds. | `${CLAUDE_PLUGIN_ROOT}/templates/profiles/woo-full.json` |
`references/plugins.md` holds why each plugin is there, why the popular alternatives are not,
and the add-ons (digital downloads, print-on-demand, wholesale, multilingual). Read it before
adding, removing or swapping a plugin in a store profile: `tests/checks/wp-profiles.sh` fails
a profile plugin with no row under Picks, or one listed under Avoid.
When a client will never sell online, a product custom post type from `/wp-cpt` is lighter
than any tier: no sessions, no Action Scheduler, no Store API. The catalog tier is for the
client who may sell later.
## What a WP-CLI install gets wrong
Measured on WooCommerce 11.1.2 unless marked (source).
- **New-store defaults never run.** They are hooked to `admin_init` (source), so HPOS, "coming
soon" and the onboarding defaults wait for the first wp-admin visit. Setup sets every value
itself.
- **HPOS must be on before the first order.** On a store with no orders `wp wc hpos enable`
creates the tables and switches; after an order exists WooCommerce will not switch on its own.
- **Pages.** Activation creates Shop, Cart, Checkout (block markup) and My account, and a draft
Refund policy — no Terms page.
- **Polylang gives WooCommerce's pages no language** when it was configured after they were
created, and `product` is not a translated post type by default. Setup assigns the default
language to the store pages. Products stay untranslated: this plugin has
no bridge between WooCommerce and Polylang.
- **Card testing.** Both Store API limiters are off by default, and card-testing scripts post to
`/wc/store/v1/checkout` directly even on a classic-checkout store. Setup turns on the
**checkout limit** (`woocommerce_feature_rate_limit_checkout_enabled`): 3 attempts a minute
per IP, refused as HTTP 400 `rate_limit_exceeded`. It never turns on the **general limiter**
(`woocommerce_store_api_rate_limit_options`) — both keep one per-IP row, so ordinary cart
traffic dilutes the checkout limit to the general one.
- **Turnstile loads its WooCommerce integration only once `cfturnstile_tested` is `yes`** (source),
normally set by testing the keys in wp-admin. Setup sets it together with Cloudflare's
always-pass test keys on a local site; real keys are a launch step.
- **Background jobs.** With `DISABLE_WP_CRON`, Action Scheduler runs only on wp-admin requests
(source); setup runs the queue once and reports the missing cron (PERF-061, PERF-062).
- **Emails.** Every send attempt leaves an order note (`Email "New order" sent.`), which is how a
test proves email without an inbox.
## Practices every store gets
- **API keys never at rest; a rotated webhook secret is the one exception.** Stripe's API keys
are `STORE_KIT_STRIPE_*` constants in `wp-config.php`; `store-kit` supplies them when the
gateway reads its settings and strips them when it saves, so a database dump or clone carries
none of those, and SEC-040 (which reads the raw row) agrees. A webhook-secret constant only
ever seeds an *empty* field, because Stripe rotates that secret on its own when Stripe
reconfigures webhooks (connect, re-key, the settings button, or after a plugin update) — a
rotated value is left to reach the database, with an admin notice naming the now-stale
constant. Configure Stripe with API keys, not "Connect with Stripe", which saves keys to the
database.
- **Paid downloads stay closed.** Download method `force`; the native nginx and Caddy templates
deny `woocommerce_uploads`, because nginx never reads WooCommerce's `.htaccess` (SEC-039).
- **Guest checkout on, account creation offered after purchase, coupons on.** A forced account is
among the top reasons US shoppers abandon a checkout (Baymard, 2025).
- **Block checkout by default.** It is WooCommerce's default and where new features land; classic
PHP checkout hooks do not fire on it. `store.checkout: shortcode` only with a written reason in
`store.checkout_reason` — an extension that does not support blocks.
- **Test mode, always.** `store.payments.mode` is `test`, and the validator accepts nothing
else. Cash on delivery only in a `local` environment, for the automated test order; "coming
soon" everywhere else.
- **No command takes a store live.** Live Stripe keys, test mode and "coming soon" off, cash on
delivery off and real Turnstile keys are one launch step with a fixed order, and it is not
built: setup refuses a live key. Do not change these by hand — stop and hand the launch to
the operator.
- **Abandoned-cart email depends on the market.** The US is opt-out (CAN-SPAM); the UK allows a
soft opt-in only for an address typed at checkout; in the EU — Germany above all — treat it as
advertising that needs prior consent.
## The scripts
Never run them directly: `/wp-woo-setup` runs `scripts/woo-setup.php` (dry run in its Step 5,
apply in Step 6) after the gates they depend on. What the command passes, for reading its
output:
```bash
$WP eval-file "${CLAUDE_PLUGIN_ROOT}/skills/wp-woocommerce/scripts/woo-setup.php" '<project-path>' [dry-run] [force]
```
- **Arguments** are bare words, because `wp eval-file` refuses a `--flag` it does not know.
- **Exit `0`**: the store matches the block, `degraded` lines allowed. **Exit `1`**: refused
before anything is written, with a `refused:` line saying why.
- **It needs** native WP-CLI on the host (`environment.engine` is `native`), with WooCommerce
and `store-kit` active; otherwise it refuses.
- **Report-only:** on a store that has orders, or was adopted, and where setup has never run, it
writes nothing and reports what it would change — until the operator passes `force`.
Setup records a hash of every value it writes in `store_kit_setup_state`; a value that no
longer matches that hash is the client's and is left alone unless `force` is passed. The one
exception is launch state — "coming soon", Stripe's `enabled`/`testmode`, cash on delivery —
which a store with orders never has written for it, absent or not, force or not: change it in
WooCommerce instead.
`scripts/woo-lib.php` holds setup's decisions with no WordPress calls, so
`tests/checks/woo-lib.sh` runs them under bare PHP. Read it to see how a value is compared or
decided; it is never run on its own. Both files hold the PHP 7.4 floor.
Files in this skill
- SKILL.md
- references/plugins.md
- scripts/woo-lib.php
- scripts/woo-setup.php
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.