AforoUpgradeCancel
Self-service plan upgrade / downgrade / cancel wizard with proration preview and deflection offers. Backed by the V50 cancellation feedback API for operator-side churn analytics.
What it renders#
A multi-step wizard letting a customer change or cancel their subscription without leaving your storefront. The widget runs in two modes set by the mode prop:
- upgrade — pick a target offering → see a prorated proration preview (credit, debit, net charge today, next-renewal amount) → confirm. Backend infers UPGRADE / DOWNGRADE / LATERAL from the offering price delta.
- cancel — pick a reason → see a category-specific deflection offer (20% discount, downgrade prompt, support hand-off, or no offer) → confirm with IMMEDIATE or PERIOD_END choice.
Both modes finish with a success state showing the invoice id, credit-note id, or scheduled effective date. The flow renders as a modal dialog by default and can be rendered inline via renderMode="inline".
Mount examples#
Props reference#
Events#
See the for the full vocabulary. UpgradeCancel emits 8 outbound events; all payloads are non-PII.
aforo.upgrade-cancel.completed event is advisory only. The authoritative signal for a plan change or cancellation is the server-side webhook (subscription.upgraded / subscription.cancelled). Use the event to update UI; never use it to grant or revoke entitlements in your own system.Upgrade flow#
- Step 1 — Pick target offering. The wizard fetches your published headless config and lists upgrade / downgrade / lateral candidates. The customer radios one.
- Step 2 — Review proration. The widget calls
POST /preview-changeand renders a credit / debit / net-cents card with a plain-English summary line generated by the backend. The IMMEDIATE / PERIOD_END toggle re-fetches the preview on change. - Step 3 — Confirm. Clicking Confirm calls
POST /upgradewith an Idempotency-Key header. Anti-double-submit ref guards against double-firing. - Success. The wizard shows the new offering name, status (ACTIVE or ACTIVE_PENDING_CHANGE), and the invoice id (IMMEDIATE) or scheduled-for date (PERIOD_END).
Cancel flow + deflection#
The cancel flow has a deflection step keyed on the customer's stated reason. Six canonical reasons are surfaced as Step 1 radio options. The deflection offer rendered in Step 2 is fixed per reason in Phase 1 — per-tenant customization is on the roadmap (see Limitations).
Step 3 (confirm) lets the customer pick IMMEDIATE or PERIOD_END. IMMEDIATE returns a credit-note id for the prorated refund. PERIOD_END returns the effective-at date (≈ current period end). Backend stamps every cancel through V50 with cancel_channel = EMBED so operator analytics can split self-serve embed cancels from in-app cancels.
aforo.upgrade-cancel.deflection_offer_accepted to your CRM to track retention wins by reason category. The payload carries the offer key + reason for downstream segmentation.IMMEDIATE vs PERIOD_END#
- IMMEDIATE — change applies now. Upgrades generate a prorated invoice today. Downgrades and cancellations generate a prorated credit note. Customer sees the change immediately in entitlements.
- PERIOD_END — change is scheduled for the current period end. Customer keeps current entitlements until then. No invoice or credit note is generated; the change applies when the renewal job runs.
Default is IMMEDIATE. Override with the defaultApplyAt prop to start the wizard on PERIOD_END (common for cancel flows where you want to give customers the rest of the period they've already paid for).
Common patterns#
Wiring deflection_offer_accepted to your CRM. The event fires once per accepted offer with the reason + offer key. Send it to your CRM as a retention event so customer-success can follow up.
When to use IMMEDIATE vs PERIOD_END. Default upgrades to IMMEDIATE (customers want their new entitlements now). Default cancels to PERIOD_END so customers retain access for what they've already paid for:
Try it in the sandbox. The embed sandbox mounts the live widget against synthetic offerings — flip the mode toggle to switch between upgrade and cancel flows.
Limitations#
- No
mode="both". Earlier drafts showed a shell value letting one widget render upgrade and cancel tabs. The contract was dropped pre-launch — render two widget instances with differentmodevalues instead. - Deflection offers are fixed per reason category. Phase 1 hardcodes the four offer types (20% discount / downgrade / support / no offer). Per-tenant customization (e.g. swap the 20% offer for a longer trial) is a Phase 2 follow-up.
- No
PAUSE_3_MONTHSoffer (FR-WIDGET-UC-5). The shape exists in the public retention-offer enum; the actual pause-flow wiring is Phase 2. - Refund preview at cancel-IMMEDIATE is the backend's number, not a UI preview. The widget shows the credit-note id and amount after the cancel completes; it doesn't render a refund-preview row at Step 2. Phase 1 limitation.
- No live SSE refresh. If the parent page leaves the wizard open long enough for the subscription to change underneath, the wizard doesn't auto-refresh — submit returns a typed error which the widget surfaces inline. SSE wiring is on the embed-events roadmap.
- Multi-step upgrade for plans with mandatory add-ons is Phase 2. Today the widget assumes the target offering is self-contained.