Skip to main content

SPEC 011: Billing and Subscriptions

Status: DRAFT Priority: P0 Created: 2026-02-21 Approved: pending Repo(s): equa-web, equa-server

1. Feature Purpose

The Billing and Subscriptions module handles the commercial side of the Equa platform: subscription tier management, payment profile collection, checkout flows, coupon redemption, and waitlist positioning. It integrates with Chargify as the external payment processor and subscription lifecycle manager. The module enforces tier-based limits (e.g. member caps) across the platform and provides self-service upgrade/downgrade flows for organization admins.

2. Current State (Verified)

Frontend modules:
equa-web/src/modules/payments/
├── components/   # Billing dashboard, payment profiles, checkout
├── services/     # API client for billing operations
├── store/        # Payment and subscription state
└── index.tsx

equa-web/src/modules/subscriptions/
├── components/   # Subscription management, plan selection, upgrade/downgrade
├── services/     # API client for subscription operations
├── store/        # Subscription state
└── index.tsx
Frontend service layer:
equa-web/src/service/services/payments   # Shared payment service utilities
Backend module:
equa-server/modules/billing/
├── src/
│   ├── chargify/      # Chargify API client and webhook handlers
│   ├── services/      # Billing business logic
│   └── entity/        # Billing-related entities
Backend endpoints:
equa-server/modules/api/src/endpoints/billing-endpoints.ts

3. Data Model

Entities

EntityDescriptionFile
MemberLimitsEnforces subscription-tier member caps per organizationequa-server/modules/persistence/src/entity/MemberLimits.ts
UserCouponsCoupon codes redeemed by usersequa-server/modules/persistence/src/entity/UserCoupons.ts
WaitlistsPre-launch or tier-gated waitlist positionsequa-server/modules/persistence/src/entity/Waitlists.ts

Key Fields — MemberLimits

FieldTypeNullableDescription
organizationuuidNoOrganization this limit applies to
memberLimitintegerNoMaximum team members allowed by current subscription tier

Key Fields — UserCoupons

FieldTypeNullableDescription
useruuidNoUser who redeemed the coupon
codestringNoCoupon code

Key Fields — Waitlists

FieldTypeNullableDescription
iduuidNoPrimary key
useruuidNoUser on the waitlist
positionintegerNoQueue position

Relationships

External Integration: Chargify

Chargify manages the canonical subscription state. The Equa backend syncs via:
  • API calls to Chargify for creating/updating subscriptions, applying coupons, and managing payment profiles.
  • Webhooks from Chargify for subscription lifecycle events (activation, renewal, cancellation, payment failure, dunning).
The local MemberLimits entity is updated when subscription tier changes are confirmed by Chargify.

4. API Endpoints

Served by equa-server/modules/api/src/endpoints/billing-endpoints.ts.
MethodPathAuthDescription
GET/v1/billing/subscriptionsSessionGet current user’s subscriptions
POST/v1/billing/subscriptionsSessionCreate a new subscription (triggers Chargify)
PUT/v1/billing/subscriptions/:idSessionUpdate subscription (upgrade/downgrade)
DELETE/v1/billing/subscriptions/:idSessionCancel a subscription
GET/v1/billing/payment-profilesSessionList payment profiles on file
POST/v1/billing/payment-profilesSessionAdd a payment profile
PUT/v1/billing/payment-profiles/:idSessionUpdate a payment profile
DELETE/v1/billing/payment-profiles/:idSessionRemove a payment profile
POST/v1/billing/coupons/redeemSessionRedeem a coupon code
GET/v1/billing/coupons/validate/:codeSessionValidate a coupon code without redeeming
GET/v1/billing/plansPublicList available subscription plans and pricing
POST/v1/billing/checkoutSessionInitiate checkout flow
GET/v1/billing/invoicesSessionList billing invoices
POST/v1/billing/webhooks/chargifyWebhookChargify webhook receiver
GET/v1/waitlist/statusSessionGet user’s waitlist position
POST/v1/waitlist/joinSessionJoin the waitlist

5. Frontend Components

Payments Module

