prices-as-code
Version:
Prices as Code (PaC) - Define your product pricing schemas with type-safe definitions
188 lines (187 loc) • 5.68 kB
JavaScript
import { z } from 'zod';
/**
* Base Types
*/
/**
* Common metadata schema used across all providers
*/
export const MetadataSchema = z.record(z.string().or(z.number()).or(z.boolean()).or(z.array(z.string())));
/**
* Base product schema shared across all providers
*/
export const BaseProductSchema = z.object({
id: z.string().optional(),
name: z.string(),
description: z.string().optional(),
metadata: MetadataSchema.optional().default({}),
});
/**
* Common price intervals
*/
export const PriceIntervalSchema = z.enum(['day', 'week', 'month', 'year']);
/**
* Base recurring price schema
*/
export const BaseRecurringSchema = z.object({
interval: PriceIntervalSchema,
intervalCount: z.number().optional().default(1),
});
/**
* Base price schema shared across all providers
*/
export const BasePriceSchema = z.object({
id: z.string().optional(),
name: z.string(),
unitAmount: z.number(),
currency: z.string().length(3), // ISO 4217 currency code
type: z.enum(['one_time', 'recurring']).default('one_time'),
recurring: BaseRecurringSchema.optional(),
active: z.boolean().optional().default(true),
productId: z.string().optional(), // Reference to product ID
metadata: MetadataSchema.optional().default({}),
});
/**
* Stripe Provider Schemas
*/
/**
* Stripe-specific product schema
*/
export const StripeProductSchema = BaseProductSchema.extend({
features: z.array(z.string()).optional(),
highlight: z.boolean().optional(),
provider: z.literal('stripe'),
key: z.string().optional(), // Well-known ID for reference
});
/**
* Stripe-specific recurring schema
*/
export const StripeRecurringSchema = BaseRecurringSchema.extend({
interval: z.enum(['day', 'week', 'month', 'year']),
usageType: z.enum(['licensed', 'metered']).optional(),
aggregateUsage: z
.enum(['sum', 'last_during_period', 'last_ever', 'max'])
.optional(),
trialPeriodDays: z.number().optional(),
});
/**
* Stripe-specific price schema
*/
export const StripePriceSchema = BasePriceSchema.extend({
nickname: z.string(), // Stripe-specific field
recurring: StripeRecurringSchema.optional(),
provider: z.literal('stripe'),
key: z.string().optional(), // Well-known ID for reference
taxBehavior: z.enum(['inclusive', 'exclusive', 'unspecified']).optional(),
billingScheme: z.enum(['per_unit', 'tiered']).optional(),
productKey: z.string().optional(), // Reference by key instead of ID
});
/**
* Union types for providers
*/
export const ProductSchema = z.discriminatedUnion('provider', [
StripeProductSchema,
]);
export const PriceSchema = z.discriminatedUnion('provider', [
StripePriceSchema,
]);
/**
* Configuration schema for all providers
*/
export const ConfigSchema = z.object({
products: z.array(ProductSchema),
prices: z.array(PriceSchema),
});
/**
* Provider-specific client options
*/
export const StripeOptionsSchema = z.object({
secretKey: z.string(),
apiVersion: z.string().optional(),
});
/**
* Provider union type
*/
export const ProviderOptionsSchema = z.discriminatedUnion('provider', [
z.object({ provider: z.literal('stripe'), options: StripeOptionsSchema }),
]);
/**
* Global options
*/
export const PaCOptionsSchema = z.object({
configPath: z.string().optional(),
providers: z.array(ProviderOptionsSchema),
writeBack: z.boolean().optional().default(false),
format: z.enum(['yaml', 'json', 'ts']).optional().default('yaml'),
});
/**
* Generate options
*/
export const GenerateOptionsSchema = z.object({
configPath: z.string(),
format: z.enum(['yaml', 'json', 'ts']).optional().default('yaml'),
provider: z.enum(['stripe']).optional().default('stripe'),
productTiers: z.array(z.string()).optional().default(['basic', 'pro', 'enterprise']),
intervals: z.array(z.enum(['month', 'year'])).optional().default(['month', 'year']),
currency: z.string().length(3).optional().default('usd'),
includeMetadata: z.boolean().optional().default(true),
includeFeatures: z.boolean().optional().default(true),
});
/**
* Result of synchronization
*/
export const SyncResultSchema = z.object({
config: ConfigSchema,
configUpdated: z.boolean(),
});
/**
* Result of pull operation
*/
export const PullResultSchema = z.object({
config: ConfigSchema,
configPath: z.string(),
});
/**
* Result of generate operation
*/
export const GenerateResultSchema = z.object({
config: ConfigSchema,
configPath: z.string(),
});
/**
* Private/internal types for Recurly support (not exposed in public API)
*/
/**
* Recurly-specific product schema
*/
export const RecurlyProductSchema = BaseProductSchema.extend({
provider: z.literal('recurly'),
code: z.string(), // Recurly-specific identifier
taxCode: z.string().optional(),
taxExempt: z.boolean().optional(),
});
/**
* Recurly-specific recurring schema
*/
export const RecurlyRecurringSchema = BaseRecurringSchema.extend({
intervalLength: z.number().min(1),
intervalUnit: z.enum(['days', 'months']),
trialLength: z.number().optional(),
trialUnit: z.enum(['days', 'months']).optional(),
});
/**
* Recurly-specific price schema
*/
export const RecurlyPriceSchema = BasePriceSchema.extend({
provider: z.literal('recurly'),
code: z.string(), // Recurly-specific identifier
taxInclusive: z.boolean().optional(),
recurring: RecurlyRecurringSchema.optional(),
productCode: z.string(), // Reference to product code
});
/**
* Recurly client options
*/
export const RecurlyOptionsSchema = z.object({
apiKey: z.string(),
subdomain: z.string().optional(),
});