Skip to main content

Active Stripe Subscriptions

Source: internal/api/v1/store/Controllers/stripe-subscription.js

Overview

The Stripe Subscription controller provides a specialized endpoint for listing active subscriptions with partner information, cost calculations, and discount analysis. Unlike the main subscription controller, this focuses on displaying active subscriptions with enriched partner and product data.

Key Capabilities

  • List active subscriptions with pagination
  • Enrich with partner (contact) information
  • Calculate actual costs with discount breakdown
  • Exclude void-paused and 100%-off subscriptions
  • Product and price population

MongoDB Collections

CollectionOperationsPurpose
_store.subscriptionsReadSubscription data
crm.contactsReadPartner/contact information
_store.productsReadProduct details
_store.pricesReadPrice information
_store.invoicesReadPhone number invoice amounts

Service Methods

getStripeSubscriptions

Lists active subscriptions with comprehensive data enrichment.

Endpoint: GET /store/stripe-subscriptions

Query Parameters:

{
page: number,
limit: number
}

Response:

{
success: true,
data: [{
id: string,
partner: {
name, phone, email, account
},
product: {
name, tier, interval, interval_count, amount, images
},
actual_cost: {
subtotal: number,
discount_amount: number,
amount_due: number,
percent_off: number,
amount_off: number,
interval_count: number,
interval: string
},
invoice_amount_due: number // For phone numbers
}],
pagination: {...}
}

Filtering:

{
customer: req.auth.account.stripe_customer,
status: 'active',
'discount.coupon.percent_off': { $ne: 100 }, // Exclude 100% off
'pause_collection.behavior': { $ne: 'void' } // Exclude void-paused
}

Business Logic

Partner Lookup

Joins subscriptions with CRM contacts using metadata.account_id or metadata.main_account_id:

{
$lookup: {
from: 'crm.contacts',
let: {
accountId: { $toObjectId: {
$ifNull: ['$metadata.account_id', '$metadata.main_account_id']
}}
},
pipeline: [{
$match: { $expr: { $eq: ['$account', '$$accountId'] }}
}]
}
}

Product/Price Population

  • Looks up product by plan.product (Stripe product ID)
  • Looks up price by price field (MongoDB ObjectId)
  • Populates product images and metadata

Cost Calculation

Base Subtotal:

const subTotal = (plan.amount || product.amount || 0) / plan.interval_count || 1;

With Discount:

let discountAmount = 0;
if (coupon.amount_off) {
discountAmount = coupon.amount_off / plan.interval_count;
} else if (coupon.percent_off) {
discountAmount = ((coupon.percent_off / 100) * subTotal) / plan.interval_count;
}

actual_cost = {
subtotal: subTotal,
discount_amount: discountAmount,
amount_due: Math.max(0, subTotal - discountAmount),
percent_off: coupon.percent_off || 0,
amount_off: coupon.amount_off || 0,
};

Phone Number Special Case:

For phone number subscriptions (metadata.action_type === 'phone_number'), uses invoice amount instead:

{
$lookup: {
from: '_store.invoices',
pipeline: [{
$match: {
$expr: {
$and: [
{ $eq: ['$stripe_id', '$$latestInvoice'] },
{ $eq: ['$metadata.action_type', 'phone_number'] }
]
}
}
}]
}
}

actual_cost.amount_due = invoice_amount_due || product.amount || subTotal;

Edge Cases & Business Rules

1. Exclusion Filters

100% Off Subscriptions: Excluded because they generate no revenue

Void-Paused: Excluded because subscription is effectively inactive

2. Partner Matching

Must have matching contact in CRM:

{
$match: {
'partner.0': { $exists: true }
}
}

Subscriptions without partner contacts are filtered out.

3. Tiered Billing

For tiered pricing:

amount: {
$cond: {
if: { $eq: ['$plan.billing_scheme', 'tiered'] },
then: '$quantity', // Use quantity for tiered
else: '$plan.amount'
}
}

4. Interval Count Normalization

Divides amounts by interval_count for consistent comparison:

// Monthly vs annual comparison
// $120 annual (interval_count=1) → $120
// $10 monthly (interval_count=1) → $10
// $120 annual billed semi-annually (interval_count=2) → $60

💬

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM