Skip to main content

Cart Management

The Cart Management submodule handles shopping cart operations, checkout orchestration, invoice preview generation, and promotional code management for the DashClicks Store platform.

API Endpoints Overview

MethodEndpointDescription
POST/v1/store/cartAdd item to cart
GET/v1/store/cartGet cart items with invoice preview
PUT/v1/store/cart/:idUpdate cart item quantity
DELETE/v1/store/cart/:idRemove item from cart
POST/v1/store/cart/checkoutProcess cart checkout
POST/v1/store/cart/single-purchaseDirect purchase without cart
POST/v1/store/cart/promoAdd promo code to cart
DELETE/v1/store/cart/promo/:idRemove promo code from cart

MongoDB Collections Used

Primary Collections

  • _store.cart - Shopping cart items and promo codes
  • _store.prices - Product pricing information
  • _store.products - Product catalog
  • _store.subscriptions - Active subscriptions for upgrades
  • _store.promo.codes - Promotional codes
  • crm.contacts - Business and person contact information
  • _accounts - Account information and settings
  • queues - Background job processing for fulfillment
  • _store.orders - Created orders during checkout
  • _store.subscription-feedback - Subscription cancellation feedback
  • projects-typeforms - Onboarding form requests
  • _loyalty.program.orders - Loyalty program order tracking

Core Cart Workflows

Add to Cart Flow

graph TD
A[Add Item Request] --> B[Validate Cart Limit 60 items]
B --> C{Is Bundle?}
C -->|Yes| D[Create Multiple Cart Items]
C -->|No| E[Create Single Cart Item]
D --> F[Check Duplicate Items]
E --> F
F --> G[Validate Business/Person Relationships]
G --> H{Is Software Product?}
H -->|Yes| I[Check for Existing Software Subscriptions]
H -->|No| J[Validate Service Availability]
I --> K{Has Conflicting Software?}
K -->|Yes| L[Return Error: Mutual Exclusivity]
K -->|No| M[Store Onboarding Preferences]
J --> M
M --> N[Save Cart Items]
N --> O[Return Created Items]

Checkout Processing Flow

graph TD
A[Checkout Request] --> B[Create Checkout Lock]
B --> C{Lock Created?}
C -->|No| D[Return Error: Concurrent Checkout]
C -->|Yes| E[Retrieve Cart Items]
E --> F{Has Items?}
F -->|No| G[Return Error: Empty Cart]
F -->|Yes| H[Create Sub-Accounts for New Businesses]
H --> I[Group Items by Billing Interval]
I --> J[For Each Billing Group]
J --> K{Has Existing Subscription?}
K -->|Yes| L[Upgrade Existing Subscription]
K -->|No| M[Create New Subscription]
L --> N[Apply Loyalty Coupons]
M --> N
N --> O[Attach Setup Fees to First Subscription]
O --> P[Create Order Records]
P --> Q[Send Onboarding Invites]
Q --> R[Queue Fulfillment Jobs]
R --> S[Clear Cart Items]
S --> T[Delete Checkout Lock]
T --> U[Return Created Subscriptions]

Service Methods & Functionality

Cart Operations

newCartItem(req, res, next) - Add item to cart

  • Purpose: Add product to shopping cart with validation
  • Parameters:
    • business (ObjectId): Business contact ID
    • person (ObjectId, optional): Person contact ID
    • price (ObjectId): Price ID to purchase
    • bundle (String, optional): Bundle identifier
    • external_action (Object, optional): Service-specific metadata
    • onboarding_preference (String, optional): Onboarding workflow preference
  • Business Logic:
    1. Cart Limit Validation: Enforces 60-item maximum per account
    2. Duplicate Check: Prevents adding same price for same business multiple times
    3. Business Validation: Verifies business exists and belongs to account
    4. Person Validation: If provided, validates person-business relationship
    5. Software Exclusivity: Prevents multiple software subscriptions (mutual exclusivity rule)
    6. Service Availability:
      • For listings: Validates available slots via external API
      • For websites: Checks domain availability
      • For phone numbers: Validates area code availability
    7. Bundle Handling: Creates multiple cart items with shared bundle_id UUID
    8. Onboarding Preferences: Stores form preferences for later processing
  • Returns: Created cart item(s) or array of items for bundles
  • Error Cases:
    • 400: Cart limit exceeded, duplicate item, invalid business/person
    • 404: Business or person not found
    • 409: Conflicting software subscription exists

