The brief was simple: add a 7-day free trial to a fitness app sold through Shopify. Monthly ($19.99/month) and yearly ($179.88/year) plans. Customer tries the app free, gets charged automatically on day 8.
The client already had Recharge handling subscriptions. The plan was to add a free trial on top of the existing setup without rebuilding everything from scratch. What followed was five bugs, a cascade of workarounds, and eventually a full switch to Stripe. This is the full post-mortem.
The architecture we built (and why)
Recharge doesn't have a native "free trial" field you can attach directly to a selling plan on an existing product - or at least, not reliably on all account tiers. So the approach, following guidance from Recharge's own documentation and support, was a placeholder product architecture:
- Create a separate "Free Trial" product in Shopify priced at $0
- Customer subscribes to the Free Trial product at checkout - $0 charge, subscription starts
- After 7 days, a Recharge Automate flow runs and swaps the trial subscription for the real paid subscription
- Customer is charged $19.99/month or $179.88/year from that point forward
On paper, reasonable. In practice, a cascade of problems - each one a consequence of the placeholder design, not an isolated bug.
Bug 1: The trial plan was set up as prepaid
The first issue appeared immediately during testing. The Free Trial selling plan had been configured as "Prepaid, 2-week" instead of a standard subscription. This caused a frequency mismatch when the Automate flow tried to convert the trial to the real plan - Recharge flagged a warning and the conversion didn't complete cleanly.
Fix: Updated the Free Trial selling plan to:
- Type: Subscription (monthly)
- Expire after 1 charge
This meant the $0 charge fires once on day 0, the automation runs on day 7, and the trial subscription expires. No repeat $0 charges.
Bug 2: The automation re-triggered on every $0 charge
Before the fix above, the original plan was set to recur, so the automation kept firing every time a new $0 charge processed. One test customer had two automation runs in the activity log - Recharge had tried to add a paid subscription twice.
The "expire after 1 charge" fix resolved this going forward, but any customers who had signed up before the fix were already in a broken state - subscribed to two things, neither of them the intended outcome.
Bug 3: The portal showed "no active subscription" right after checkout
After fixing the selling plan, a new problem surfaced: customers landing on the Recharge customer portal immediately after checkout saw "no active subscriptions".
The cause: "Expire after 1 charge" was being applied too aggressively on one plan variant. The subscription was being marked complete at the moment of the $0 charge instead of staying active for 7 days.
The fix was to verify that "Cancel after X charges" was fully disabled on both the monthly trial plan and the yearly trial plan - not just one.
Bug 4: The automation converted yearly customers to monthly
The original Automate flow had one path: convert the free trial subscription to the paid plan at $19.99/month. Fine for monthly customers. Not fine for yearly - after 7 days, someone who signed up for $179.88/year would get charged $19.99/month instead.
The fix was a conditional branch in the Automate flow:
- Branch 1:
Subscription → Is Prepaid → Equals → True→ swap to paid plan (yearly) - Branch 2:
Subscription → Is Prepaid → Equals → False→ swap to paid plan (monthly)
Recharge automatically carries over billing frequency from the trial to the real plan, so as long as both branches used Swap (not Add), the correct price would apply.
Bug 5: Branch 1 was set to "Add" instead of "Swap"
After the conditional branch was built, testing found that Branch 1 was configured as "Subscription Add" instead of "Subscription Swap". This meant yearly customers ended up with two active subscriptions - the $0 trial running indefinitely and the real paid plan. They'd be charged for both.
Branch 2 was correctly set to Swap. Branch 1 was not. Once both branches used Swap, the automation ran correctly end to end.
The UX problems that weren't fixable
Even after all five bugs were resolved, the setup had display problems that could not be fixed given the placeholder architecture.
The portal showed the wrong product during the trial
Because the customer is technically subscribed to the Free Trial placeholder product (not the real paid product), the Recharge customer portal showed:
- Product name: "Free Trial" - not the actual app name
- Next order date: 1 month or 12 months from signup - the trial plan's natural billing cycle, which would never be reached
- For yearly trial customers: "Renews [date one year from now]" - technically accurate from Recharge's perspective, completely wrong from a customer's
Shopify checkout showed "$0.00 recurring every month"
At checkout, Shopify displayed:
Recurring subtotal: $0.00 every month
Because that is literally what the customer is subscribing to - the $0 Free Trial product. Shopify has no idea about the swap happening on day 7. A customer expecting "free for 7 days, then $19.99/month" instead sees a $0 recurring charge. It's confusing, and on a fitness app purchase it raises enough doubt to kill conversions.
The root cause of every UX problem here is the same: the customer is subscribed to the wrong product for 7 days. Every portal display, checkout summary, and automated email describes the placeholder - not the real product. There is no way to make this feel native while the placeholder architecture is in place.
The clean architecture we never got to use
The correct way to build a free trial is to attach it directly to the real product's selling plan:
- Customer subscribes to the real paid product at checkout
- The selling plan has a built-in 7-day first-charge delay
- Portal shows the real product as active from day 0
- Next charge date is 7 days from signup
- No automation, no swap, no placeholder product
Recharge does support this natively on some account tiers. I spent significant time trying to get a definitive answer from their support on whether it was available for this specific account configuration. I never got a clear one. That ambiguity - combined with the accumulated workarounds - is what ultimately moved the decision toward switching tools entirely.
What we moved to
After exhausting the Recharge path, the decision was to move subscription management to Stripe. Stripe's free trial is a first-class feature: set trial_period_days: 7 on a subscription price object, and the trial is native to the real product from day one. No placeholder. No automation flow. No swap.
The new architecture:
- Shopify handles the marketing site and landing pages only - no products, no cart
- Stripe handles subscriptions, billing, the 7-day trial, customer portal, and cancellations
- Zapier connects Stripe subscription events to the app delivery platform for automatic onboarding and offboarding
The checkout experience now shows exactly what it is. The customer portal shows the real product, the real next charge date, and the real billing amount. No workarounds.
What this cost in real time
The debugging work on the Recharge path - finding each bug, testing the fix, finding the next bug - ran to several days of back-and-forth. The Stripe migration, by contrast, was straightforward: Stripe's documentation is clear, the trial feature is documented and supported, and the architecture has no moving parts to debug.
The total time spent on Recharge workarounds far exceeded what a Stripe setup would have cost from the start.
Lessons
1. Workaround architectures compound. Every bug in this project was a consequence of the placeholder product design, not an independent failure. Each fix introduced new edge cases. By bug five, I was debugging consequences of fixes for consequences of workarounds.
2. Verify platform capabilities before choosing a tool. I assumed Recharge could handle a native free trial. It may be able to on certain plans. That assumption should have been verified in hour one - before any build work started. "Confirm the platform supports this natively" is now the first step of any subscription project scoping.
3. "It works technically" is not the same as "it works for users." The automation eventually ran correctly end to end. But the customer portal still showed the wrong product, the wrong dates, and the wrong price during the trial window. A technically correct but customer-confusing implementation is not a finished product.
4. When a workaround requires fighting the tool at every step, that's the signal to switch - not to add another layer. The moment the fifth bug appeared, the right call was to stop patching and re-evaluate the tool choice. The cost of switching earlier would have been lower than the cost of continuing.