Product
๐ Overviewโ
internal/api/v1/billing/services/product.service.js provides CRUD-style operations for Stripe products and prices. It powers catalogue management inside DashClicks, enabling teams to search, filter, add, archive, or delete products and associated pricing. The service centralises Stripe interactions, ensuring consistent pagination, metadata, and error handling.
Primary Controller: product.controller.js
๐ External Dependenciesโ
- Stripe Products API โ Create, retrieve, update, archive, and delete products.
- Stripe Prices API โ Manage recurring and one-time price entries.
- Stripe Files API โ Upload assets (e.g., product images) and optional file links.
- Stripe Subscriptions API โ Count active subscriptions per price when requested.
๐ง Key Behavioursโ
- Supports search-based pagination via
stripe.products.searchwhen a query is provided. - Differentiates
activevsarchivedstatus filters, translating them to Stripeactiveflags. - Harmonises price interval naming (
one_timeโone-time) for UI consistency. - Buckets prices into
archive/unarchivearrays for quick UI rendering. - Wraps Stripe connection failures with the standard
customerror helper.
๐ Data Flowโ
flowchart TD
UI[Catalogue UI] -->|GET/POST| Controller
Controller --> Service
Service -->|stripeAccount| Stripe
Stripe --> Products
Stripe --> Prices
Stripe --> Files
๐งฉ Functionsโ
Retrievalโ
getProducts({ status, search, limit, lastProductId, firstProductId, nextPage, type, currency, stripeBilling })- Lists products, applies optional search, and eagerly loads associated prices (up to 100).
- Returns cursor metadata,
has_more, and price buckets.
getFilters({ stripeBilling })- Auto-paginates Stripe products to compute counts for
ActivevsArchivedfilters.
- Auto-paginates Stripe products to compute counts for
getProduct({ productId, expand, stripeBilling })- Retrieves a single product, optionally expanding price arrays with subscription counts.
Mutationโ
addProduct({ productDetails, priceDetails, stripeBilling })- Creates a product (respecting requested status), then creates prices from payload.
addFiles({ file, purpose, fileLinkData, stripeBilling })- Reads file buffer, uploads via Stripe Files API, optionally creating file links.
addPrice({ productId, priceDetails, stripeBilling })- Adds a new price, supporting custom intervals when requested.
updateProduct({ productId, productDetails, priceDetails, stripeBilling })- Updates product metadata, adds or renames prices, and honours archive flags.
archiveProduct({ productId, active, stripeBilling })/archivePrice({ priceId, active, stripeBilling })- Toggles active state for products or prices.
updatePrice({ priceId, priceDetails, stripeBilling })- Performs partial updates on price metadata (e.g., nickname).
deleteProduct({ productId, stripeBilling })- Attempts deletion; converts Stripe dependency errors into a friendly archive suggestion.
๐งช Testing Notesโ
- Mock Stripe responses when unit testing
getProductsto cover search and pagination branches. - For end-to-end flows, verify price buckets and subscription counts when
expand = true.
โ ๏ธ Operational Considerationsโ
- Controllers should limit
limitto avoid hitting Stripe rate limits when expanding prices. - Custom interval pricing requires additional fields (
interval_type,interval_count) in payloads. - File uploads rely on
file.datacontaining a local path readable by the Node process.