Stop Losing Hot Prospects: A Checkout Drop‑Off Playbook for Shopify Apps
A practical playbook for Shopify and SaaS apps to recover revenue from users who start checkout but don’t finish, using Spreeflo journeys, events, wait conditions, and multi-channel nudges to convert near-customers and measure recovered ARR.
Industry
Niche
Pattern
Loading sequence...
CartWizard’s founder knew something was off long before they could prove it.
Stripe showed a healthy stream of merchants clicking “Upgrade” from their free tier. Session recordings confirmed they were reaching the pricing page, picking a plan, and loading the payment screen. Then… nothing. No subscription created. No revenue. Just a quiet checkout_started with no matching payment.
For a $79/mo app, even ten of those drop‑offs a week is nearly $3,000 of annual recurring revenue walking out the door. That’s before you count expansion, referrals, and everything else that compounds from a paying account.
The sequence at the top of this page is the whole journey, end to end. It’s built to tackle exactly this problem: people who start your checkout, don’t finish, and never hear from you again.
This article walks through that journey node by node so you can adapt it to your own SaaS or Shopify app business.
Why checkout drop‑off is your highest‑intent leak
Most teams already think about cart abandonment.
But “cart abandoned” is a fuzzy signal. Someone added a product, got distracted, decided to research alternatives, or was never that serious in the first place.
Reaching the payment form is different. For your app:
They’ve hit your pricing page.
They’ve chosen a plan.
They’ve clicked into whatever checkout (Stripe, Braintree, Shopify Billing) you use.
They’ve started entering billing details.
At that point, this isn’t cold traffic. It’s a near‑customer.
When those people disappear, you’re not fighting awareness. You’re fighting friction:
A question they weren’t sure about (billing terms, feature limits, integrations).
A momentary trust wobble (card form design, domain mismatch).
A timing issue (meeting started, kid woke up, train arrived).
That’s exactly the kind of friction that a well‑timed, targeted nudge can fix. And it’s why this pattern is tagged with Brand Message 3: most businesses leak lifetime value by not nurturing engagement at their most important moments.
What you need in place before you build the journey
This playbook assumes you have two things wired into Spreeflo:
A
checkout_startedcustom event
Fired whenever someone opens your payment screen. This can come from your backend via the Spreeflo API, or from the browser using the JS SDK.A contact attribute that reflects purchase state
For example, a booleanis_paying_customeror a text attributesubscription_statusset to"active"after a successful checkout. Your billing layer should update this via API/SDK whenever a subscription is created or first payment clears.
If you haven’t added tracking yet, start with the web tracking and analytics guide. Once Spreeflo is on your site/app and you’re identifying users at email, you can send both the event and the subscription attribute.
With that in place, you’re ready to build a journey using the visual editor described on the campaigns and journeys page.
Journey overview: how the flow works
At a high level, the journey at the top of this page does five things:
Enrolls on a
checkout_startedCustom Event whenever a user hits your payment form.Waits up to an hour to see if they complete checkout on their own (by watching
is_paying_customer).Skips anyone who converts during that hour so they never see a recovery message.
Sends a focused recovery email to everyone else, only if they’re actually subscribed to marketing email.
Gives them 24 more hours to buy, then, if they still haven’t, sends a final, gentle web push reminder (Professional plan) before exiting.
Let’s walk that step by step in Spreeflo terms.
Trigger: a Custom Event when checkout is actually underway
The journey starts with a Custom Event trigger:
Event name:
checkout_startedRe-enrollment: on (
isReEnrollment = true)
Why Custom Event?
Because page views alone are noisy. You care about the specific moment they open your payment flow, which your app can emit as a single, precise event.
If your checkout has multiple stages (plan selection, company details, billing), configure the event so it only fires when the payment step loads. You can do that with an event property and property conditions on the trigger. For example:
Event:
checkout_startedProperty:
stepCondition:
step is "payment"
Re-enrollment is turned on so that if the same contact starts checkout again a month from now, the journey can run again. Spreeflo’s mid‑journey lock prevents duplicates: if they’re already mid‑recovery, a second checkout_started won’t double‑enroll them.
Step 2: Give them up to an hour to finish on their own
Immediately after the trigger, the flow uses a Wait Condition action:
Condition: contact attribute
is_paying_customer is trueTimeout: 1 hour
The logic: “Pause this contact until either they become a paying customer, or an hour has passed, whichever comes first.”
This is why that is_paying_customer (or subscription_status) attribute matters. Your billing system is the source of truth. The moment it flips that attribute via the API, Spreeflo sees it and the Wait Condition releases.
If they complete checkout in 5 minutes, they wait 5 minutes. If they never complete, they wait the full hour.
Why not send the recovery email immediately?
Because you don’t want to nag people who are still in flow, switching tabs to grab their card, or reading your terms. The hour buffer dramatically reduces “why are you emailing me, I’m literally paying” replies.
Step 3: Branch on who actually converted
Once the Wait Condition releases, the next node is an If/Else process with the same condition:
Condition:
is_paying_customer is true
Two branches:
Then branch (Yes): They’ve become a paying customer within the hour.
Else branch (No): They started checkout and are still not paying.
On the Yes branch, the sequence at the top simply applies an Add Tag action:
Tag:
new-customer(or whatever you use)
Then it ends.
You could also increment a numeric attribute like checkouts_started or checkouts_completed, but in most SaaS app cases you’re already recording that elsewhere. The important part is that they never see a recovery message if they convert during the one‑hour window.
On the No branch, the real recovery work begins.
Step 4: Only email people who are actually subscribed
The first thing the No branch does is check consent with another If/Else:
Condition: Email Subscription Status
is Subscribed
(This uses the “Email Subscription Status” filter in the segment builder.)
Two outcomes:
Subscribed: It’s safe to send marketing email.
Not subscribed: You must not send marketing email. You can still choose to use web push later, which has its own permission system.
This branch is where a lot of teams either spam or leave money on the table. If you skip it, you’ll try to email contacts who never opted in, which is both a deliverability and compliance problem.
In the journey:
The Subscribed branch flows into a Send Email node.
The Not subscribed branch skips the email and flows straight into a Merge node that recombines both paths.
The Merge exists so downstream steps (the 24‑hour wait and optional web push) can be shared without creating invalid graph fan‑in.
Step 5: Craft a focused recovery email (Subscribed branch)
On the Subscribed branch, the Send Email action is your main recovery tool. Configure it with:
A clear internal name like “Checkout recovery – 1 hour”.
Your usual sender identity.
“Send only once” left on, so re‑enrollments months later don’t accidentally get the exact same email twice.
Copy‑wise, a good checkout recovery email for a Shopify app or SaaS tool is short and specific:
Subject: “Still want to finish setting up CartWizard?”
Body:
- A single‑sentence reminder of the benefit they were buying (“CartWizard helps you recover abandoned carts automatically across email and SMS.”).
- A clear “Resume checkout” button linking directly back to the payment form with their selections prefilled.
- A quick reassurance (“No long‑term contracts. You can cancel anytime from inside your Shopify admin.”).
- A real reply‑to address inviting questions.
You can speed this up by using Spreeflo’s AI‑driven personalization from the AI variables guide to tailor subject lines or snippets based on plan, app use case, or store size.
Once the email is in place, both branches (Subscribed and Not subscribed) meet at the Merge node and move on together.
Step 6: Give them 24 hours, watching for purchases
From the Merge, the journey uses a second Wait Condition:
Condition:
is_paying_customer is trueTimeout: 24 hours (1 day)
This does two jobs at once:
It respects pacing rules, guaranteeing there’s at least a full day between the email and any subsequent web push.
It watches for late conversions, whether driven by the recovery email, a manual retry, or some other touch.
Some contacts will click the email and buy within minutes. Others might come back organically that evening. As soon as your billing system flips is_paying_customer to true, this Wait Condition releases.
After the wait, a second If/Else checks the same attribute again:
Yes branch (they’re now paying): They converted after the nudges.
No branch (still not paying): They’ve started checkout, ignored the email, and gone 24 hours without converting.
On the Yes branch, add an Add Tag action like:
Tag:
checkout_recovered
This tag is gold for reporting:
Build a segment of “Email subscription status is Subscribed AND tag is
checkout_recovered” to see how many accounts were rescued by this flow.Compare:
- Count of contacts in this segment vs. all paying customers.
- Their average MRR vs. average MRR overall.Estimate recovered revenue: number of recovered customers × their ARPU.
Even at small volumes, this compounds. Ten recovered $79/mo accounts over a year is >$9,000 in ARR. That’s the kind of “set it once, let it run” automation that justifies itself quickly for a lean team.
You can also use an Update Contact Attribute to set a static text like acquisition_source = "checkout_recovery" if you want another dimension in your BI tool.
Then end the Yes branch.
Step 7: A final, gentle nudge via web push
On the No branch (“still not paying after 24 hours”), the journey sends a last reminder using Send Web Push:
Template name: “Checkout recovery – web push”
“Send only once” optionally on, depending on whether you reuse this template elsewhere.
Content should be even tighter than the email:
Title: “Ready to finish setting up CartWizard?”
Body: One line on the value, e.g. “Turn more abandoned carts into revenue in under 10 minutes.”
Click URL: Deep link into your app, ideally straight back to the checkout or onboarding.
Because browser notifications require explicit permission, this step will only reach contacts whose browser has already granted web push for your domain. The rest silently no‑op.
Web push requires the Professional plan and a one‑time setup via the web push setup wizard. Once your service worker and opt‑in prompt are in place, this node becomes an easy extra touch.
After this push, the flow ends. At this point you’ve:
Given them a fair window to self‑convert.
Respected email consent.
Nudged twice (email, then push) without spamming.
Tagged recovered checkouts for revenue analysis.
Adapting the pattern for your own app
The skeleton above works for most SaaS / Shopify apps, but you can tune a few levers:
Change the thresholds by plan or value.
Add a Multi-way Split after the trigger that branches on cart value or plan type using contact attributes or event properties. High‑value plans might get:
- A shorter initial Wait Condition (e.g. 30 minutes instead of 1 hour by adjusting timeout).
- An extra Send Internal Email to your sales inbox whencheckout_startedfires, so a human can step in if needed.Run A/B tests on subject lines.
Insert a Random Split right before the Send Email action:
- 50% go to Version A.
- 50% to Version B.
Each branch has its own Send Email node with a different subject or CTA, but the downstream Wait Condition and web push remain shared via a Merge.Tighten or loosen time windows.
If your trial is short and urgency is high, shorten the second Wait Condition to 12 hours. If your buyers are typically in agencies juggling time zones, a full 48 hours might perform better.Use segments to exclude edge cases.
For example, wrap the trigger in a “Join Segment” pattern instead, where the segment defines “country is NOT test market” or “email does not contain@yourcompany.com”.
All of these changes use the same building blocks: Custom Event, Wait Condition, If/Else, Send Email, Send Web Push, Add Tag, Merge.
Measuring recovered revenue and proving this earns its keep
Because every recovered checkout path passes through that checkout_recovered tag (or equivalent attribute), it’s easy to quantify impact.
In your reporting, you can:
Build a segment:
tag is checkout_recovered AND is_paying_customer is true.Compare:
- Count of contacts in this segment vs. all paying customers.
- Their average MRR vs. average MRR overall.Estimate recovered revenue: number of recovered customers × their ARPU.
Even at small volumes, this compounds. Ten recovered $79/mo accounts over a year is >$9,000 in ARR. That’s the kind of “set it once, let it run” automation that justifies itself quickly for a lean team.
You can also use audiences on the platform overview to build cohorts like:
“Started checkout but never became paying” (to feed into win‑back later).
“Recovered by checkout flow” (for testimonials, beta invites, or referral programs).
The bigger idea: treat every “almost” as an automation moment
For founder‑led SaaS and Shopify apps, this checkout drop‑off flow is often the first time you see how much money you were silently leaking.
But the principle applies everywhere:
They installed your app but never used a key feature.
They upgraded to Pro but never invited teammates.
They hit your pricing page five times without starting a trial.
Each “almost” is a moment where a tiny bit of targeted engagement changes their trajectory and their lifetime value.
Spreeflo’s job is to make those nudges automatic: you define the events, attributes, and branches once, then the journeys keep working long after you’ve moved on to the next feature or partnership.
Plug this checkout recovery pattern into your stack, watch the recovered revenue line start to grow, and then ask the same question across the rest of your funnel: “Where else are people raising their hand… and then hearing nothing?”