prices-as-code
Version:
Prices as Code (PaC) - Define your product pricing schemas with type-safe definitions
325 lines (257 loc) • 9.02 kB
Markdown
# Prices as Code (PaC)
<p align="center">
<img src="https://raw.githubusercontent.com/wickdninja/assets/refs/heads/main/PaC.webp" alt="Prices as Code" width="400" />
</p>
Define your product pricing schemas with type-safe definitions and synchronize them across multiple providers.
## 🚀 Features
- **Type-Safe**: Use TypeScript and Zod schemas to define your pricing models with full type safety
- **Declarative**: Define your products and prices in code, sync them to providers
- **Scaffolding**: Generate template pricing structures to quickly get started
- **Bidirectional**: Use Push mode to sync local configs to providers, or Pull mode to generate configs from existing providers
- **Idempotent**: Run it multiple times, only changes what's needed
- **Push Model**: Push your config to different environments without ID conflicts
- **Pull Model**: Generate config files from existing provider data to onboard existing customers
- **Metadata Support**: Add custom metadata to your products and prices
- **YAML or TypeScript**: Define your pricing in either YAML or TypeScript format
- **Extensible**: Easily add support for your own billing providers
## Installation
```bash
npm install prices-as-code
```
## Quick Start
### 1. Create a pricing configuration file (pricing.ts)
```typescript
import { Config } from "prices-as-code";
const config: Config = {
products: [
{
provider: "stripe",
name: "Basic Plan",
description: "For individuals and small teams",
features: ["5 projects", "10GB storage", "Email support"],
highlight: false,
metadata: {
displayOrder: 1,
},
},
{
provider: "stripe",
name: "Pro Plan",
description: "For growing businesses",
features: ["Unlimited projects", "100GB storage", "Priority support"],
highlight: true,
metadata: {
displayOrder: 2,
},
},
],
prices: [
{
provider: "stripe",
name: "Basic Monthly",
nickname: "Basic Monthly",
unitAmount: 999, // $9.99
currency: "usd",
type: "recurring",
recurring: {
interval: "month",
intervalCount: 1,
},
productKey: "basic_plan",
metadata: {
displayName: "Basic Monthly",
},
},
{
provider: "stripe",
name: "Pro Monthly",
nickname: "Pro Monthly",
unitAmount: 1999, // $19.99
currency: "usd",
type: "recurring",
recurring: {
interval: "month",
intervalCount: 1,
},
productKey: "pro_plan",
metadata: {
displayName: "Pro Monthly",
},
},
],
};
export default config;
```
### 2. Set up environment variables
```bash
# .env file
STRIPE_SECRET_KEY=sk_test_...
```
### 3. Run the synchronization
```bash
npx prices-as-code pricing.ts
```
## CLI Options
```
prices-as-code [command] [configPath] [options]
Commands:
sync Synchronize your pricing schema with provider (default)
pull Pull pricing from provider into a local config file
generate Generate a template pricing structure
Options:
--env=<path> Path to .env file
--stripe-key=<key> Stripe API key
--write-back Write provider IDs back to config file (only for sync)
--format=<format> Output format for 'pull' and 'generate' commands (yaml, json, ts)
Generate Options:
--tiers=<tiers> Comma-separated list of product tiers (default: basic,pro,enterprise)
--currency=<cur> ISO currency code (default: usd)
--intervals=<int> Comma-separated list of intervals (default: month,year)
--no-metadata Don't include metadata in generated file
--no-features Don't include feature lists in generated products
```
### Examples
```bash
# Sync local pricing to provider (Push mode)
npx prices-as-code pricing.ts
# Pull provider pricing to local file (Pull mode)
npx prices-as-code pull pricing.yml
# Pull provider pricing with specific format
npx prices-as-code pull --format=ts pricing.ts
# Generate a template pricing structure
npx prices-as-code generate pricing.yml
# Generate with custom tiers and currency
npx prices-as-code generate --tiers=free,basic,pro --currency=eur pricing.yml
```
## Supported Providers
### Stripe
The Stripe provider allows you to sync products and prices to your Stripe account.
Required environment variables:
- `STRIPE_SECRET_KEY`: Your Stripe secret key
- `STRIPE_API_VERSION` (optional): Stripe API version to use
## Programmatic Usage
### Push Mode (Sync)
```typescript
import { pac } from "prices-as-code";
async function syncPricing() {
try {
const result = await pac({
configPath: "./pricing.ts",
providers: [
{
provider: "stripe",
options: {
secretKey: process.env.STRIPE_SECRET_KEY,
apiVersion: "2025-02-24",
},
},
],
// Set to true to write IDs back to config file (legacy behavior)
// Default is false (push mode - no writes to config file)
writeBack: false,
});
console.log("Sync result:", result);
} catch (error) {
console.error("Sync failed:", error);
}
}
syncPricing();
```
### Pull Mode (Import)
```typescript
import { pac } from "prices-as-code";
async function pullPricing() {
try {
const result = await pac.pull({
configPath: "./pricing.yml", // Output file path
providers: [
{
provider: "stripe",
options: {
secretKey: process.env.STRIPE_SECRET_KEY,
},
},
],
format: "yaml", // 'yaml', 'json', or 'ts'
});
console.log("Pull complete:", result);
} catch (error) {
console.error("Pull failed:", error);
}
}
pullPricing();
```
### Generate Mode (Scaffolding)
```typescript
import { pac } from "prices-as-code";
async function generatePricing() {
try {
const result = await pac.generate({
configPath: "./pricing.yml", // Output file path
format: "yaml", // 'yaml', 'json', or 'ts'
provider: "stripe",
productTiers: ["basic", "pro", "enterprise"], // Custom tiers
intervals: ["month", "year"], // Billing intervals
currency: "usd",
includeMetadata: true,
includeFeatures: true
});
console.log("Template generated:", result);
} catch (error) {
console.error("Generation failed:", error);
}
}
generatePricing();
```
## Adding Your Own Provider
You can extend the library with your own providers by implementing the `ProviderClient` interface:
```typescript
import { ProviderClient, Product, Price } from "prices-as-code";
export class MyCustomProvider implements ProviderClient {
constructor(options: any) {
// Initialize your provider client
}
// Push mode methods - required
async syncProducts(products: Product[]): Promise<Product[]> {
// Implement product synchronization
return products;
}
async syncPrices(prices: Price[]): Promise<Price[]> {
// Implement price synchronization
return prices;
}
// Pull mode methods - required
async fetchProducts(): Promise<Product[]> {
// Implement product fetching from provider
return [];
}
async fetchPrices(): Promise<Price[]> {
// Implement price fetching from provider
return [];
}
}
```
## 📖 Documentation
Visit our [documentation website](https://wickdninja.github.io/prices-as-code) for comprehensive guides and API reference.
### Guides
- [Getting Started](https://wickdninja.github.io/prices-as-code/guides/getting-started)
- [Push Model](https://wickdninja.github.io/prices-as-code/guides/push-model)
- [Pull Model](https://wickdninja.github.io/prices-as-code/guides/pull-model) (New in v3.3.0)
- [Generate Templates](https://wickdninja.github.io/prices-as-code/guides/generate) (New in v3.5.0)
- [Configuration Format](https://wickdninja.github.io/prices-as-code/guides/configuration-file)
- [Command Line Interface](https://wickdninja.github.io/prices-as-code/guides/cli)
- [Custom Providers](https://wickdninja.github.io/prices-as-code/guides/custom-providers)
- [CI/CD Integration](https://wickdninja.github.io/prices-as-code/guides/ci-cd)
- [Working with Metadata](https://wickdninja.github.io/prices-as-code/guides/metadata)
- [Custom Pricing Logic](https://wickdninja.github.io/prices-as-code/guides/custom-pricing)
### API Reference
- [Main API](https://wickdninja.github.io/prices-as-code/api)
- [Stripe Provider](https://wickdninja.github.io/prices-as-code/providers/stripe)
## 🧩 Why Prices as Code?
Managing pricing configurations across multiple systems is challenging. Prices as Code provides:
1. **Single Source of Truth**: Define your pricing once, deploy everywhere
2. **Type Safety**: Catch errors before they happen with TypeScript validation
3. **Version Control**: Track pricing changes alongside your codebase
4. **CI/CD Integration**: Automate pricing updates as part of your deployment pipeline
## 📄 License
MIT