ComponentFilePurpose
BillingDashboardequa-web/src/modules/payments/components/BillingDashboard.tsxOverview of billing status, invoices, payment method
PaymentProfileFormequa-web/src/modules/payments/components/PaymentProfileForm.tsxAdd/edit credit card or bank account
CheckoutFlowequa-web/src/modules/payments/components/CheckoutFlow.tsxMulti-step checkout with plan selection and payment
InvoiceListequa-web/src/modules/payments/components/InvoiceList.tsxView past invoices
CouponInputequa-web/src/modules/payments/components/CouponInput.tsxCoupon code entry and validation

Subscriptions Module

ComponentFilePurpose
SubscriptionManagerequa-web/src/modules/subscriptions/components/SubscriptionManager.tsxCurrent plan details, upgrade/downgrade
PlanSelectorequa-web/src/modules/subscriptions/components/PlanSelector.tsxCompare and select subscription tiers
UsageMeterequa-web/src/modules/subscriptions/components/UsageMeter.tsxShow usage vs. tier limits (e.g. members)

Routes

RouteComponentDescription
/settings/billingBillingDashboardBilling overview and payment methods
/settings/billing/checkoutCheckoutFlowNew subscription checkout
/settings/subscriptionSubscriptionManagerCurrent subscription management
/pricingPlanSelectorPublic pricing / plan comparison

State Management

Payment and subscription state is split across two module stores. The payments store manages payment profiles, invoices, and checkout state. The subscriptions store manages the active subscription, tier details, and usage. Both sync with Chargify-backed API endpoints.

6. Business Rules and Validation

RuleDescriptionEnforcement
BIL-001An organization cannot add members beyond its MemberLimits.memberLimitBackend
BIL-002A coupon code can only be redeemed once per userBackend
BIL-003Subscription downgrades take effect at the end of the current billing periodBackend (Chargify)
BIL-004Subscription upgrades take effect immediately with prorated billingBackend (Chargify)
BIL-005A valid payment profile is required before creating a subscriptionBoth
BIL-006Webhook payloads from Chargify must be signature-verified before processingBackend
BIL-007Failed payments trigger dunning workflow managed by Chargify; after final failure, subscription is suspendedBackend (Chargify)
BIL-008Waitlist position is immutable once assigned; users cannot change their positionBackend
BIL-009Cancelled subscriptions retain read-only access until the end of the paid periodBackend

7. Acceptance Criteria

  • AC-1: User can view available subscription plans with pricing on the public pricing page
  • AC-2: User can add a payment profile (credit card) and complete checkout
  • AC-3: Subscription is created in Chargify and reflected in the Equa billing dashboard
  • AC-4: Organization member limit is enforced based on subscription tier
  • AC-5: User can upgrade subscription; change takes effect immediately with proration
  • AC-6: User can downgrade subscription; change takes effect at next billing cycle
  • AC-7: User can cancel subscription; access remains until period end
  • AC-8: Coupon codes apply correct discounts and prevent duplicate redemption
  • AC-9: Chargify webhooks are received, verified, and correctly update local subscription state
  • AC-10: Failed payments trigger appropriate user notifications and dunning flow
  • AC-11: Waitlist users can check their position and are notified when promoted
  • AC-12: Invoice history is accessible from the billing dashboard

8. Risks and Edge Cases

RiskLikelihoodImpactMitigation
Chargify API downtime blocks subscription changesLowHighQueue failed requests for retry; show user-friendly error with retry option
Webhook delivery failure causes state driftMedHighPeriodic reconciliation job syncs Chargify state; idempotent webhook handler
Race condition: member added while downgrade pendingLowMedCheck limit at member-add time against current (not pending) tier
Coupon abuse via code sharingMedLowRate-limit redemptions; tie coupons to specific campaigns
Payment profile PCI complianceLowCriticalChargify handles card storage; Equa never stores raw card numbers
Waitlist position disputesLowLowPosition assigned server-side via auto-increment; no manual override

9. Dependencies

DependencyTypeStatus
SPEC 001: AuthenticationRequired beforeDRAFT
SPEC 002: Organization ManagementRequired beforeDRAFT
SPEC 012: Team Members and RolesIntegrates withDRAFT
Chargify (external)External integrationActive