prices-as-code
Version:
Prices as Code (PaC) - Define your product pricing schemas with type-safe definitions
565 lines (516 loc) • 20.5 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Configuration File - Prices as Code</title>
<meta name="description" content="Learn about the configuration file format for Prices as Code">
<link rel="stylesheet" href="../assets/css/main.css">
<link rel="icon" href="https://raw.githubusercontent.com/wickdninja/assets/refs/heads/main/PaC.webp">
</head>
<body>
<header class="site-header">
<div class="container">
<div class="header-content">
<div class="logo">
<a href="../index.html">
<img src="https://raw.githubusercontent.com/wickdninja/assets/refs/heads/main/PaC.webp" alt="Prices as Code" width="40">
<span>Prices as Code</span>
</a>
</div>
<nav class="main-nav">
<ul>
<li><a href="index.html">Guides</a></li>
<li><a href="../api/index.html">API</a></li>
<li><a href="../providers/index.html">Providers</a></li>
<li><a href="https://github.com/wickdninja/prices-as-code" target="_blank">GitHub</a></li>
<li><a href="https://www.npmjs.com/package/prices-as-code" target="_blank">NPM</a></li>
</ul>
</nav>
</div>
</div>
</header>
<main class="content">
<div class="container">
<h1>Configuration File Format</h1>
<span class="badge">Updated for v3.0.0</span>
<p>The Prices as Code configuration file is where you define your products and pricing structures. This guide explains the configuration format in detail and provides examples for both TypeScript and YAML formats.</p>
<div class="whats-new">
<h2>Version 3.0.0 Updates</h2>
<ul>
<li><strong>Simplified Provider Support:</strong> Now focuses exclusively on Stripe integration</li>
<li><strong>Enhanced Sample Configurations:</strong> More comprehensive example files included</li>
<li><strong>Improved Type Safety:</strong> Better TypeScript type definitions and validation</li>
</ul>
</div>
<h2>File Formats</h2>
<p>Prices as Code supports both TypeScript/JavaScript and YAML configuration files:</p>
<ul>
<li><code>.ts</code>, <code>.js</code> - TypeScript/JavaScript configuration</li>
<li><code>.yml</code>, <code>.yaml</code> - YAML configuration</li>
</ul>
<h2>Configuration Structure</h2>
<div class="tabs">
<div class="tab active" onclick="showTab('ts')">TypeScript</div>
<div class="tab" onclick="showTab('yaml')">YAML</div>
</div>
<div id="ts-tab" class="tab-content">
<p>Every configuration file has two main sections: <code>products</code> and <code>prices</code>:</p>
<div class="highlight">
<pre><code class="language-typescript">// TypeScript example
import { Config } from 'prices-as-code';
const config: Config = {
products: [
// Product definitions
],
prices: [
// Price definitions
]
};
export default config;</code></pre>
</div>
</div>
<div id="yaml-tab" class="tab-content" style="display: none;">
<p>Every configuration file has two main sections: <code>products</code> and <code>prices</code>:</p>
<div class="highlight">
<pre><code class="language-yaml"># YAML example
products:
- # First product definition
- # Second product definition
prices:
- # First price definition
- # Second price definition</code></pre>
</div>
</div>
<h2>Products Configuration</h2>
<p>The <code>products</code> section defines your product offerings with the following properties:</p>
<div class="table-responsive">
<table>
<thead>
<tr>
<th>Property</th>
<th>Type</th>
<th>Required</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>provider</code></td>
<td>string</td>
<td>Yes</td>
<td>The payment provider (e.g., 'stripe')</td>
</tr>
<tr>
<td><code>name</code></td>
<td>string</td>
<td>Yes</td>
<td>The product name</td>
</tr>
<tr>
<td><code>description</code></td>
<td>string</td>
<td>No</td>
<td>A description of the product</td>
</tr>
<tr>
<td><code>features</code></td>
<td>string[]</td>
<td>No</td>
<td>Array of feature descriptions for the product</td>
</tr>
<tr>
<td><code>highlight</code></td>
<td>boolean</td>
<td>No</td>
<td>Whether to highlight this product in UI (e.g., as a recommended option)</td>
</tr>
<tr>
<td><code>metadata</code></td>
<td>object</td>
<td>No</td>
<td>Custom metadata for the product</td>
</tr>
<tr>
<td><code>key</code></td>
<td>string</td>
<td>No</td>
<td>Unique identifier for referencing this product (auto-generated if not provided)</td>
</tr>
<tr>
<td><code>id</code></td>
<td>string</td>
<td>No</td>
<td>Provider-specific ID (populated after syncing)</td>
</tr>
</tbody>
</table>
</div>
<h3>Product Example</h3>
<div class="tabs">
<div class="tab active" onclick="showTab('product-ts')">TypeScript</div>
<div class="tab" onclick="showTab('product-yaml')">YAML</div>
</div>
<div id="product-ts-tab" class="tab-content">
<div class="highlight">
<pre><code class="language-typescript">// TypeScript example
{
provider: "stripe",
name: "Pro Plan",
description: "For growing businesses",
features: [
"Unlimited projects",
"100GB storage",
"Priority support",
"Advanced analytics"
],
highlight: true,
metadata: {
icon: "Star",
color: "purple",
displayOrder: 2,
},
key: "pro"
}</code></pre>
</div>
</div>
<div id="product-yaml-tab" class="tab-content" style="display: none;">
<div class="highlight">
<pre><code class="language-yaml"># YAML example
- provider: stripe
name: Pro Plan
description: For growing businesses
features:
- Unlimited projects
- 100GB storage
- Priority support
- Advanced analytics
highlight: true
metadata:
icon: Star
color: purple
displayOrder: 2
key: pro</code></pre>
</div>
</div>
<h2>Prices Configuration</h2>
<p>The <code>prices</code> section defines the pricing structure for your products with the following properties:</p>
<div class="table-responsive">
<table>
<thead>
<tr>
<th>Property</th>
<th>Type</th>
<th>Required</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>provider</code></td>
<td>string</td>
<td>Yes</td>
<td>The payment provider (e.g., 'stripe')</td>
</tr>
<tr>
<td><code>name</code></td>
<td>string</td>
<td>Yes</td>
<td>The name of the price</td>
</tr>
<tr>
<td><code>nickname</code></td>
<td>string</td>
<td>No</td>
<td>A shorter name for display purposes</td>
</tr>
<tr>
<td><code>unitAmount</code></td>
<td>number</td>
<td>Yes</td>
<td>The amount in smallest currency units (e.g., cents for USD)</td>
</tr>
<tr>
<td><code>currency</code></td>
<td>string</td>
<td>Yes</td>
<td>The three-letter currency code (e.g., 'usd', 'eur')</td>
</tr>
<tr>
<td><code>type</code></td>
<td>string</td>
<td>Yes</td>
<td>Price type ('recurring' or 'one_time')</td>
</tr>
<tr>
<td><code>recurring</code></td>
<td>object</td>
<td>Conditionally</td>
<td>Required if type is 'recurring'</td>
</tr>
<tr>
<td><code>productKey</code></td>
<td>string</td>
<td>Yes</td>
<td>The key that links to a product</td>
</tr>
<tr>
<td><code>metadata</code></td>
<td>object</td>
<td>No</td>
<td>Custom metadata for the price</td>
</tr>
<tr>
<td><code>key</code></td>
<td>string</td>
<td>No</td>
<td>Unique identifier for this price (auto-generated if not provided)</td>
</tr>
<tr>
<td><code>id</code></td>
<td>string</td>
<td>No</td>
<td>Provider-specific ID (populated after syncing)</td>
</tr>
<tr>
<td><code>active</code></td>
<td>boolean</td>
<td>No</td>
<td>Whether the price is active (default: true)</td>
</tr>
</tbody>
</table>
</div>
<h3>Recurring Object Properties</h3>
<div class="table-responsive">
<table>
<thead>
<tr>
<th>Property</th>
<th>Type</th>
<th>Required</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>interval</code></td>
<td>string</td>
<td>Yes</td>
<td>Billing interval ('day', 'week', 'month', or 'year')</td>
</tr>
<tr>
<td><code>intervalCount</code></td>
<td>number</td>
<td>No</td>
<td>Number of intervals between billings (default: 1)</td>
</tr>
</tbody>
</table>
</div>
<h3>Price Example</h3>
<div class="tabs">
<div class="tab active" onclick="showTab('price-ts')">TypeScript</div>
<div class="tab" onclick="showTab('price-yaml')">YAML</div>
</div>
<div id="price-ts-tab" class="tab-content">
<div class="highlight">
<pre><code class="language-typescript">// TypeScript example
{
provider: "stripe",
name: "Pro Monthly",
nickname: "Pro Monthly",
unitAmount: 1999, // $19.99
currency: "usd",
type: "recurring",
recurring: {
interval: "month",
intervalCount: 1,
},
productKey: "pro",
metadata: {
plan_code: "pro_monthly",
display_price: "$19.99",
},
active: true
}</code></pre>
</div>
</div>
<div id="price-yaml-tab" class="tab-content" style="display: none;">
<div class="highlight">
<pre><code class="language-yaml"># YAML example
- provider: stripe
name: Pro Monthly
nickname: Pro Monthly
unitAmount: 1999
currency: usd
type: recurring
recurring:
interval: month
intervalCount: 1
productKey: pro
metadata:
plan_code: pro_monthly
display_price: "$19.99"
active: true</code></pre>
</div>
</div>
<h2>Key Generation and Mapping</h2>
<p>Prices as Code automatically generates keys for products and prices if they're not explicitly provided:</p>
<ul>
<li>For <strong>products</strong>, a key is typically derived from the product name (e.g., "Pro Plan" becomes "pro")</li>
<li>For <strong>prices</strong>, keys are auto-generated based on the price name and a timestamp</li>
<li>The <code>productKey</code> in prices refers to the <code>key</code> of a product, forming a relationship between them</li>
</ul>
<h2>Metadata</h2>
<p>Both products and prices support a <code>metadata</code> field for adding custom attributes. This is useful for:</p>
<ul>
<li>UI display preferences (order, highlighting, icons, colors, etc.)</li>
<li>Feature flags</li>
<li>Integration with other systems</li>
<li>Any other custom data you need to associate with products or prices</li>
</ul>
<div class="note">
<p>Metadata is highly flexible and can contain any valid JSON data. Stripe limits metadata to strings, so PaC will convert non-string values automatically when syncing with Stripe.</p>
</div>
<h2>Sample Configuration</h2>
<div class="sample-config">
<h3>Real-World Example</h3>
<p>Here's a comprehensive example based on a tiered subscription model:</p>
<div class="highlight">
<pre><code class="language-yaml">products:
- name: Free
description: Start your learning journey
metadata:
icon: Book
color: blue
key: free
features:
- Access to free content
- Limited features
- Core lessons
- Ad-supported experience
highlight: false
provider: stripe
key: free
- name: Pro
description: Help us shine bright with new features
metadata:
icon: Star
color: purple
key: pro
features:
- Support ongoing development
- Supporter recognition
- Early access to new features
- Ad-free experience
- Unlimited access
highlight: true
provider: stripe
key: pro
- name: Ultra
description: The ultimate experience
metadata:
icon: Trophy
color: amber
key: ultra
features:
- All Pro perks
- Ultra member recognition
- Input on future features
- Priority support
- Personalized plans
highlight: false
provider: stripe
key: ultra
prices:
- name: Free
unitAmount: 0
currency: usd
type: recurring
recurring:
interval: year
intervalCount: 1
active: true
metadata:
plan_code: free
display_price: $0
nickname: Free plan
provider: stripe
productKey: free
- name: Pro Monthly
unitAmount: 1000
currency: usd
type: recurring
recurring:
interval: month
intervalCount: 1
active: true
metadata:
plan_code: pro_monthly
display_price: $10
nickname: Pro Monthly Plan
provider: stripe
productKey: pro
- name: Pro Yearly
unitAmount: 9600
currency: usd
type: recurring
recurring:
interval: year
intervalCount: 1
active: true
metadata:
plan_code: pro_yearly
display_price: $96
nickname: Pro Yearly Plan
provider: stripe
productKey: pro</code></pre>
</div>
</div>
<h2>Next Steps</h2>
<ul>
<li>Learn how to use the <a href="cli.html">Command-Line Interface</a></li>
<li>Set up <a href="ci-cd.html">CI/CD Integration</a> for automated pricing updates</li>
<li>Explore <a href="metadata.html">Working with Metadata</a> for enhanced flexibility</li>
<li>Understand how to implement <a href="custom-providers.html">Custom Providers</a> beyond Stripe</li>
</ul>
</div>
</main>
<footer class="site-footer">
<div class="container">
<div class="footer-content">
<p>Copyright © 2025 Nate Ross. Distributed by an <a href="https://github.com/wickdninja/prices-as-code/blob/main/LICENSE">MIT license.</a></p>
<p><a href="#top" class="back-to-top">Back to top</a></p>
</div>
</div>
</footer>
<script src="../assets/js/main.js"></script>
<script>
function showTab(tabId) {
// Find the tab section containing this tab
const tabContent = document.getElementById(tabId + '-tab');
if (!tabContent) return;
// Find the parent content area that holds these tabs
const tabSection = tabContent.parentElement;
// Hide all tab contents in this section
const tabContents = tabSection.querySelectorAll('.tab-content');
tabContents.forEach(tab => {
tab.style.display = 'none';
});
// Show the selected tab content
tabContent.style.display = 'block';
// Update active tab styling
// Find the tab nav that is in the same section
const tabs = tabSection.querySelectorAll('.tab');
tabs.forEach(tab => {
if (tab.getAttribute('onclick').includes(tabId)) {
tab.classList.add('active');
} else {
tab.classList.remove('active');
}
});
}
</script>
</body>
</html>