getCartItems(req, res, next) - Retrieve cart with invoice preview

  • Purpose: Get aggregated cart items with Stripe invoice preview
  • Business Logic:
    1. Cart Aggregation: Calls aggregateCart() for complex data processing
    2. Invoice Preview: Retrieves Stripe upcoming invoice for each billing interval
    3. Promo Validation: Validates and expires invalid promo codes
    4. Loyalty Discounts: Applies account loyalty program benefits
    5. Bundle Merging: Combines bundle items into single line items with total quantities
    6. Totals Calculation: Computes subtotals, discounts, taxes, and final totals
  • Returns: Cart object with structure:
    {
    items: [...], // Cart items grouped by price
    subtotal: 0, // Item total before discounts
    setup_fee: 0, // One-time setup fees
    discount: 0, // Total discounts applied
    tax: 0, // Sales tax if applicable
    total: 0, // Final total after all adjustments
    promo_code: {...}, // Applied promo code details
    upcoming_invoices: [...] // Stripe invoice previews by interval
    }

updateCartItems(req, res, next) - Update cart item quantity

  • Purpose: Modify cart item quantity with validation
  • Parameters:
    • quantity (Number): New quantity (min 1)
  • Business Logic:
    1. Lock Check: Some items (upgrades, software) cannot change quantity
    2. Bundle Validation: Ensures bundle items maintain consistency
    3. Quantity Update: Updates all items in bundle if applicable
  • Returns: Updated cart item(s)

deleteCartItem(req, res, next) - Remove item from cart

  • Purpose: Delete cart item and associated bundle items
  • Business Logic:
    1. If item is part of bundle, deletes all bundle items
    2. If item is promo code, removes promo
    3. Single item deletion otherwise
  • Returns: Deletion confirmation

Checkout Operations

checkout(req, res, next) - Process complete checkout

  • Purpose: Execute full checkout flow with subscription creation

  • Parameters:

    • card (String, optional): Payment method ID for new payment method
  • Business Logic:

    1. Checkout Locking:

      • Creates lock document: {account_id, type: 'checkout'}
      • Prevents concurrent checkouts causing duplicate subscriptions
      • Lock automatically expires after processing
    2. Cart Retrieval:

      • Fetches all cart items for account
      • Validates cart is not empty
      • Groups by buyer (business contact)
    3. Sub-Account Creation:

      • For new businesses (accounts with pending: true)
      • Creates sub-account linked to parent
      • Generates affiliate code
      • Sets up account-business relationship
    4. Subscription Grouping:

      • Groups items by billing interval (monthly, quarterly, semi-annually, annually)
      • Creates separate subscription for each interval group
      • Ensures proper billing cycle management
    5. Stripe Integration:

      • Resolves Stripe account (platform vs connected)
      • Creates/updates customer in Stripe
      • Attaches payment method if provided
      • Creates subscription with line items:
        {
        price: priceId,
        quantity: itemQuantity,
        metadata: {
        business_id: businessId,
        person_id: personId,
        external_action: {...}
        }
        }
    6. Application Fees (for connected accounts):

      • Calculates wholesale amount from additional_info.wholesale_unit_amount
      • Adds percentage fees: ADDITIONAL_APP_FEE_PERCENTAGE + ADDITIONAL_APP_FEE_SUBSCRIPTION_PERCENTAGE
      • Applies to subscription: application_fee_percent
    7. Discount Application:

      • Applies loyalty program coupons if eligible
      • Applies promo codes from cart
      • Ensures coupon compatibility (Stripe limitations)
    8. Setup Fees:

      • Extracts one-time setup fees from prices
      • Creates invoice items attached to first subscription only
      • Prevents duplicate setup charges across intervals
    9. Order Generation:

      • Creates order record for each subscription
      • Links subscription → order → business
      • Stores product metadata and external actions
    10. Onboarding Triggers:

      • Sends onboarding invites if onboarding_preference provided
      • Creates typeform requests for product-specific forms
      • Queues onboarding notification emails
    11. Fulfillment:

      • Queues fulfillment jobs for each product type
      • Passes external_action metadata to queues
      • Triggers provisioning workflows
    12. Cleanup:

      • Deletes all cart items
      • Removes promo code from cart
      • Deletes checkout lock document
  • Returns: Array of created subscriptions with orders:

    [
    {
    subscription: {...}, // Stripe subscription
    order: {...}, // Created order record
    business: {...} // Business contact
    }
    ]

