Skip to content

feat(notifications): add order failed email notification#3188

Open
faisalahammad wants to merge 1 commit into
gocodebox:devfrom
faisalahammad:fix/3049-failed-order-notification
Open

feat(notifications): add order failed email notification#3188
faisalahammad wants to merge 1 commit into
gocodebox:devfrom
faisalahammad:fix/3049-failed-order-notification

Conversation

@faisalahammad

Copy link
Copy Markdown
Contributor

Summary

Adds a new order_failed email notification that fires when an order is marked as llms-failed. Covers both the initial checkout failure and the terminal recurring payment failure (after all retry rules are exhausted). Customer is on by default, product author and custom recipients are opt-in.

Fixes #3049

Changes

New: includes/notifications/controllers/class.llms.notification.controller.order.failed.php

New controller LLMS_Notification_Controller_Order_Failed that subscribes to two existing action hooks:

  • lifterlms_order_status_failed — fires on every transition into llms-failed. Covers initial checkout failure.
  • llms_automatic_payment_maximum_retries_reached — fires from LLMS_Order::maybe_schedule_retry() when retry rules are exhausted. Covers terminal recurring failure.

A per-request static cache (self::$sent) keyed by order ID prevents a double-send when both hooks fire for the same order in one request (e.g. when the terminal retry failure cascades into the llms-failed status transition).

Why: keeps the existing source files in LLMS_Controller_Orders and LLMS_Order untouched, reuses every existing abstract, and gives the trigger full admin UI (subject, body, merge codes, per-recipient opt-in) for free.

New: includes/notifications/views/class.llms.notification.view.order.failed.php

New view LLMS_Notification_View_Order_Failed with email body, subject, title, and a {{RETRY_NOTICE}} merge code that switches between "We have exhausted all automatic retry attempts..." for recurring orders and "No further payments will be attempted..." for one-time orders. Deleted-product guard on {{PRODUCT_TITLE_LINK}} falls back to the plain title.

Modified: includes/notifications/class.llms.notifications.php

Added 'order_failed' to the $triggers array in load(). Autoloader already maps the directories.

New: tests/phpunit/unit-tests/notifications/class-llms-test-notification-order-failed.php

Test class extending LLMS_NotificationTestCase. Covers test_is_registered, test_set_merge_data (six merge codes plus {{FAKE_CODE}} pass-through), and test_get_title.

New: .changelogs/order-failed-notification.yml

Standard changelog entry, significance: minor, type: added.

Testing

PHPUnit (run locally):

vendor/bin/phpunit --filter LLMS_Test_Notification_Order_Failed
vendor/bin/phpunit --group notifications

Result: 3 new tests pass, 14 assertions. The full notifications group (29 tests) is green except for one pre-existing incomplete test (LLMS_Test_Notification_Achievement_Earned::test_set_merge_data — unrelated to this change).

PHPCS:

vendor/bin/phpcs \
  includes/notifications/controllers/class.llms.notification.controller.order.failed.php \
  includes/notifications/views/class.llms.notification.view.order.failed.php \
  includes/notifications/class.llms.notifications.php \
  tests/phpunit/unit-tests/notifications/class-llms-test-notification-order-failed.php

Result: clean.

Manual end-to-end test (in TESTING_INSTRUCTIONS.md):

  1. Move any order to llms-failed from the admin order edit screen.
  2. The customer's email queue receives the new "Order Failed" notification.
  3. For recurring orders, when the last retry rule is exhausted, the customer gets one final "Order Failed" email with the retry-exhausted message.
  4. Toggling the Author / Custom subscribers in the settings screen works as expected.
  5. Disabling the notification in admin settings stops further sends.

Build

lifterlms-fix-3049.zip available for manual install:

/Users/faisal/Coding/GitHub/lifterlms-fix-3049.zip

WP Admin → Plugins → Upload Plugin → Install Now → Activate.

Coexistence with payment_retry

When a recurring payment finally fails after retries are exhausted, the site can send two related emails if both are enabled:

  1. The last payment_retry email from the retry sequence.
  2. The new order_failed email at terminal failure.

The terminal email is the one that matters and cannot be derived from the retry email (different merge codes, different message), so they are kept separate. To avoid duplicates, disable the email channel on payment_retry under LifterLMS → Settings → Notifications.

Adds a new `order_failed` notification trigger that sends an email to the
customer when an order is marked as failed. Covers both the initial
checkout failure and the terminal recurring payment failure that occurs
after all retry rules are exhausted.

Subscribes to two existing action hooks:
- `lifterlms_order_status_failed` (fires on every `llms-failed` transition)
- `llms_automatic_payment_maximum_retries_reached` (fires from
  LLMS_Order::maybe_schedule_retry() when retries are exhausted)

A per-request static cache in the controller prevents a double-send when
both hooks fire for the same order in a single request.

Fixes gocodebox#3049
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Awaiting Review

Development

Successfully merging this pull request may close these issues.

2 participants