Subscriptions aren't a checkout feature - they're an operating model.
Most teams start subscription ecommerce with a simple goal: "charge the customer every month." The first production incident teaches you the real scope:
→ Request an audit · See our services
Renewals fail - expired cards, SCA challenges, insufficient funds.
Customers want to pause, skip, or swap items mid-cycle.
Inventory and fulfillment need to align with billing cycles, not just order dates.
Finance wants clean reconciliation: MRR, churn, refunds, proration.
Support needs visibility into what's active and why a renewal failed.
The platform landscape, stripped to what matters, looks like this:
Traditional ecommerce with subscription add-ons (Shopify + Recharge, for example). Billing platforms like Stripe Billing, Chargebee, or Recurly connected to your storefront. Or headless commerce stacks where you own the data model and workflows. MedusaJS fits in that third category.
This article is about building subscriptions in MedusaJS - in a way that stays flexible when the business inevitably evolves.
When Medusa is the right base for subscription commerce
We reach for Medusa when the project needs one or more of these:
Control over the subscription model - not just "subscribe and save" on a product page. Custom intervals, hybrid plans, metered usage.
A headless storefront - you want full control over UX and performance. React, Next.js, whatever frontend stack your team prefers.
Mixed business logic - one-time and recurring purchases, bundles, tiered B2B pricing, multi-region tax handling. All in one system.
No app tax - you own the code. No $200/month plugins for features you could build in a sprint.
If you need a no-code subscription setup by next Tuesday, Medusa isn't the fastest path. But if subscriptions are strategically important to the business, it's usually the better long-term bet.
Two real implementation paths (pick intentionally)
Medusa's architecture gives you two clean options for subscriptions. Both work. They optimize for different things.
Option 1: Build subscription logic inside Medusa
You create a Subscription module - data models, a service layer, lifecycle management - and wire it into Medusa's existing order and product systems using workflows and scheduled jobs.
This is the right choice when:
You need custom renewal rules or complex fulfillment logic (swaps, variable cadences, conditional shipments).
You want to stay payment-provider-agnostic. Stripe today, Adyen tomorrow.
Subscriptions affect fulfillment in ways that a billing platform can't model.
The tradeoff: you own the complexity. Renewal jobs, dunning logic, retry schedules, edge cases - all yours.
Option 2: Delegate billing to Stripe Subscriptions
Let Stripe handle the recurring charges and timing. Medusa manages catalog, order context, fulfillment, and the customer experience.
This works well when:
Stripe is your long-term payment provider and you're not planning to switch.
You want proration, invoices, and retry logic without building them from scratch.
The complexity is mostly billing, not unusual fulfillment rules.
The tradeoff: you're coupled to Stripe's subscription model. And note - Medusa's built-in Stripe provider handles payments, but not subscriptions end-to-end. You'll still write glue code and possibly a custom module for the subscription flows.
The evaluation checklist (answer before you build)
We run through these questions at the start of every subscription project. Getting them wrong means rewrites later.
What exactly is being subscribed to?
Physical goods shipped on a cadence? Digital entitlements (access, credits)? Services (appointments, maintenance windows)? Usage-based billing (metered API calls, storage)? Each type has different fulfillment, billing, and cancellation logic. Don't treat them the same.
Mixed cart: allowed or not?
If customers can buy one-time items and subscription items in the same checkout, you need to decide:
Do you split into two orders? Collect a one-time payment and create a subscription for the rest? How do taxes and shipping get allocated across the split?
Mixed-cart is where "simple subscriptions" get expensive fast. Many teams disallow it as a product decision - and that's a valid choice.
What happens when a renewal fails?
Define your dunning policy before you write code:
Retry schedule (we typically do 1 day, 3 days, 7 days). Grace period - does the customer keep access during retries? When do you auto-pause or auto-cancel? How do you notify the customer and let them update their payment method?
How does fulfillment align with billing?
For physical goods: do you bill first and then fulfill, or authorize and capture after inventory allocation? What's the cut-off time for changes before the next shipment? These decisions affect your entire order pipeline.
A Medusa-native subscription architecture that scales
Here's how we structure it when building subscriptions directly in Medusa.
Step 1: Create a Subscription module
This is your source of truth. Model a subscription explicitly:
subscription.id, customer_id, status (active, paused, canceled, expired, past_due), interval (week, month, quarter), next_renewal_at as the anchor date, optional end_date for fixed-term subscriptions, items (what's included), payment_method_reference (a token, never raw card details), shipping_address_snapshot, and metadata for things like plan name, cohort, or acquisition channel.
Step 2: Link subscriptions to Medusa's existing models
Subscription to Order - renewals create child orders. Subscription to Product - if the subscription is a product attribute. Subscription to Customer - for the portal and support visibility. These are standard Medusa module links. Nothing exotic.
Step 3: Checkout workflow creates order + subscription
When a customer subscribes: complete cart checkout (initial order), create the subscription record, set next_renewal_at, trigger initial fulfillment if applicable, emit domain events for email, CRM, and analytics.
Step 4: Scheduled renewal job
Run daily or hourly. Query subscriptions due for renewal. Create a renewal order or payment intent. Capture payment. On success: fulfill and advance next_renewal_at. On failure: mark past_due, schedule retries, notify the customer. This is the heart of your subscription system - test it heavily.
Step 5: Expiration job
If you support fixed-term subscriptions: detect when end_date has passed, mark as expired, stop fulfillment, revoke entitlements.
Step 6: Admin and customer portal
If customers can't self-serve, churn goes up and support tickets spike. Minimum viable portal: pause and cancel, update shipping address, swap items (if your model allows), update payment method. Medusa Admin is extensible with widgets and custom routes, so you add only what you need.
Stripe-first architecture: what to delegate, what to keep
If you go with Stripe Billing, be deliberate about the split.
Delegate to Stripe: recurring charge scheduling, invoices and proration, retry logic.
Keep in Medusa: catalog and subscription semantics ("what is this subscription?"), fulfillment rules (especially for physical goods), customer portal experience, internal reporting and business rules.
You'll map Stripe webhooks - subscription created, invoice paid, payment failed - into your Medusa-side subscription view. The webhook handler is the integration point; get it right and the two systems stay in sync.
The parts that make or break subscription commerce
"We'll add proration later"
Proration shows up immediately when customers change plans mid-cycle, when you offer trials, or when you adjust pricing. Decide up front: are mid-cycle changes allowed? Do you prorate now or apply the change next cycle? Deferring this decision means rewriting billing logic under pressure.
Inventory and cadence mismatch
For physical goods, what happens when an item is out of stock on renewal day? Skip the shipment? Substitute? Delay and notify? Each answer has different implications for billing, customer communication, and fulfillment ops.
Analytics can't come from orders alone
Orders tell you what shipped. They don't tell you MRR, churn rate, cohort retention, or payment failure recovery rate. You need subscription-level analytics from day one, not bolted on after the first board meeting.
Mixed-cart edge cases
Refunds on mixed carts, tax allocation, shipping cost splits, customer expectations about "one order" that's actually two - these get messy fast. If you're not sure, start by disallowing mixed carts and add them when you understand the edge cases.
Decision matrix
Choose Medusa-native logic if you need flexibility, complex fulfillment, or want to keep your options open on payment providers.
Choose Stripe Billing if Stripe is the long-term bet and your complexity is mostly billing and invoicing.
A solid hybrid - and what we recommend to most clients - is to use Stripe for billing but still model subscriptions in Medusa for operations, fulfillment, and future-proofing. You get Stripe's billing reliability without being locked into Stripe's view of what a subscription is.
Request a subscription commerce audit
If you're running subscriptions (or planning to) and want to avoid costly rewrites, we can review your current stack and produce a clear plan.
In an audit, we typically cover:
Subscription model and lifecycle - renewals, pauses, dunning. Mixed-cart and checkout architecture. Fulfillment alignment and inventory edge cases. Metrics setup - MRR, churn, and operational visibility.
If that sounds useful, get in touch via the contact form.