Skip to content

feat(actionhandler): allow actions to opt out of window-closed monitoring - #274

Draft
roshan-gorasia-cko wants to merge 4 commits into
masterfrom
feat/apm-no-listen-window-closed
Draft

feat(actionhandler): allow actions to opt out of window-closed monitoring#274
roshan-gorasia-cko wants to merge 4 commits into
masterfrom
feat/apm-no-listen-window-closed

Conversation

@roshan-gorasia-cko

@roshan-gorasia-cko roshan-gorasia-cko commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What

Stops processout.js from raising a spurious customer.canceled ({ reason: "tab_closed" }) when a gateway (e.g. MercadoPago cash) transiently closes and reopens its redirect window/tab, driven by a no_listen_to_window_closed action-metadata flag.

Why

In the vanilla APM redirect, the hosted checkout page opens in a new tab. The ActionHandler that polls window.closed runs on the parent page, but the no_listen_to_window_closed metadata (from tessel9's CreatePayment FieldTV) is only seen by the checkout page in the child tab. So a handler that only reads the metadata locally never reaches the monitor that actually throws the error.

How

  • actionhandler.ts
    • New listenToWindowClosed option (default true) on ActionHandlerOptions. The 500ms monitor skips all window.closed detection when it's false. Explicit cancellation and postMessage-driven completion are unaffected.
    • listenEvents handles a new disable-window-close-monitoring checkout-namespace action by flipping options.listenToWindowClosed to false on the live monitor without terminating the flow (keeps waiting for the real success/cancel/error).
  • processout.ts (handleCardActions)
    • Reads customer_action.metadata.no_listen_to_window_closed === "true" (same mechanism as no_iframe).
    • Applies it to the local ActionHandlerOptions for the url / redirect flows.
    • When running inside the hosted-checkout tab (window.opener present), postMessages the opener the disable-window-close-monitoring action so the parent monitor stops treating the closed tab as a cancel.
  • Version bumped 1.9.91.9.10 for traceability via the ProcessOut-JS-Version header.

Deployment dependency

Both the merchant page and the hosted checkout page must run this build (≥ 1.9.10) — the parent needs the receiver, the child needs the sender. Backend flag comes from tessel9 processout/tessel9#7230 (scoped to mercadopagomxcash).

Testing

  • Staging: mercadopagomxcash cash payment, let the redirect page close/reopen the tab, confirm no premature customer.canceled. Verify ProcessOut-JS-Version: 1.9.10 in request headers.

🤖 Generated with Claude Code

roshan-gorasia-cko and others added 4 commits July 27, 2026 15:46
…ring

Some gateways (e.g. MercadoPago cash) transiently close and reopen the
redirect payment window/tab. The ActionHandler's 500ms monitor polls
window.closed and treats a closed window as a cancellation, firing a
spurious "customer.canceled" ({ reason: "tab_closed" }) that kills an
otherwise-valid payment.

Add a listenToWindowClosed option (default true) to ActionHandlerOptions.
When false, the monitor skips all window.closed detection and relies on
explicit cancellation / postMessage completion instead. Wire it from the
no_listen_to_window_closed customer_action metadata flag in the url and
redirect flows (same mechanism as the existing no_iframe flag).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bumps the version so the active bundle is traceable via the
ProcessOut-JS-Version request header during staging testing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The vanilla APM redirect opens the hosted checkout page in a new tab; the
ActionHandler polling window.closed lives on the *parent* page, while the
no_listen_to_window_closed metadata is only seen by the checkout page in
the *child* tab. So the previous handleCardActions-only wiring never
reached the handler that raises the spurious "customer.canceled".

- Child: when handleCardActions sees the flag and a window.opener exists,
  postMessage the opener with a new "disable-window-close-monitoring"
  checkout-namespace action.
- Parent: listenEvents handles that action by flipping
  options.listenToWindowClosed to false on the live monitor, without
  terminating the flow, so it keeps waiting for the real outcome.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diagnostics and mitigation for the "cancels as soon as the APM page
opens" race, where the redirect tab navigates to the gateway login page
before the checkout tab's disable-window-close-monitoring message is
received.

- debugLog helper: mirrors traces to console + telemetry (tagged
  apm-window-monitoring, carries SCRIPT_VERSION) at every decision point:
  window opened, popup-blocked, isCanceled cancel, closed-window cancel,
  every inbound checkout message (with elapsed timings), and the
  disable-monitoring send/receive.
- 2s startup grace window: a window-closed report (or access error)
  within the first 2s is logged but does not cancel, giving a late
  disable message time to land. Explicit user cancellation is unaffected.
- Bump version to 1.9.11.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant