Sales Controller
Path: internal/api/v1/funnels/controllers/sale.controller.js
Service: salesService
Module: Funnels
Overview
The Sales controller tracks and manages payment transactions from funnel checkout pages. It integrates with Stripe and Square payment providers to record sales, retrieve transaction details, and export sales data.
Key Capabilities
- Sales Tracking: Record all funnel payment transactions
- Transaction Details: Retrieve Stripe/Square charge information
- Sales Reporting: View sales by funnel with date filtering
- Charge Management: Update charge metadata and notes
- Sales Export: Export sales data as CSV
Methods
getSales()
Retrieves all sales for a funnel with date filtering and pagination.
Route: GET /v1/funnels/:id/sales
Auth: Required (account-scoped)
Request Parameters
{
id: string; // Funnel ID
}
Request Query Parameters
{
start_date?: string; // Filter from date (ISO 8601)
end_date?: string; // Filter to date (ISO 8601)
limit?: number; // Results per page (default: 10)
page?: number; // Page number (0-indexed)
}
Response
{
"success": true,
"message": "SUCCESS",
"data": [
{
"_id": "60d5ec49f1b2c72e8c8e4b50",
"funnel_id": "60d5ec49f1b2c72e8c8e4b1a",
"step_id": "60d5ec49f1b2c72e8c8e4b1e",
"charge_id": "ch_3MmlLr2eZvKYlo2C0b7lzxVZ",
"amount": 99.99,
"currency": "usd",
"status": "succeeded",
"payment_method": "stripe",
"customer": {
"id": "cus_NZlw7J6RdPXJ4Y",
"email": "customer@example.com",
"name": "John Doe"
},
"metadata": {
"product_name": "Premium Plan",
"quantity": 1
},
"created_at": "2024-01-22T14:30:00Z",
"updated_at": "2024-01-22T14:30:05Z"
},
{
"_id": "60d5ec49f1b2c72e8c8e4b51",
"funnel_id": "60d5ec49f1b2c72e8c8e4b1a",
"step_id": "60d5ec49f1b2c72e8c8e4b1e",
"charge_id": "sq0idp-xyz789",
"amount": 149.99,
"currency": "usd",
"status": "succeeded",
"payment_method": "square",
"customer": {
"id": "CUSTOMER_ID_123",
"email": "jane@example.com",
"name": "Jane Smith"
},
"metadata": {
"product_name": "Enterprise Plan",
"quantity": 1
},
"created_at": "2024-01-22T15:45:00Z",
"updated_at": "2024-01-22T15:45:10Z"
}
],
"pagination": {
"total": 142,
"page": 1,
"limit": 10,
"pages": 15
}
}
MongoDB Collections
| Collection | Operation | Purpose |
|---|---|---|
funnel_sales | find with date range | Query sales transactions |
funnels | findOne | Verify funnel ownership |
getSale()
Retrieves detailed information for specific sale transactions.
Route: GET /v1/funnels/:id/sales/:step_id
Auth: Required (account-scoped, Stripe config required)
Request Parameters
{
id: string; // Funnel ID
step_id: string; // Step ID where sale occurred
}
Request Query Parameters
{
charges: string; // Comma-separated charge IDs
}
Response
{
"success": true,
"message": "SUCCESS",
"data": {
"charges": [
{
"id": "ch_3MmlLr2eZvKYlo2C0b7lzxVZ",
"amount": 9999,
"amount_captured": 9999,
"amount_refunded": 0,
"currency": "usd",
"status": "succeeded",
"paid": true,
"refunded": false,
"customer": {
"id": "cus_NZlw7J6RdPXJ4Y",
"email": "customer@example.com",
"name": "John Doe",
"phone": "+1 555-0123"
},
"payment_method_details": {
"type": "card",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2025,
"funding": "credit"
}
},
"billing_details": {
"address": {
"city": "San Francisco",
"country": "US",
"line1": "123 Main St",
"postal_code": "94102",
"state": "CA"
}
},
"receipt_url": "https://pay.stripe.com/receipts/...",
"created": 1674403800,
"metadata": {
"funnel_id": "60d5ec49f1b2c72e8c8e4b1a",
"step_id": "60d5ec49f1b2c72e8c8e4b1e",
"product_name": "Premium Plan"
}
}
],
"total_amount": 99.99,
"total_refunded": 0.0,
"net_amount": 99.99
}
}
External API Integration
- Calls Stripe API:
stripe.charges.retrieve(charge_id) - Uses account's Stripe API key from
req.auth.stripe_config.token - Returns raw Stripe charge objects with full details
updateSale()
Updates sale metadata and notes.
Route: PUT /v1/funnels/:id/sales/:step_id
Auth: Required (account-scoped, Stripe config required)
Request Parameters
{
id: string; // Funnel ID
step_id: string; // Step ID
}
Request Body
{
"charge": {
"id": "ch_3MmlLr2eZvKYlo2C0b7lzxVZ",
"metadata": {
"order_number": "ORD-12345",
"fulfillment_status": "pending",
"notes": "Rush delivery requested"
}
}
}
Response
{
"success": true,
"message": "SUCCESS",
"data": {
"id": "ch_3MmlLr2eZvKYlo2C0b7lzxVZ",
"metadata": {
"funnel_id": "60d5ec49f1b2c72e8c8e4b1a",
"step_id": "60d5ec49f1b2c72e8c8e4b1e",
"product_name": "Premium Plan",
"order_number": "ORD-12345",
"fulfillment_status": "pending",
"notes": "Rush delivery requested"
},
"updated": true
}
}
External API Integration
- Calls Stripe API:
stripe.charges.update(charge_id, { metadata }) - Updates charge metadata in Stripe system
- Does NOT modify amount or payment status
exportSales()
Exports sales data as CSV file.
Route: GET /v1/funnels/:id/sales/export
Auth: Required (account-scoped)
Request Parameters
{
id: string; // Funnel ID
}
Request Query Parameters
{
start_date?: string; // Filter from date (ISO 8601)
end_date?: string; // Filter to date (ISO 8601)
}
Response
- Content-Type:
text/csv - Filename:
Sales-2024-01-22-19-30-00.csv
CSV Format
Date,Transaction ID,Customer Name,Customer Email,Amount,Currency,Status,Payment Method,Product
2024-01-22 14:30:00,ch_3MmlLr2eZvKYlo2C0b7lzxVZ,John Doe,customer@example.com,99.99,USD,succeeded,stripe,Premium Plan
2024-01-22 15:45:00,sq0idp-xyz789,Jane Smith,jane@example.com,149.99,USD,succeeded,square,Enterprise Plan
MongoDB Collections
| Collection | Operation | Purpose |
|---|---|---|
funnel_sales | find with date range | Query all sales for export |
Data Models
Sale Document
{
_id: ObjectId;
funnel_id: ObjectId;
step_id: ObjectId;
account_id: ObjectId;
// Payment provider details
payment_method: 'stripe' | 'square' | 'paypal';
charge_id: string; // Provider's transaction ID
// Transaction details
amount: number; // Amount in dollars (e.g., 99.99)
currency: string; // ISO currency code (e.g., "usd")
status: 'succeeded' | 'pending' | 'failed' | 'refunded';
// Customer info
customer: {
id: string; // Provider's customer ID
email?: string;
name?: string;
phone?: string;
};
// Product/metadata
metadata?: {
product_name?: string;
product_id?: string;
quantity?: number;
order_number?: string;
fulfillment_status?: string;
notes?: string;
[key: string]: any;
};
// Attribution
visitor_id?: string; // Analytics visitor ID
utm_source?: string;
utm_medium?: string;
utm_campaign?: string;
// Timestamps
created_at: Date; // When sale was recorded
updated_at: Date; // Last metadata update
payment_date: Date; // Actual payment timestamp
}
Integration Points
Stripe Integration
- Purpose: Process credit card payments
- Configuration: Account-level Stripe API key (
req.auth.stripe_config.token) - Operations: Retrieve charges, update metadata
- Webhooks: Record sales from Stripe webhook events
Square Integration
- Purpose: Process credit card payments (alternative to Stripe)
- Configuration: Account-level Square access token
- Operations: Retrieve orders, update order metadata
- Webhooks: Record sales from Square webhook events
Business Logic & Workflows
Sale Recording Flow
sequenceDiagram
participant Checkout
participant Stripe
participant Webhook
participant Sales
participant Database
Checkout->>Stripe: Process payment
Stripe-->>Checkout: Payment succeeded
Stripe->>Webhook: payment_intent.succeeded event
Webhook->>Sales: Record sale
Sales->>Database: Insert sale document
Database-->>Sales: Sale recorded
Sales-->>Webhook: Success
Sale Export Flow
sequenceDiagram
participant User
participant Sales
participant Database
participant CSV
User->>Sales: GET /sales/export
Sales->>Database: Query sales (date range)
Database-->>Sales: Sale records
Sales->>CSV: Generate CSV
CSV-->>Sales: CSV data
Sales-->>User: Download CSV file
Performance Considerations
Pagination
- Default limit: 10 sales per page
- Uses skip/limit for efficiency
- Total count calculated once per query
Date Range Queries
- Indexed by
created_atfield - Optimized for recent date ranges (last 30/90 days)
- Full export uses streaming for large datasets
External API Calls
- Stripe API calls cached for 5 minutes
- Batch charge retrieval when possible
- Rate limiting: 100 requests/second (Stripe limit)
Security
Multi-Tenant Isolation
- All queries filtered by
account_id(via funnel ownership) - Stripe API key scoped to account
- Cannot access other accounts' sales data
Sensitive Data Handling
- Full card numbers never stored (only last4)
- CVV never stored
- PCI compliance through Stripe/Square
API Key Security
- Stripe keys stored encrypted in database
- Never exposed in API responses
- Validated before each external API call
Error Handling
Common Errors
// No Stripe configuration
{
"success": false,
"message": "Stripe configuration required",
"statusCode": 400
}
// Charge not found
{
"success": false,
"message": "Charge not found",
"data": {
"charge_id": "ch_invalid"
},
"statusCode": 404
}
// Stripe API error
{
"success": false,
"message": "Stripe API error: invalid_request_error",
"data": {
"stripe_error": "No such charge: ch_invalid"
},
"statusCode": 500
}
// Invalid date range
{
"success": false,
"message": "start_date must be before end_date",
"statusCode": 400
}
Best Practices
When to Use This Controller
- ✅ Building sales dashboards
- ✅ Tracking funnel revenue
- ✅ Exporting transaction data
- ✅ Managing order fulfillment
- ✅ Analyzing payment performance
Common Patterns
- Date range filtering: Default to last 30 days
- Metadata updates: Use for order management (fulfillment, tracking)
- Export for accounting: Regular CSV exports for financial records
- Charge retrieval: Fetch full details only when needed (lazy loading)
Edge Cases
- Refunds: Sale document persists but status updated to "refunded"
- Partial refunds: metadata tracks refund amount
- Failed payments: Recorded with status "failed" for analytics
- Multiple charges: Query parameter accepts comma-separated IDs
Related Documentation
- Funnels Controller: Funnel checkout pages
- Stripe Webhooks: Automated sale recording
- Square Webhooks: Alternative payment recording
- Analytics Controller: Revenue attribution and metrics
Last Updated: January 2025
API Version: v1
Maintained By: DashClicks Engineering