diff --git a/API.md b/API.md index d5f689d..5a0f95a 100644 --- a/API.md +++ b/API.md @@ -384,6 +384,39 @@ Constants::EVENT_COUPON_REDEEMED // 'coupon.redeemed' ## WordPress Hooks +The plugin registers these WooCommerce and WordPress hooks: + +| Hook | Handler | Purpose | +| --- | --- | --- | +| `woocommerce_after_single_product` | `hellotext_product_viewed` | Track `product.viewed` | +| `woocommerce_after_cart` | `hellotext_trigger_cart_updated` | Compare current and previous cart state | +| `woocommerce_add_to_cart` | `hellotext_trigger_cart_updated` | Compare current and previous cart state | +| `woocommerce_cart_item_removed` | `hellotext_trigger_cart_updated` | Compare current and previous cart state | +| `woocommerce_after_cart_item_quantity_update` | `hellotext_trigger_cart_updated` | Compare current and previous cart state | +| `woocommerce_applied_coupon` | `hellotext_coupon_redeemed` | Track valid `coupon.redeemed` events | +| `woocommerce_after_order_details` | `hellotext_order_placed` | Track `order.placed` and persist encrypted session metadata on the order | +| `woocommerce_order_status_changed` | `track_order_status` | Track `order.confirmed`, `order.cancelled`, and `order.delivered` | +| `woocommerce_order_refunded` | `hellotext_refund_created` | Track `refund.received` | +| `user_register` | `hellotext_user_registered` | Track customer registration/profile flow | +| `hellotext_woocommerce_cart_updated` | `hellotext_cart_updated` | Track `cart.added` and `cart.removed` | +| `update_option` | `custom_field_updated` | Recreate integration after Business ID changes | +| `admin_init` | `hellotext_settings_init` | Register plugin settings | +| `admin_head` / `wp_head` | `hellotext_script` | Inject frontend/admin scripts | + +## WooCommerce Compatibility + +Compatibility posture as of 2026-06-11. See [WooCommerce Compatibility and API Audit](docs/WOOCOMMERCE-AUDIT.md) for the full hook/API audit, HPOS assessment, compatibility matrix, and release recommendations. + +| Area | Status | Notes | +| --- | --- | --- | +| WooCommerce order metadata | HPOS-safe code path | Uses `WC_Order` metadata CRUD for Hellotext session metadata. | +| Order status and refund events | HPOS-safe code path | Reads session metadata from the order object instead of `get_post_meta()`. | +| Product, cart, coupon events | Public hook/API usage | Uses WooCommerce hooks and objects, not order storage internals. | +| Automated tests | Mock-backed unit coverage | Event tests cover outbound payloads without real WordPress/WooCommerce. | +| Runtime verification | Required before declaration | Test with HPOS enabled and disabled in a real WooCommerce site before declaring formal compatibility in the plugin header/runtime. | + +Do not add a formal HPOS compatibility declaration until the release candidate has passed runtime checks on WooCommerce with HPOS enabled and disabled. + ### Actions #### `hellotext_create_profile` diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 10af1bb..c0d287f 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -177,6 +177,35 @@ The project uses [Pest](https://pestphp.com/) for testing. ./vendor/bin/paratest ``` +The Composer aliases used by CI and maintainers are: + +```bash +composer install +composer test +composer format:check +composer build +``` + +`composer build` installs production dependencies with `--no-dev --optimize-autoloader`. Do not use the build output for local development without reinstalling dev dependencies afterward. + +### Verified Local Flow + +Last verified: 2026-06-11 + +| Command | Result | Notes | +| --- | --- | --- | +| `composer install` | Passed | Requires PHP 8.2+ and Composer 2. The local system Composer emitted PHP deprecation notices under PHP 8.4; dependencies still installed correctly. | +| `composer test` | Passed | Pest suite runs against WordPress/WooCommerce mocks, not a real WordPress install. | +| `composer format:check` | Passed | PHP CS Fixer warned when run under PHP 8.4 because the Composer platform is PHP 8.2.12. CI runs the style check on PHP 8.2. | +| `composer build` | Passed | Runs `composer install --no-dev --optimize-autoloader` and removes dev tooling from `vendor/`. Run `composer install` again afterward before continuing local development. | + +Setup assumptions: + +- PHP 8.2 or newer is available locally. +- Composer can install from the checked-in `composer.lock`. +- Tests do not require a real WordPress, WooCommerce, database, or Hellotext API connection. +- Outbound HTTP in tests is mocked through WordPress HTTP function stubs. + ### Writing Tests #### Unit Tests @@ -312,14 +341,22 @@ add_action('all', function($hook) { ### Pre-Release Checklist -- [ ] Run all tests: `./vendor/bin/pest` -- [ ] Check for PHP errors/warnings -- [ ] Test on fresh WordPress installation -- [ ] Test with WooCommerce latest version -- [ ] Verify settings page functionality -- [ ] Test event tracking in Hellotext dashboard -- [ ] Update documentation if API changed -- [ ] Update changelog.txt +- [ ] Confirm the version in `hellotext.php` matches the release tag. +- [ ] Update `changelog.txt` with user-facing changes, compatibility notes, and any known limitations. +- [ ] Review open dependency PRs and confirm there are no urgent security/runtime updates pending. +- [ ] Run `composer install` from a clean checkout. +- [ ] Run `composer test` and confirm all tests pass. +- [ ] Run `composer format:check` and confirm no style diff is reported. +- [ ] Run `composer build` and confirm production dependencies install with `--no-dev --optimize-autoloader`. +- [ ] Inspect the production `vendor/` tree enough to confirm dev-only packages such as Pest, Mockery, stubs, PHPUnit, and PHP CS Fixer are not included in the release build. +- [ ] Verify `.distignore` excludes development files from the release zip: `.github/`, `tests/`, `composer.json`, `composer.lock`, `DEVELOPMENT.md`, and `API.md`. +- [ ] Smoke test the plugin zip in a clean WordPress/WooCommerce site. +- [ ] Confirm settings save correctly: Business ID, access token, webchat ID, placement, and behavior. +- [ ] Confirm script/webchat injection renders on the storefront when configured. +- [ ] Smoke test tracking for product view, cart add/remove, coupon redemption, checkout/order placement, order status change, refund, user registration, plugin activation, and plugin deactivation. +- [ ] Smoke test order and refund flows with WooCommerce HPOS enabled and disabled. +- [ ] Record WooCommerce API/HPOS compatibility notes in the release description if anything changed. +- [ ] Run `composer install` again after build validation to restore dev dependencies before more local work. ### Creating a Release @@ -329,24 +366,17 @@ git tag -a v1.3.0 -m "Release version 1.3.0" git push origin v1.3.0 ``` -2. **Build release package:** -```bash -# Remove dev dependencies -composer install --no-dev - -# Create zip -cd .. -zip -r hellotext-wordpress-1.3.0.zip hellotext-wordpress \ - -x "hellotext-wordpress/.git/*" \ - -x "hellotext-wordpress/tests/*" \ - -x "hellotext-wordpress/node_modules/*" -``` +2. **Let GitHub Actions build the release package:** + - Tags matching `v*` trigger `.github/workflows/release.yml`. + - The workflow installs PHP 8.2 dependencies with `composer install --no-dev --optimize-autoloader`. + - The workflow creates `release/hellotext-wordpress.zip` and uploads it to the GitHub release. -3. **Create GitHub release:** - - Go to Releases → Draft new release - - Select the tag - - Upload the zip file - - Add release notes from changelog +3. **Verify the generated release:** + - Download the release zip from GitHub. + - Confirm files excluded by `.distignore` are not included: `.github/`, `tests/`, `composer.json`, `composer.lock`, `DEVELOPMENT.md`, and `API.md`. + - Confirm runtime files are included: `hellotext.php`, `src/`, `vendor/`, `README.md`, and `changelog.txt` if present. + - Install the zip in a clean WordPress/WooCommerce site. + - Confirm the release asset installs and activates without PHP warnings in `debug.log`. ### Post-Release @@ -354,6 +384,24 @@ zip -r hellotext-wordpress-1.3.0.zip hellotext-wordpress \ 2. Announce release to team 3. Monitor error logs for issues +### Dependency Update Triage + +Use this checklist before merging dependency-only PRs: + +- Confirm the diff is limited to dependency metadata or the expected workflow file. +- Confirm GitHub reports the PR as mergeable. +- Confirm required CI checks pass. +- Prefer merging patch/minor test stub updates independently from runtime code changes. +- For GitHub Action major updates, inspect the action release notes before merging. + +Dependency PRs reviewed during this maintenance pass: + +- Keep dependency-only lockfile refreshes separate from runtime compatibility changes. +- Prefer one broad lock refresh over multiple older overlapping Dependabot PRs when it carries newer compatible versions and CI passes. +- For GitHub Action major updates, inspect the action release notes and verify the generated zip on the next tagged release because release publishing only runs on tags. + +See also [WooCommerce Compatibility and API Audit](docs/WOOCOMMERCE-AUDIT.md) for hook, HPOS, and release compatibility notes. + ## Contributing ### Workflow diff --git a/README.md b/README.md index 9f0a551..5c32059 100644 --- a/README.md +++ b/README.md @@ -117,12 +117,30 @@ For developers looking to integrate, extend, or contribute to this plugin: - **[API Documentation](API.md)** - Complete API reference for classes, methods, and hooks - **[Development Guide](DEVELOPMENT.md)** - Setup, testing, and contribution guidelines -### Requirements +- **[WooCommerce API Audit](docs/WOOCOMMERCE-AUDIT.md)** - Hook, HPOS, and compatibility audit notes + +## Requirements - PHP 8.2 or higher - WordPress 5.0 or higher - WooCommerce 5.0 or higher +## Compatibility Matrix + +| Layer | Declared support | Tested in CI | Notes | +| --- | --- | --- | --- | +| PHP | 8.2+ | 8.2, 8.3, 8.4, 8.5 | Composer platform is pinned to PHP 8.2.12. | +| WordPress | 5.0+ | Unit tests only, no WordPress runtime | Tests use WordPress stubs/mocks; runtime smoke testing is required before release. | +| WooCommerce | 5.0+ | Unit tests only, no WooCommerce runtime | Tests use WooCommerce stubs/mocks; HPOS and block-theme behavior require manual smoke testing. | +| WooCommerce stubs | Not a runtime dependency | Locked through Composer dev dependencies | Used for development/test confidence only. | +| Release package | GitHub tag workflow | Tag workflow only runs on `v*` tags | Verify the generated zip contents before publishing. | + +## Compatibility Notes + +- Order session metadata uses WooCommerce order CRUD methods instead of direct post meta APIs. +- Before each release, test event tracking with WooCommerce HPOS enabled and disabled. +- Formal WooCommerce HPOS compatibility declaration is deferred until manual runtime smoke testing passes. + ## Support Need help? diff --git a/docs/WOOCOMMERCE-AUDIT.md b/docs/WOOCOMMERCE-AUDIT.md new file mode 100644 index 0000000..1713ff6 --- /dev/null +++ b/docs/WOOCOMMERCE-AUDIT.md @@ -0,0 +1,86 @@ +# WooCommerce Compatibility and API Audit + +Last reviewed: 2026-06-11 + +This audit covers the WooCommerce and WordPress integration points used by the Hellotext WooCommerce plugin. It is intentionally maintenance-focused: it documents current hooks, validates order access against current WooCommerce HPOS guidance, and records practical release risks without changing Hellotext API contracts. + +## References + +- WooCommerce APIs overview: +- WooCommerce block hooks reference: +- WooCommerce HPOS extension recipe book: +- WooCommerce Code Reference: +- WordPress Plugin Handbook hooks and activation/deactivation APIs: + +## Summary + +The plugin uses current WooCommerce PHP hooks and CRUD object access for the customer activity it tracks. Order and refund handling are HPOS-safe in code after the order session metadata changes because order reads/writes use `wc_get_order()`, `$order->get_meta()`, `$order->update_meta_data()`, and `$order->save()` rather than direct post meta functions. + +Do not declare HPOS compatibility yet. Add the declaration only after a manual runtime smoke test passes with WooCommerce HPOS enabled and disabled in a real WordPress/WooCommerce install. + +## Hook and API Matrix + +| Area | Hook/API | Handler | Current status | Notes | +| --- | --- | --- | --- | --- | +| Product views | `woocommerce_after_single_product` | `hellotext_product_viewed()` | Appropriate for classic product templates | This is a classic template hook. Confirm behavior with block themes before claiming full block-theme coverage. | +| Cart updates | `woocommerce_add_to_cart` | `hellotext_trigger_cart_updated()` | Appropriate | Mutation hook is still a valid WooCommerce cart hook. Tests cover event creation with mocks. | +| Cart removals | `woocommerce_cart_item_removed` | `hellotext_trigger_cart_updated()` | Appropriate | Tests cover `cart.removed` payloads. | +| Cart quantity changes | `woocommerce_after_cart_item_quantity_update` | `hellotext_trigger_cart_updated()` | Appropriate | Tests cover quantity increases and no-op unchanged carts. | +| Cart page diff check | `woocommerce_after_cart` | `hellotext_trigger_cart_updated()` | Works for classic cart template | This hook does not cover all Cart block render paths. Future block-specific coverage should be evaluated. | +| Coupon redemption | `woocommerce_applied_coupon` | `hellotext_coupon_redeemed()` | Appropriate | Handler validates the coupon before sending `coupon.redeemed`; tests cover valid and invalid coupons. | +| Order placement | `woocommerce_after_order_details` | `hellotext_order_placed()` | Works, but has duplicate-risk | This template hook can fire on thank-you/order details and My Account view-order screens. Evaluate `woocommerce_thankyou` for classic checkout and `woocommerce_store_api_checkout_order_processed` for Checkout block support. | +| Order status changes | `woocommerce_order_status_changed` | `track_order_status()` | Appropriate | Handler maps `processing`, `cancelled`, and `completed` to Hellotext events. Uses WooCommerce order object/meta access. | +| Refunds | `woocommerce_order_refunded` | `hellotext_refund_created()` | Appropriate | Handler loads orders/refunds with WooCommerce APIs and reads session meta with `$order->get_meta()`. | +| User registration | `user_register` | `hellotext_user_registered()` | Appropriate WordPress hook | Tests cover profile creation flow with mocked HTTP. | +| Profile creation | `hellotext_create_profile` | Closure in `CreateProfile.php` | Internal plugin hook | Used by event flows to associate logged-in users/sessions. | +| Plugin deactivation | `register_deactivation_hook()` and `hellotext_remove_integration` | `hellotext_deactivate()` and closure in `AppRemoved.php` | Appropriate WordPress APIs | Tests cover DELETE request shape for integration cleanup. | +| Plugin uninstall | `register_uninstall_hook()` | `uninstall()` | Appropriate WordPress API | Cleans Hellotext options and WooCommerce API keys. | +| Settings | WordPress Settings API | `src/Misc/Settings.php` | Appropriate | Stores Business ID, access token, and webchat options using WordPress options. | +| Webchat injection | WordPress script/footer hooks | `src/Misc/Scripts.php` | Appropriate WordPress APIs | Verify rendered script manually in a real WordPress page during release smoke testing. | +| WooCommerce API keys | Direct `$wpdb` against `{$prefix}woocommerce_api_keys` | `src/Events/AppInstalled.php`, `hellotext.php` uninstall | Acceptable exception | WooCommerce API keys are not order data and are not part of HPOS order storage. No WooCommerce CRUD replacement is required for this table access. | + +## HPOS Assessment + +WooCommerce HPOS changes where order data is stored. The HPOS recipe book recommends avoiding direct WordPress post/postmeta APIs for order data and using WooCommerce CRUD APIs instead. + +The plugin's order-related runtime paths are compatible with that guidance: + +- `src/Adapters/OrderAdapter.php` loads numeric orders with `wc_get_order()`. +- `src/Events/OrderPlaced.php` stores the Hellotext session with `$order->update_meta_data(Constants::META_SESSION, ...)` and `$order->save()`. +- `src/Events/OrderStatus.php` reads the stored session with `$order->get_meta(Constants::META_SESSION, true)`. +- `src/Events/RefundReceived.php` loads the order with `wc_get_order($order_id)` and reads session metadata with `$order->get_meta(Constants::META_SESSION, true)`. +- The plugin does not query `shop_order` posts or use direct post meta functions for order session metadata. + +The test suite uses WordPress/WooCommerce mocks, so it validates code behavior and request shape but not a real HPOS datastore. Because of that, the formal declaration is deferred. + +## Deferred HPOS Declaration + +After a real WooCommerce smoke test passes with HPOS enabled and disabled, add this to the main plugin file: + +```php +add_action('before_woocommerce_init', function () { + if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) { + \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true); + } +}); +``` + +Do not add this declaration until manual runtime testing confirms product view, cart, coupon, checkout, status transition, refund, settings, deactivation, and webchat behavior in a real store. + +## Compatibility Matrix + +| Layer | Declared | CI-tested | Stub-tested | Assumed or manual-only | +| --- | --- | --- | --- | --- | +| PHP | README requires PHP 8.2+; Composer platform is PHP 8.2.12 | GitHub Actions runs Pest on PHP 8.2, 8.3, 8.4, and 8.5; format check on PHP 8.2 in CI | Not applicable | Local contributor machines may run newer PHP; PHP CS Fixer warns when run outside project platform. | +| WordPress | README requires WordPress 5.0+ | No full WordPress runtime in CI | `php-stubs/wordpress-stubs` is locked to 6.9.x after dependency refresh | Runtime compatibility with each supported WordPress version is manual-only. | +| WooCommerce | README requires WooCommerce 5.0+ | No full WooCommerce runtime in CI | `php-stubs/woocommerce-stubs` is locked to 10.x after dependency refresh | HPOS, Cart/Checkout blocks, and block themes require manual smoke testing. | +| Hellotext API | Existing plugin endpoints and payload contracts | HTTP calls are intercepted in tests | Not applicable | Real API authentication and dashboard ingestion require staging/production smoke testing. | +| Release package | `.github/workflows/release.yml` builds tag-triggered zip | Release workflow runs only on `v*` tags | Not applicable | Zip contents and installability must be checked after each tagged release. | + +## Recommendations + +1. Keep the HPOS declaration deferred until manual smoke testing passes with HPOS on and off. +2. Evaluate replacing `woocommerce_after_order_details` with a checkout-completion hook such as `woocommerce_thankyou` for classic checkout, while separately evaluating Checkout block coverage through Store API hooks. +3. Document classic-template limitations for `woocommerce_after_single_product`, `woocommerce_after_cart`, and any block-theme gaps in release notes until runtime coverage is confirmed. +4. Consider migrating cart diff state from `$_SESSION` to WooCommerce session storage (`WC()->session`) in a future compatibility-focused PR. +5. Continue using WooCommerce CRUD methods for all future order/refund metadata reads and writes.