(input: UpdateProductInput)
| 1651 | * Update product metadata (cannot change price amount - need to create new price) |
| 1652 | */ |
| 1653 | export async function updateProductMetadata(input: UpdateProductInput): Promise<BillingProduct | null> { |
| 1654 | if (!stripe) { |
| 1655 | logger.warn('Stripe not initialized - cannot update product'); |
| 1656 | return null; |
| 1657 | } |
| 1658 | |
| 1659 | try { |
| 1660 | // Build updated metadata |
| 1661 | const metadata: Record<string, string> = {}; |
| 1662 | if (input.category) metadata.category = input.category; |
| 1663 | if (input.displayName) metadata.display_name = input.displayName; |
| 1664 | if (input.customerTypes !== undefined) metadata.customer_types = input.customerTypes.join(','); |
| 1665 | if (input.revenueTiers !== undefined) metadata.revenue_tiers = input.revenueTiers.join(','); |
| 1666 | if (input.invoiceable !== undefined) metadata.invoiceable = String(input.invoiceable); |
| 1667 | if (input.sortOrder !== undefined) metadata.sort_order = String(input.sortOrder); |
| 1668 | |
| 1669 | // Update product |
| 1670 | const product = await stripe.products.update(input.productId, { |
| 1671 | metadata, |
| 1672 | ...(input.name ? { name: input.name } : {}), |
| 1673 | ...(input.description !== undefined ? { description: input.description } : {}), |
| 1674 | }); |
| 1675 | |
| 1676 | // Get the price to return full billing product |
| 1677 | const price = await stripe.prices.retrieve(input.priceId); |
| 1678 | |
| 1679 | logger.info({ |
| 1680 | productId: product.id, |
| 1681 | priceId: input.priceId, |
| 1682 | }, 'Updated product metadata in Stripe'); |
| 1683 | |
| 1684 | // Clear cache |
| 1685 | clearProductsCache(); |
| 1686 | |
| 1687 | return { |
| 1688 | lookup_key: price.lookup_key || '', |
| 1689 | price_id: price.id, |
| 1690 | product_id: product.id, |
| 1691 | product_name: product.name, |
| 1692 | display_name: product.metadata?.display_name || product.name, |
| 1693 | description: product.description, |
| 1694 | amount_cents: price.unit_amount || 0, |
| 1695 | currency: price.currency, |
| 1696 | category: product.metadata?.category || 'other', |
| 1697 | billing_type: price.recurring ? 'subscription' : 'one_time', |
| 1698 | billing_interval: price.recurring?.interval || null, |
| 1699 | customer_types: parseMetadataArray(product.metadata?.customer_types), |
| 1700 | revenue_tiers: parseMetadataArray(product.metadata?.revenue_tiers), |
| 1701 | is_invoiceable: product.metadata?.invoiceable === 'true' || !price.recurring, |
| 1702 | sort_order: parseInt(product.metadata?.sort_order || '0', 10), |
| 1703 | metadata: product.metadata || {}, |
| 1704 | }; |
| 1705 | } catch (error) { |
| 1706 | logger.error({ err: error, input }, 'Error updating product'); |
| 1707 | throw error; |
| 1708 | } |
| 1709 | } |
| 1710 |
no test coverage detected