Cart recovery and payment assist

Two optional features that send shopper data to Conviro. Both are off by default and, from 1.4.0, each needs an explicit confirmation from you in addition to its switch. Plan open decision 5, answered as "hold until the merchant confirms" (09cfe93b). Code: AiChat/Observer/{CartSave,CheckoutStarted,PaymentFailure}.php, AiChat/Model/Config.php, AiChat/Setup/Patch/Data/HoldCartEventsUntilConfirmed.php, backend abandoned-cart.service.ts, cart-recovery-email-i18n.ts, commerce-attribution/payment-assist.service.ts, magento-event-ingest.service.ts. Updated 2026-09-25 for release gate R3 (ceaaac4d, ee07b4fc, 8791cdc4): payment assist is real, the recovery e-mail is localized and links to your own cart page.

Switching them on

Either:

A switch that is on without its confirmation sends nothing, and the Integration Health card shows a Cart & payment events Warning. Conviro's side follows the same pair: the cartRecovery / paymentAssist capabilities are the default-scope switch and its confirmation, sent with every registration.

What is sent

FeatureEventDataWhen
Abandoned Cart Recoverycart.updatedcart (quote) id, shopper e-mail, item count, grand total, currency, up to 10 items (name, SKU, quantity, price), the cart link (cartUrl)Magento saves the storefront cart (checkout_cart_save_after) for a cart with an e-mail and at least one item, in a selected store view.
Abandoned Cart Recoverycheckout.startedquote id, item count, total, currency, the e-mail only when already given, attribution idsThe shopper opens the checkout page; once per checkout state. Recorded only — never a message to the shopper.
Payment Failure Assistancepayment.failedcart id, shopper e-mail, payment method code, grand total, currency, a short error text (tags stripped, ≤ 300 characters), and — from a storefront request, with Revenue attribution on and the shopper's cookie consent where required — the chat visitor id and followed link ids. Never card data.Order placement fails (sales_model_service_quote_submit_failure).

In practice a cart has an e-mail when the shopper is logged in; whether a guest cart carries one depends on the checkout (not tested). Carts changed only through REST/GraphQL cart APIs (some headless storefronts) do not fire the Magento event the module listens to.

What Conviro does with it

Cart recovery

If the workspace's cart-recovery capability is on and the cart is worth at least 1:

  1. The cart is stored (magento_abandoned_carts: e-mail, cart id, item count, total, currency, items, the store view, its locale and the cart link) and a contact is found or created for the e-mail.
  2. One recovery e-mail per cart is scheduled 60 minutes after the last change to the cart — every cart event pushes it back, so nobody is e-mailed mid-checkout.
  3. An order.placed for the same e-mail cancels it; switching the capability off cancels it at send time; Disconnect cancels every scheduled one; a customer.deleted for the e-mail deletes it.
  4. Conviro's cron (every 5 minutes) sends due e-mails; a failed send is retried after 15 minutes, up to 3 attempts.

Language. The store view's locale from the store-view mapping the module sent in the setup wizard (else the event's own store.locale); if that is not a language Conviro ships, the language of the shopper's latest Conviro conversation (found by the cart's contact, a lead or the e-mail the conversation identified or captured, last 90 days); else English. Shipped: every language the chat answers in — en, tr, de, fr, es, it, pt, nl, pl, lt, et, ru, uk, ar, el, he, hi, th, ja, ko, zh (cart-recovery-email-i18n.ts, held to that set by the canned-copy coverage test). The subject, heading, button, note and plain-text body are translated; item names are shown as the store sent them (HTML-escaped).

Link. The button opens the cart link the module sent: the Cart page URL (headless stores) you set for the store view (Sync Settings, store view scope — for a PWA / headless storefront), otherwise the store view's own checkout/cart URL (with its store code / domain). Without either (a cart from a module before 1.4.0) it falls back to the store view's base URL from the mapping + /checkout/cart, then the connected store URL + /checkout/cart.

Payment assist

Each payment.failed is recorded in commerce_payment_failures: method code, total, currency, the error text with e-mail addresses and runs of six or more digits masked, the e-mail only as the keyed hash — kept 90 days. Then:

Measured: the module side on the local stack (payment-assist.sh, 12 checks — what is queued when, with and without consent); the backend on Postgres (payment-assist.pg.spec.ts: one offer under five parallel page views, withdrawn, expired, deleted conversation) and the widget page-view path (widget-pageview.service.spec.ts). Not yet run against the deployed backend.

Upgrading from 1.3.x — the confirmation hold

In 1.3.x these switches were read at default scope only, and the payment hook was never called by Magento's default checkout, so a switch turned on in 1.3.x never sent anything. In 1.4.0 they work per website and the payment hook fires — so upgrading would start sending cart contents and shopper e-mails at once.

Instead, setup:upgrade runs the data patch HoldCartEventsUntilConfirmed once: for each switch that is on at any scope, it writes the matching confirmation as 0 (unless a confirmation value already exists) and records the time in sync/confirmation_required_since. Result:

Measured on the local stack: the patch held an enabled cart switch and no cart event was queued until it was confirmed in step 5 (setup-wizard.sh).