Skip to content

fix: send payment_received receipt after manual payment confirmation and email verification #2055

Closed

Description

@superdav42

Goal

Send the payment_received system email (customer receipt and admin notice) in two cases that currently send nothing:

  1. A network admin confirms a pending manual payment.
  2. A free signup becomes active after its customer verifies their email address.

Evidence

Release 2.18.0 smoke test on a fresh dev network at origin/main c8b5ffe (2026-10-28), with mail captured by Mailpit:

  • Manual payment: Premium checkout with the Manual gateway left payment Welcome to WP Multisite WaaS Discussions! #1 and membership Welcome to WP Multisite WaaS Discussions! #1 pending, as expected. An admin then set the payment to Completed with "Activate membership?" on. The membership became active and the site was published, and "Your site is ready!" was sent. No payment_received email went to the customer or the admin. Event log rows for this step were changed payment and changed membership only.
  • Free signup with email verification: the thank-you page says "a receipt for your purchase has been emailed to you". After the customer followed the verification link, the site went READY and "Your site is ready!" was sent. No "Your free membership is active" / payment_received email was sent.
  • Both paths are unchanged since v2.17.2, so this is long-standing, not a regression.

How

  • payment_received fires only from Payment_Manager::handle_payment_success() (inc/managers/class-payment-manager.php:109-219), which listens to wu_gateway_payment_processed.
  • wu_gateway_payment_processed comes from Base_Gateway::trigger_payment_processed() (inc/gateways/class-base-gateway.php:901). The checkout calls it for free/trial orders only when email verification is not pending (inc/checkout/class-checkout.php:1074 and :1108). Gateways call it on gateway-confirmed payments.
  • Manual confirmation path: Payment_Edit_Admin_Page::handle_save() (inc/admin-pages/class-payment-edit-admin-page.php:1458+) activates the membership but never triggers payment processed.
  • Verification path: Customer_Manager::maybe_verify_email_address() / transition_customer_email_verification() (inc/managers/class-customer-manager.php:183, :245) publish the deferred free signup but never trigger payment processed.
  • Trigger it once in each path, with the payment's gateway (wu_get_gateway($payment->get_gateway())) or the manual/free gateway. Guard against duplicate events, for example by checking whether a payment_received event already exists for the payment, so a repeated save or verification can't send twice.

Files Scope

  • inc/admin-pages/class-payment-edit-admin-page.php
  • inc/managers/class-customer-manager.php
  • inc/managers/class-payment-manager.php
  • tests/WP_Ultimo/Managers/Payment_Manager_Test.php
  • tests/WP_Ultimo/Managers/Customer_Manager_Test.php

Verification

  • vendor/bin/phpunit --filter 'Payment_Manager_Test|Customer_Manager_Test'
  • vendor/bin/phpcs on the changed files.
  • Dev site with Mailpit: (1) a manual Premium checkout, then admin Completed + "Activate membership?", sends exactly one customer receipt and one admin notice; (2) a free signup with verification sends "Your free membership is active" once, after verification; (3) re-saving the completed payment sends nothing further.

aidevops.sh v3.39.49 plugin for OpenCode