singlePurchase(req, res, next) - Direct purchase without cart

  • Purpose: Single-item purchase with immediate checkout
  • Parameters:
    • type (String): 'preview' | 'purchase'
    • business (ObjectId): Business ID
    • price (ObjectId): Price ID
    • subscription (ObjectId, optional): For upgrades
    • external_action (Object, optional): Service metadata
    • waive_setup (Boolean): Skip setup fees
  • Business Logic:
    1. Temporary Cart: Creates temporary cart item
    2. Invoice Preview: If type === 'preview', returns Stripe invoice preview
    3. Immediate Checkout: If type === 'purchase', executes full checkout
    4. Upgrade Handling: For existing subscriptions, prorates changes
    5. Phone Number Quantity: For phone products, calculates quantity via external API
    6. Cleanup: Removes temporary cart item after processing
  • Returns: Invoice preview object or completed purchase

Promo Code Operations

addPromoToCart(req, res, next) - Apply promo code

  • Purpose: Add promotional code to cart
  • Parameters:
    • promo_code (String): Stripe promotion code ID
  • Business Logic:
    1. Validates promo code exists in Stripe
    2. Removes existing promo code if present
    3. Creates cart document with type: 'promocode'
    4. Stores promo code reference
  • Returns: Success confirmation

deletePromoFromCart(req, res, next) - Remove promo code

  • Purpose: Remove promo code from cart
  • Returns: Deletion confirmation

Utility Methods

validatePromocode(promocodeId, stripe, customerId) - Validate promo code

  • Purpose: Check if promo code is valid and usable
  • Business Logic:
    1. Active Check: Verifies promo code active === true
    2. Coupon Expiry: Checks coupon not expired (expires_at)
    3. Redemption Limit: Validates times_redeemed < max_redemptions
    4. Customer-Specific: If promo has restrictions.first_time_transaction, validates customer eligibility
    5. Auto-Expiry: If expired, sets active: false in Stripe
  • Returns: true if valid, false if invalid

aggregateCart(account_id, req) - Complex cart aggregation

  • Purpose: MongoDB aggregation pipeline for cart data

  • Business Logic:

    1. Item Grouping:

      $group: {
      _id: {
      buyer: '$buyer',
      price: '$price'
      },
      quantity: { $sum: 1 },
      items: { $push: '$$ROOT' }
      }
    2. Price Lookup: Populates price details with product info

    3. Subscription Check: Determines if items are upgrades:

      $lookup: {
      from: '_store.subscriptions',
      localField: 'buyer',
      foreignField: 'business',
      as: 'existing_subscriptions'
      }
    4. Transaction Type: Sets new, upgrade, or downgrade

    5. Quantity Lock: Locks items that can't change quantity (software, upgrades)

    6. Subtotal Calculation:

      item_subtotal: price.unit_amount * quantity;
      setup_subtotal: price.setup_fee * quantity;
    7. Bundle Merging:

      • Groups items by bundle_name and bundle_id
      • Combines quantities: {bundle: 'name', total_quantity: 5, items: [...]}
    8. Stripe Invoice Preview:

      • Groups by billing interval
      • Calls Stripe for upcoming invoice per group
      • Merges discount and tax information
    9. Promo Validation:

      • Retrieves promo code from cart
      • Validates using validatePromocode()
      • Removes if expired
  • Returns: Aggregated cart object with all computed fields

phoneNumQuantity(params) - Calculate phone number quantity

  • Purpose: Query external API for phone number pricing
  • Parameters:
    • account_id, uid, parent_account
    • external_action: Contains area code and search params
    • price_id: Price to match
  • Business Logic:
    1. Generates JWT token for external API authentication
    2. Calls Twilio integration for available numbers search
    3. Matches price from pricing response array
    4. Returns unit_amount (price in cents) as quantity
  • Returns: Number quantity or null if not found

Technical Implementation Details

Checkout Locking Mechanism

Problem: Multiple concurrent checkout requests can create duplicate subscriptions

Solution: Database-level locking with cart documents

const lock = await Cart.findOneAndUpdate(
{ account_id, type: 'checkout' },
{ account_id, type: 'checkout', created_at: new Date() },
{ upsert: true, new: true },
);

