import type Stripe from "stripe" import * as z from "zod" import { defineAction } from "../../../automation/actions" import { stripeAction, fromStripe, getStripeApi, stripeActionSchemas, stripeInputSchema, stripeMutationArguments, stripeMutationReplaySafety, stripeMutationSchema, stripeObjectSchema, stripeSchema, toStripeParams, type StripeMutationInput, type StripeInputValue, } from "../lib" /** Stripe product retrieval input with a public resource ID. */ type ProductIdInput = StripeInputValue & { productId: string } /** Stripe product mutation input with a public resource ID. */ type ProductMutationInput = StripeMutationInput & { productId: string } /** Stripe price retrieval input with a public resource ID. */ type PriceIdInput = StripeInputValue & { priceId: string } /** Stripe price mutation input with a public resource ID. */ type PriceMutationInput = StripeMutationInput & { priceId: string } /** Creates a product that can be sold through Stripe prices. */ export const createStripeProduct = stripeAction< StripeMutationInput, Stripe.Product >( defineAction("Create Stripe product") .describe( "Creates a Stripe product with catalog, tax, and metadata fields.", ) .account("stripe") .input( stripeMutationSchema( stripeActionSchemas.mutation({ name: z.string().min(1).max(250) }), ), ) .output(stripeObjectSchema()) .retry({ replaySafety: stripeMutationReplaySafety }) .handler(async ({ account, input }) => { const [params, options] = stripeMutationArguments(input) return fromStripe( await getStripeApi(account).products.create(params, options), ) }), ) /** Retrieves a Stripe product by ID. */ export const getStripeProduct = stripeAction< ProductIdInput, Stripe.Product | Stripe.DeletedProduct >( defineAction("Get Stripe product") .describe("Retrieves one active, archived, or deleted Stripe product.") .account("stripe") .input( stripeSchema>( stripeActionSchemas.id("productId", "prod_"), ), ) .output(stripeObjectSchema()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { productId, ...params } = input return fromStripe( await getStripeApi(account).products.retrieve( productId, toStripeParams(params), ), ) }), ) /** Lists Stripe products with cursor pagination and catalog filters. */ export const listStripeProducts = stripeAction< StripeInputValue, Stripe.ApiList >( defineAction("List Stripe products") .describe("Lists Stripe products with activity, creation, and URL filters.") .account("stripe") .input( stripeInputSchema(stripeActionSchemas.list), ) .output(stripeObjectSchema>()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromStripe( await getStripeApi(account).products.list(toStripeParams(input)), ), ), ) /** Searches products with Stripe Search Query Language. */ export const searchStripeProducts = stripeAction< StripeInputValue, Stripe.ApiSearchResult >( defineAction("Search Stripe products") .describe("Searches the product catalog with Stripe Search Query Language.") .account("stripe") .input( stripeInputSchema(stripeActionSchemas.search), ) .output(stripeObjectSchema>()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromStripe( await getStripeApi(account).products.search(toStripeParams(input)), ), ), ) /** Updates selected catalog fields on a Stripe product. */ export const updateStripeProduct = stripeAction< ProductMutationInput, Stripe.Product >( defineAction("Update Stripe product") .describe("Updates selected product catalog, tax, or metadata fields.") .account("stripe") .input( stripeSchema>( stripeActionSchemas.id("productId", "prod_"), ), ) .output(stripeObjectSchema()) .retry({ replaySafety: stripeMutationReplaySafety }) .handler(async ({ account, input }) => { const { productId, ...mutation } = input const [params, options] = stripeMutationArguments(mutation) return fromStripe( await getStripeApi(account).products.update(productId, params, options), ) }), ) /** Archives a Stripe product without deleting its historical records. */ export const archiveStripeProduct = stripeAction< ProductMutationInput>, Stripe.Product >( defineAction("Archive Stripe product") .describe("Archives a product so it can no longer be purchased.") .account("stripe") .input( stripeSchema< ProductMutationInput> >(stripeActionSchemas.id("productId", "prod_")), ) .output(stripeObjectSchema()) .retry({ replaySafety: stripeMutationReplaySafety }) .handler(async ({ account, input }) => { const { productId, ...mutation } = input const [params, options] = stripeMutationArguments({ ...mutation, active: false, }) return fromStripe( await getStripeApi(account).products.update(productId, params, options), ) }), ) /** Creates a one-time or recurring Stripe price for a product. */ export const createStripePrice = stripeAction< StripeMutationInput, Stripe.Price >( defineAction("Create Stripe price") .describe("Creates a one-time, recurring, tiered, or usage-based price.") .account("stripe") .input( stripeMutationSchema( stripeActionSchemas .mutation({ billingScheme: z.enum(["per_unit", "tiered"]).optional(), currency: z.string().regex(/^[a-z]{3}$/), customUnitAmount: z .object({ enabled: z.literal(true) }) .loose() .optional(), product: z.string().startsWith("prod_").optional(), productData: z .object({ name: z.string().min(1) }) .loose() .optional(), recurring: z .object({ interval: z.enum(["day", "week", "month", "year"]) }) .loose() .optional(), taxBehavior: z .enum(["exclusive", "inclusive", "unspecified"]) .optional(), tiers: z .array( z .object({ upTo: z.union([z.literal("inf"), z.int().positive()]), }) .loose(), ) .min(1) .optional(), tiersMode: z.enum(["graduated", "volume"]).optional(), unitAmount: z.int().nonnegative().optional(), unitAmountDecimal: z .string() .regex(/^\d+(?:\.\d{1,12})?$/) .optional(), }) .superRefine((input, context) => { if (Boolean(input.product) === Boolean(input.productData)) { context.addIssue({ code: "custom", message: "Provide exactly one of product or productData.", path: ["product"], }) } const amountFields = [ input.customUnitAmount, input.unitAmount, input.unitAmountDecimal, ].filter((value) => value !== undefined) if (input.billingScheme !== "tiered" && amountFields.length !== 1) { context.addIssue({ code: "custom", message: "Per-unit prices require exactly one amount configuration.", path: ["unitAmount"], }) } if ( input.billingScheme === "tiered" && (input.tiers === undefined || input.tiersMode === undefined) ) { context.addIssue({ code: "custom", message: "Tiered prices require tiers and tiersMode.", path: ["tiers"], }) } if (input.billingScheme === "tiered" && amountFields.length > 0) { context.addIssue({ code: "custom", message: "Tiered prices cannot use a per-unit amount.", path: ["unitAmount"], }) } if ( input.billingScheme !== "tiered" && (input.tiers !== undefined || input.tiersMode !== undefined) ) { context.addIssue({ code: "custom", message: "Per-unit prices cannot use tiers or tiersMode.", path: ["tiers"], }) } }), ), ) .output(stripeObjectSchema()) .retry({ replaySafety: stripeMutationReplaySafety }) .handler(async ({ account, input }) => { const [params, options] = stripeMutationArguments(input) return fromStripe( await getStripeApi(account).prices.create(params, options), ) }), ) /** Retrieves a Stripe price by ID. */ export const getStripePrice = stripeAction< PriceIdInput, Stripe.Price >( defineAction("Get Stripe price") .describe("Retrieves one active or archived Stripe price.") .account("stripe") .input( stripeSchema>( stripeActionSchemas.id("priceId", "price_"), ), ) .output(stripeObjectSchema()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => { const { priceId, ...params } = input return fromStripe( await getStripeApi(account).prices.retrieve( priceId, toStripeParams(params), ), ) }), ) /** Lists Stripe prices with product, currency, and activity filters. */ export const listStripePrices = stripeAction< StripeInputValue, Stripe.ApiList >( defineAction("List Stripe prices") .describe("Lists prices with Stripe cursor pagination and catalog filters.") .account("stripe") .input(stripeInputSchema(stripeActionSchemas.list)) .output(stripeObjectSchema>()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromStripe( await getStripeApi(account).prices.list(toStripeParams(input)), ), ), ) /** Searches prices with Stripe Search Query Language. */ export const searchStripePrices = stripeAction< StripeInputValue, Stripe.ApiSearchResult >( defineAction("Search Stripe prices") .describe("Searches prices with Stripe Search Query Language.") .account("stripe") .input( stripeInputSchema(stripeActionSchemas.search), ) .output(stripeObjectSchema>()) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => fromStripe( await getStripeApi(account).prices.search(toStripeParams(input)), ), ), ) /** Updates mutable lookup, metadata, tax, transfer, or display fields. */ export const updateStripePrice = stripeAction< PriceMutationInput, Stripe.Price >( defineAction("Update Stripe price") .describe("Updates the mutable fields of one Stripe price.") .account("stripe") .input( stripeSchema>( stripeActionSchemas.id("priceId", "price_"), ), ) .output(stripeObjectSchema()) .retry({ replaySafety: stripeMutationReplaySafety }) .handler(async ({ account, input }) => { const { priceId, ...mutation } = input const [params, options] = stripeMutationArguments(mutation) return fromStripe( await getStripeApi(account).prices.update(priceId, params, options), ) }), ) /** Archives a Stripe price while preserving historical transactions. */ export const archiveStripePrice = stripeAction< PriceMutationInput>, Stripe.Price >( defineAction("Archive Stripe price") .describe("Archives a price so it cannot be used for new purchases.") .account("stripe") .input( stripeSchema< PriceMutationInput> >(stripeActionSchemas.id("priceId", "price_")), ) .output(stripeObjectSchema()) .retry({ replaySafety: stripeMutationReplaySafety }) .handler(async ({ account, input }) => { const { priceId, ...mutation } = input const [params, options] = stripeMutationArguments({ ...mutation, active: false, }) return fromStripe( await getStripeApi(account).prices.update(priceId, params, options), ) }), )