CI Repair Feedback (from PR #2064)

The previous worker's PR #2064 had terminal failed CI checks. A head-bound
finalizer is routing this issue for redispatch. The next worker should address these failures.

Terminal failed checks

  • cypress (8.5, chrome): failure — check URL — primary failing-check evidence

Worker guidance

  1. Recover the previous PR branch/commits and continue that work; do not restart from scratch.
  2. Read every terminal check URL above and preserve the accumulated evidence.
  3. Rebase the recovered work onto current origin/main, then fix the code rather than weakening CI.
  4. Run every listed local check and create the replacement PR from the recovered branch.

Routed by deterministic merge pass (pulse-merge.sh).

In-place repair fallback

In-place repair attempt outcomes

  • Attempt 1: stage=head_fetch; result=launch_failed; failure_reason=pr_head_fetch_failed; next_action=verify_remote_branch_access_and_retry
  • Attempt 2: stage=head_fetch; result=launch_failed; failure_reason=pr_head_fetch_failed; next_action=verify_remote_branch_access_and_retry

CI Repair Feedback (from PR #2091)

The previous worker's PR #2091 had terminal failed CI checks. A head-bound
finalizer is routing this issue for redispatch. The next worker should address these failures.

Terminal failed checks

  • PHP 8.2: failure — check URL — primary failing-check evidence
  • PHP 8.3: failure — check URL — primary failing-check evidence
  • PHP 8.4: failure — check URL — primary failing-check evidence
  • PHP 8.5: failure — check URL — primary failing-check evidence

Worker guidance

  1. Recover the previous PR branch/commits and continue that work; do not restart from scratch.
  2. Read every terminal check URL above and preserve the accumulated evidence.
  3. Rebase the recovered work onto current origin/main, then fix the code rather than weakening CI.
  4. Run every listed local check and create the replacement PR from the recovered branch.

Routed by deterministic merge pass (pulse-merge.sh).

In-place repair fallback

  • Reason: the bounded PR-branch repair session exhausted its retry budget
  • Retry: re-run the deterministic merge pass after restoring access to branch feature/auto-20261106-135043-gh2055; keep PR GH#2055: send deferred payment receipts #2091 open until that retry is impossible.

In-place repair attempt outcomes

  • Attempt 1: stage=preparing; result=launch_interrupted; failure_reason=headless_launch_interrupted; next_action=retry_launch
  • Attempt 2: stage=preparing; result=launch_interrupted; failure_reason=headless_launch_interrupted; next_action=retry_launch

Activity

  1. locked as resolved and limited conversation to collaborators on Oct 28, 2026
  2. superdav42 commented on Oct 28, 2026

    @superdav42
    CollaboratorAuthor

    DISPATCH_CLAIM nonce=c650d6284a78a9b6292b688e1e7a1d00 runner=superdav42 ts=2026-10-28T02:14:11Z max_age_s=1800 version=3.39.49 opencode_version=1.18.31 lease_token=c650d6284a78a9b6292b688e1e7a1d00 device=device-1783824528-2609248-26808 session=issue-2055 phase=prelaunch expires_at=1793153773

  3. superdav42 commented on Oct 28, 2026

    @superdav42
    CollaboratorAuthor

    DISPATCH_LEASE phase=prelaunch lease_token=c650d6284a78a9b6292b688e1e7a1d00 device=device-1783824528-2609248-26808 session=issue-2055 expires_at=1793154119 ts=2026-10-28T02:14:29Z attempt_id=attempt:3101d893-7027-4a18-a627-b7c90274ed98

  4. added
    status:queuedWorker dispatched, not yet started
    and removed
    status:queuedWorker dispatched, not yet started
    on Oct 28, 2026
  5. superdav42 commented on Oct 28, 2026

    @superdav42
    CollaboratorAuthor

    Dispatching worker (deterministic).

  6. superdav42 commented on Oct 28, 2026

    @superdav42
    CollaboratorAuthor

    DISPATCH_LEASE phase=ready lease_token=c650d6284a78a9b6292b688e1e7a1d00 device=device-1783824528-2609248-26808 session=issue-2055 expires_at=1793160918 ts=2026-10-28T02:15:18Z attempt_id=attempt:3101d893-7027-4a18-a627-b7c90274ed98


    aidevops.sh v3.39.49 automated scan.

  7. 52 remaining items

  8. superdav42 commented on Nov 7, 2026

    @superdav42
    CollaboratorAuthor

    Dispatching worker (deterministic).

  9. superdav42 commented on Nov 7, 2026

    @superdav42
    CollaboratorAuthor

    DISPATCH_LEASE phase=ready lease_token=9ec43d30ec74eb211c6aa09751451443 device=device-1783824528-2609248-26808 session=issue-2055 expires_at=1794022458 ts=2026-11-07T01:34:18Z attempt_id=attempt:4777b665-95fd-4ea8-8397-2dd499ea078a


    aidevops.sh v3.39.129 automated scan.

  10. superdav42 commented on Nov 7, 2026

    @superdav42
    CollaboratorAuthor

    CLAIM_RELEASED reason=worker_complete runner=superdav42 ts=2026-11-07T01:46:03Z aidevops_version=3.39.129 opencode_version=1.18.31 claim_id=6700405135 nonce=9ec43d30ec74eb211c6aa09751451443 lease_token=9ec43d30ec74eb211c6aa09751451443 device=device-1783824528-2609248-26808 session=issue-2055


    aidevops.sh v3.39.129 plugin for OpenCode v1.18.31

  11. unlocked this conversation on Nov 7, 2026
  12. superdav42 commented on Nov 9, 2026

    @superdav42
    CollaboratorAuthor

    Completion Summary

    • What: Send payment_received receipts once after manual payment confirmation and verified free signup activation; preserve retryability and isolate gateway fixtures from suite-global registry resets.
    • Issue: fix: send payment_received receipt after manual payment confirmation and email verification #2055
    • Files changed: inc/admin-pages/class-payment-edit-admin-page.php, inc/managers/class-customer-manager.php, inc/managers/class-payment-manager.php, tests/WP_Ultimo/Managers/Customer_Manager_Test.php, tests/WP_Ultimo/Managers/Payment_Manager_Test.php
    • Testing: Runtime-verified in the WordPress multisite integration harness: vendor/bin/phpunit --filter 'Payment_Manager_Test|Customer_Manager_Test' passed 19 tests and 51 assertions through real models, database metadata, gateway hooks, and email-verification activation. PHPCS and PHP syntax checks also pass.
    • Key decisions: Use payment metadata plus a network-scoped named lock for once-only dispatch. Supply scoped gateway fixtures because Gateway_Manager_Test resets the singleton registry during the full suite.

    aidevops.sh v3.39.129 plugin for OpenCode v1.19.5 with gpt-5.6-sol spent 10m and 401,587 tokens on this as a headless worker.


    Merged via PR #2092 to main.
    Merged by deterministic merge pass (pulse-wrapper.sh).

    Routing feedback

    • Route: standard; 4 attempts, 197 LLM requests, 1 delegated child (1 thinking), 0 capability escalations, 2 same-tier fallbacks.
    • Population: headless, compaction; 0 objective outcomes.
    • Usage: 25,393,381 tokens; $13.2072 estimated cost.
    • Recommendation: Review same-tier candidate health and ordering; 2 routed attempts used non-primary candidates.
  13. added
    solved:workerTask was solved by a headless worker
    and removed
    status:in-reviewNon-draft PR ready for review/merge
    on Nov 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingorigin:workerAuto-created by pulse labelless backfill (t2112)solved:workerTask was solved by a headless workersource:ci-feedbackIssue carries CI failure feedback routed from a closed worker PRstatus:doneTask is completetier:standardAuto-created by pulse labelless backfill (t2112)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions