import type { Product, StripePrice } from "@/lib/products/types" import Stripe from "stripe" import { getStripeClient } from "./client" import { filterProducts } from "@/lib/products/filters" /** * Fetch all active products from Stripe */ export async function fetchAllProducts(): Promise { const stripe = getStripeClient() try { // Fetch all products with their prices const products = await stripe.products.list({ active: true, expand: ["data.default_price"], limit: 100, }) // Map Stripe products to our Product format const mappedProducts: Product[] = [] for (const product of products.data) { // Get the default price let price: StripePrice | null = null let priceId: string | undefined if (product.default_price) { if (typeof product.default_price === "string") { priceId = product.default_price const priceData = await stripe.prices.retrieve(priceId) price = { id: priceData.id, unit_amount: priceData.unit_amount, currency: priceData.currency, type: priceData.type, active: priceData.active, } } else { price = { id: product.default_price.id, unit_amount: product.default_price.unit_amount, currency: product.default_price.currency, type: product.default_price.type, active: product.default_price.active, } priceId = price.id } } // Only include products with valid prices if (price && price.unit_amount !== null && price.active) { mappedProducts.push({ id: product.id, stripeId: product.id, name: product.name, description: product.description, price: price.unit_amount / 100, // Convert from cents to dollars currency: price.currency, images: product.images || [], metadata: product.metadata, priceId: priceId, }) } } // Apply filters to exclude service charges return filterProducts(mappedProducts) } catch (error) { console.error("Error fetching products from Stripe:", error) // Return empty array instead of throwing to prevent page crashes if (error instanceof Error && error.message.includes("STRIPE_SECRET_KEY")) { throw error // Re-throw configuration errors } return [] // Return empty array for other errors } } /** * Fetch a single product by Stripe ID */ export async function fetchProductById(id: string): Promise { const stripe = getStripeClient() try { const product = await stripe.products.retrieve(id, { expand: ["default_price"], }) if (!product.active) { return null } // Get the default price let price: StripePrice | null = null let priceId: string | undefined if (product.default_price) { if (typeof product.default_price === "string") { priceId = product.default_price const priceData = await stripe.prices.retrieve(priceId) price = { id: priceData.id, unit_amount: priceData.unit_amount, currency: priceData.currency, type: priceData.type, active: priceData.active, } } else { price = { id: product.default_price.id, unit_amount: product.default_price.unit_amount, currency: product.default_price.currency, type: product.default_price.type, active: product.default_price.active, } priceId = price.id } } if (!price || price.unit_amount === null || !price.active) { return null } const mappedProduct: Product = { id: product.id, stripeId: product.id, name: product.name, description: product.description, price: (price.unit_amount ?? 0) / 100, currency: price.currency, images: product.images || [], metadata: product.metadata, priceId: priceId, } // Check if product should be excluded const { shouldExcludeProduct } = await import("@/lib/products/config") if (shouldExcludeProduct(mappedProduct.name)) { return null } return mappedProduct } catch (error) { console.error(`Error fetching product ${id} from Stripe:`, error) return null } } /** * Create a new product in Stripe */ export async function createProductInStripe(productData: { name: string description?: string price: number currency?: string images?: string[] metadata?: Record }): Promise<{ product: Product; priceId: string } | null> { const stripe = getStripeClient() try { const currency = productData.currency || "usd" // Create the product first const stripeProduct = await stripe.products.create({ name: productData.name, description: productData.description || "", images: productData.images || [], metadata: productData.metadata || {}, active: true, }) // Create the price const price = await stripe.prices.create({ product: stripeProduct.id, unit_amount: Math.round(productData.price * 100), // Convert to cents currency, metadata: { ...productData.metadata, created_by: "admin", }, }) // Update product with default price await stripe.products.update(stripeProduct.id, { default_price: price.id, }) const mappedProduct: Product = { id: stripeProduct.id, stripeId: stripeProduct.id, name: stripeProduct.name, description: stripeProduct.description, price: (price.unit_amount ?? 0) / 100, currency: price.currency, images: stripeProduct.images || [], metadata: stripeProduct.metadata, priceId: price.id, } return { product: mappedProduct, priceId: price.id } } catch (error) { console.error("Error creating product in Stripe:", error) return null } } /** * Update an existing product in Stripe */ export async function updateProductInStripe( productId: string, updates: { name?: string description?: string price?: number images?: string[] metadata?: Record } ): Promise { const stripe = getStripeClient() try { // Update product fields const updateData: Stripe.ProductUpdateParams = {} if (updates.name !== undefined) updateData.name = updates.name if (updates.description !== undefined) updateData.description = updates.description if (updates.images !== undefined) updateData.images = updates.images if (updates.metadata !== undefined) updateData.metadata = updates.metadata const updatedProduct = await stripe.products.update(productId, updateData) // Update price if provided let priceId = typeof updatedProduct.default_price === "string" ? updatedProduct.default_price : updatedProduct.default_price?.id if (updates.price !== undefined && priceId) { const existingPrice = await stripe.prices.retrieve(priceId) const updatedPrice = await stripe.prices.create({ currency: existingPrice.currency, metadata: { ...existingPrice.metadata, ...updates.metadata, updated_at: new Date().toISOString(), }, product: updatedProduct.id, unit_amount: Math.round(updates.price * 100), }) await stripe.products.update(productId, { default_price: updatedPrice.id, }) priceId = updatedPrice.id } const mappedProduct: Product = { id: updatedProduct.id, stripeId: updatedProduct.id, name: updatedProduct.name, description: updatedProduct.description, price: updates.price || 0, currency: "usd", // You may need to fetch the actual currency images: updatedProduct.images || [], metadata: updatedProduct.metadata, priceId: priceId, } return mappedProduct } catch (error) { console.error(`Error updating product ${productId} in Stripe:`, error) return null } } /** * Deactivate a product in Stripe (soft delete) */ export async function deactivateProductInStripe( productId: string ): Promise { const stripe = getStripeClient() try { await stripe.products.update(productId, { active: false }) return true } catch (error) { console.error(`Error deactivating product ${productId} in Stripe:`, error) return false } } /** * Fetch prices for a specific product */ export async function fetchProductPrices( productId: string ): Promise { const stripe = getStripeClient() try { const prices = await stripe.prices.list({ product: productId, active: true, }) return prices.data.map((price) => ({ id: price.id, unit_amount: price.unit_amount, currency: price.currency, type: price.type, active: price.active, })) } catch (error) { console.error(`Error fetching prices for product ${productId}:`, error) return [] } }