koala73 / koala73/worldmonitor

feat(payments): abandoned-checkout recovery — declined/3DS-abandoned prospects get zero follow-up (complement of #4799)

Open
#4,815 0 comments 0 reactions 0 assignees View on GitHub
area: API feature
Dominant language
TypeScript
Stars
86.6k
Forks
13.1k
Avg merge
8h 4m
Merged PRs (30d)
825

Description

## Problem

We have no recovery touchpoint for **prospects whose first checkout fails**. Dodo live data for Jun 24 – Jul 5 (most recent 100 payments):

- 68 payments succeeded; ~13 unique customers hit `failed` / `requires_customer_action` / `requires_payment_method` and **never converted** (in-window).
- 7 other customers failed at least once and later succeeded — the in-app retry banner (`src/components/checkout-failure-banner.ts`) recovers the users who are *still on the page*, but anyone who leaves gets nothing, ever.
- Notable walk-aways: a **$399.99/yr annual-plan prospect** (corporate email) abandoned 3DS verification twice on Jun 26; a **€105 prospect** declined Jun 30; roughly six $39.99–€44 monthly prospects declined Jun 30 – Jul 5.
- Failure reasons are issuer-side (`CARD_DECLINED`, abandoned 3DS) on a heavily international base (EUR/INR/CAD/ZAR/ETB…) — not a checkout bug. The Sentry signal is WORLDMONITOR-P1 ("Dodo checkout declined", intentional telemetry, baseline ~2/wk).

This is the *never-customer* complement to #4799 (win-back for **expired subscribers**) — neither it, #4807, nor the reconciliation PRs (#4794/#4795, entitlement-side) touch this cohort.

## Suggested shape (small, shares email infra with #4799)

1. **Recovery email on terminal checkout failure** (Dodo `payment.failed` webhook or a daily sweep of the payments list): delay ~2–6h, one-click resume link with the exact product they attempted (mirrors `LAST_CHECKOUT_ATTEMPT_KEY` semantics), max 1 email per prospect.
2. **Suppress** when the customer has any succeeded payment after the failure (the 7 self-recovered customers above must get nothing).
3. **Copy split**: card-declined ("try a different card") vs 3DS-abandoned ("finish verification — your bank needs a tap") — the two dominant modes in the data.

## Acceptance criteria

- [ ] A terminal `failed` checkout with no subsequent success triggers exactly one recovery email; a later success suppresses/ends the sequence (tests).
- [ ] Self-recovered customers (success after failure) never receive the email (test on the fail→succeed sequence).
- [ ] No email for `cancelled` returns (user chose to leave) — only `failed` / abandoned `requires_customer_action`.

https://claude.ai/code/session_019Ec6Arn7rfLnQrVj8qpjFm

Contributor guide

Open the contributing guide

Research direction

Start by reading src/components/checkout-failure-banner.ts and the LAST_CHECKOUT_ATTEMPT_KEY semantics, then inspect the email and checkout-recovery work referenced by #4799 and the Dodo payment events. Define the terminal-failure and fail→succeed cases before adding tests. Done means exactly one email for eligible failures, none after a later success or for cancelled returns, with separate declined and 3DS-abandoned copy.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
payments
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.