## Stripe Subscriptions

Most of the code for coordinating Stripe subscriptions can be found in
`plans.ts`. Conceptually, we organize subscriptions as follows:

- A `Price` captures the ID information of a Stripe Price and it's current
  status:
  - `inactive` means that the price should no longer be used (but may have been
    used previously).
  - `active` means that the price should be used going forward.
- A `Product` maps to what is shown in Stripe's
  [Product Catalogue](https://dashboard.stripe.com/products?active=true) UI.
  These help use organize the dev and prod versions of a `Product`, as well as
  their underlying `productId` and `priceIds`.
- A `Plan` is a collection of `Product`s, organized by their tier (basic,
  legacy_pro, pro, scale).

Each Plan also specifies the `eligiblePlanTiers` that it accepts for validation
purposes. E.g. the basic plan specifies
`['basic', 'legacy_pro', 'pro', 'scale']` in its `eligiblePlanTiers` field,
meaning that Products belonging within any of those tiers would satisfy
validation requirements. Similarly, the basic plan specifies `['scale']` in its
tiers field, meaning that only **Products** belonging to the scale **Plans**
would satisfy validation.

### Adding a new subscription price

Before adding a new price, you'll first need to identify the Stripe
[Stripe Product](https://dashboard.stripe.com/products?active=true) that the
price should be added to.

Once you've added a new price to the product in Stripe's `live` and `test` mode,
then you can come back and add it to the relevant product in `plans.ts`.

For example, these are the dev and prod records for the legacy enterprise
product:

```ts
const legacyEnterpriseDev = createProduct('prod_Hc9PMnHUmHvOlw', {
  monthly_700: createPrice('price_1H2v6JIewCKA2h0IgUwsuctb', 'active'),
});

const legacyEnterpriseProd = createProduct('prod_GuGGWeMQ3SCuE9', {
  monthly_700: createPrice('plan_GuGICX6nRtDthN', 'active'),
});
```

If you added a new Stripe price to this product that is billed yearly for
$10,000, then you would add update the products as follows:

```ts
const legacyEnterpriseDev = createProduct('prod_Hc9PMnHUmHvOlw', {
  monthly_700: createPrice('price_1H2v6JIewCKA2h0IgUwsuctb', 'active'),
  yearly_10000: createPrice('...', 'active'),
});

const legacyEnterpriseProd = createProduct('prod_GuGGWeMQ3SCuE9', {
  monthly_700: createPrice('plan_GuGICX6nRtDthN', 'active'),
  yearly_10000: createPrice('...', 'active'),
});
```

### Rebuilding the Stripe Customer Portals

ToDesktop's subscription flow needs to support both **CLI** and **ToDesktop
Builder** customers. To achieve this, we dynamically create/load billing portals
based on whether the customer is a **CLI** or **ToDesktop Builder** user,
whether the customer needs to **Upgrade** or **Update** their plan, and whether
the environment is in prod or dev.

This leaves us with 8 unique customer portal configurations:

- _cliUpdateConfigurationDev_
- _cliUpdateConfigurationProd_
- _cliUpgradeConfigurationDev_
- _cliUpgradeConfigurationProd_
- _builderUpdateConfigurationDev_
- _builderUpdateConfigurationProd_
- _builderUpgradeConfigurationDev_
- _builderUpgradeConfigurationProd_

Each configuration specifies the products and prices (with an `active` status)
that should be displayed when a user navigates to the customer billing portal.

The web app and desktop app then only need to specify the `PortalConfigKey` when
creating a checkout session from the client:

```ts
// begin a CLI upgrade customer portal session
await createCustomerPortalSession({
  configuration: PortalConfigKey.CLIUpgradeProd,
  flowData: {
    type: 'subscription_update',
    subscription_update: { subscription: subscription.id },
  },
});
```

If you have updated the products that are used by any of the portal
configurations, then you'll also need to increase the `PORTAL_VERSION` constant
by 1. This will ensure that the portals are rebuilt to use the latest products
and prices. This happens in `createCustomerPortalSession` in the web app.