if (!lock) {
throw new Error('Another checkout is in progress');
}

try {
// Process checkout
} finally {
await Cart.deleteOne({ _id: lock._id });
}

Bundle Handling

Bundles are groups of products sold together with shared identifier:

  1. Creation: Single API call creates multiple cart items

    const bundle_id = uuidv4();
    for (const priceId of bundlePrices) {
    await Cart.create({
    account_id,
    buyer,
    price: priceId,
    bundle_id,
    bundle_name: 'Bundle Name',
    });
    }
  2. Aggregation: Bundle items merged in display

    {
    bundle_name: 'Starter Pack',
    items: [
    { price: 'Website', quantity: 1 },
    { price: 'SEO', quantity: 1 }
    ],
    total_quantity: 2,
    total_amount: 29900
    }
  3. Deletion: Removing one bundle item removes all

Invoice Preview System

Multi-Interval Previews: Cart can have multiple billing intervals

const intervals = ['month', 'quarter', 'semi-annual', 'year'];
const previews = {};

for (const interval of intervals) {
const itemsForInterval = cartItems.filter(i => i.price.recurring.interval === interval);

if (itemsForInterval.length > 0) {
previews[interval] = await stripe.invoices.retrieveUpcoming({
customer: customerId,
subscription_items: itemsForInterval.map(i => ({
price: i.price.stripe_id,
quantity: i.quantity,
})),
coupon: promoCode?.coupon?.id,
});
}
}

Software Subscription Exclusivity

Rule: Account can only have one active software subscription

const existingSoftware = await Subscription.findOne({
account_id,
'product.type': 'software',
status: { $in: ['active', 'trialing'] },
});

if (existingSoftware && newItemType === 'software') {
throw new ApiError(409, 'Cannot add multiple software subscriptions');
}

Connected Account Routing

Platform vs Connected Accounts:

let stripe, application_fee_percent;

if (account.main) {
// Platform account
stripe = Stripe(process.env.STRIPE_SECRET_KEY);
application_fee_percent = 0;
} else {
// Connected account (white-label)
const stripe_keys = await StripeKey.findOne({
account_id: account.parent_account,
});
stripe = Stripe(stripe_keys.token.access_token);

// Calculate application fee
application_fee_percent = calculateAppFee(prices);
}

Error Handling

Common Error Codes

  • 400 Bad Request: Invalid parameters, cart limit exceeded, duplicate items
  • 404 Not Found: Business, person, price, or promo code not found
  • 409 Conflict: Conflicting software subscription, concurrent checkout
  • 422 Unprocessable: Invalid external_action metadata, service unavailable
  • 500 Server Error: Stripe API errors, database failures

Validation Patterns

// Cart item validation schema (Joi)
{
business: Joi.objectId().required(),
person: Joi.objectId().optional(),
price: Joi.objectId().required(),
bundle: Joi.string().optional(),
external_action: Joi.object().optional(),
onboarding_preference: Joi.string().valid('skip', 'send')
}

Integration Points

External Services

  • Stripe API: Subscriptions, invoices, customers, payment methods, promo codes
  • Twilio Integration: Phone number availability and pricing
  • Listing APIs: Business listing availability checks
  • Website Service: Domain availability validation

Internal Services

  • Queue Manager: Fulfillment job processing
  • Notification Service: Onboarding invite emails
  • Subscription Service: Subscription creation and management
  • Order Service: Order record generation
  • Account Service: Sub-account creation

PubSub Events

// Published after checkout
pubsub.publish('store:checkout:complete', {
account_id,
subscriptions: createdSubscriptions,
orders: createdOrders,
total_amount: checkoutTotal,
});

Performance Considerations

Optimization Strategies

  1. Aggregation Pipeline: Single database query for cart retrieval
  2. Batch Operations: Create multiple cart items in single transaction
  3. Cached Lookups: Price and product data cached during aggregation
  4. Concurrent Stripe Calls: Invoice previews fetched in parallel by interval
  5. Indexed Queries: Indexes on account_id, buyer, bundle_id, type

Database Indexes

// Cart collection indexes
{
account_id: 1,
buyer: 1,
price: 1
}
{
account_id: 1,
type: 1 // For checkout lock
}
{
bundle_id: 1 // For bundle operations
}
💬

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