Headlessly

Billing

Customer, Product, Plan, Price, Subscription, Invoice, Payment — revenue powered by Stripe.

All Billing entities are Stripe-backed. Creating a Subscription in headless.ly creates a Stripe subscription. Stripe webhooks flow back as events.

Customer

Stripe Customer — the billing identity. Links an Organization to Stripe.

import { Noun } from 'digital-objects'

export const Customer = Noun('Customer', {
  name: 'string!',
  email: 'string!#',
  organization: '-> Organization',
  stripeCustomerId: 'string##',
  subscriptions: '<- Subscription.customer[]',
  invoices: '<- Invoice.customer[]',
  payments: '<- Payment.customer[]',
  paymentMethod: 'string',
  currency: 'string',
  taxExempt: 'string',
})

Product

What you sell — software, services, addons, bundles.

export const Product = Noun('Product', {
  name: 'string!',
  slug: 'string##',
  description: 'string',
  tagline: 'string',
  type: 'Software | Service | Addon | Bundle',
  icon: 'string',
  image: 'string',
  features: 'string',
  highlights: 'string',
  status: 'Draft | Active | Archived',
  visibility: 'Public | Private | Hidden',
  featured: 'string',
  plans: '<- Plan.product[]',
  stripeProductId: 'string##',
})

Plan

Pricing tiers with trial configuration and feature limits.

export const Plan = Noun('Plan', {
  name: 'string!',
  slug: 'string##',
  description: 'string',
  product: '-> Product.plans',
  prices: '<- Price.plan[]',
  trialDays: 'number',
  features: 'string',
  limits: 'string',
  status: 'Draft | Active | Grandfathered | Archived',
  isDefault: 'string',
  isFree: 'string',
  isEnterprise: 'string',
  badge: 'string',
  order: 'number',
})

Price

Pricing configuration — maps to Stripe Prices.

export const Price = Noun('Price', {
  amount: 'number!',
  currency: 'string',
  interval: 'Monthly | Quarterly | Yearly | OneTime',
  intervalCount: 'number',
  originalAmount: 'number',
  discountPercent: 'number',
  active: 'string',
  plan: '-> Plan.prices',
  stripeId: 'string##',
})
IntervalDescription
MonthlyRecurring monthly charge
QuarterlyRecurring quarterly charge
YearlyRecurring yearly charge
OneTimeSingle purchase

Subscription

Active paying relationships.

export const Subscription = Noun('Subscription', {
  status: 'Active | PastDue | Canceled | Trialing | Paused | Incomplete',
  organization: '-> Organization.subscriptions',
  customer: '-> Customer.subscriptions',
  plan: '-> Plan',
  currentPeriodStart: 'datetime!',
  currentPeriodEnd: 'datetime!',
  cancelAtPeriodEnd: 'string',
  trialStart: 'datetime',
  trialEnd: 'datetime',
  startedAt: 'datetime!',
  canceledAt: 'datetime',
  pausedAt: 'datetime',
  resumesAt: 'datetime',
  endedAt: 'datetime',
  cancelReason: 'string',
  cancelFeedback: 'string',
  quantity: 'number',
  paymentMethod: 'string',
  collectionMethod: 'string',
  stripeSubscriptionId: 'string##',
  stripeCustomerId: 'string',
  pause: 'Paused',
  cancel: 'Cancelled',
  reactivate: 'Reactivated',
  upgrade: 'Upgraded',
  downgrade: 'Downgraded',
})
VerbEventDescription
pausePausedTemporarily suspend billing
cancelCancelledEnd the subscription
reactivateReactivatedRestart a cancelled subscription
upgradeUpgradedMove to a higher-value plan
downgradeDowngradedMove to a lower-value plan

Invoice

Bills — monthly/annual.

export const Invoice = Noun('Invoice', {
  number: 'string!##',
  organization: '-> Organization',
  customer: '-> Customer.invoices',
  subscription: '-> Subscription',
  subtotal: 'number!',
  tax: 'number',
  discount: 'number',
  total: 'number!',
  amountPaid: 'number',
  amountDue: 'number!',
  currency: 'string',
  status: 'Draft | Open | Paid | Void | Uncollectible',
  periodStart: 'datetime',
  periodEnd: 'datetime',
  issuedAt: 'datetime',
  dueAt: 'datetime',
  paidAt: 'datetime',
  voidedAt: 'datetime',
  lineItems: 'string',
  receiptUrl: 'string',
  pdfUrl: 'string',
  hostedUrl: 'string',
  stripeInvoiceId: 'string##',
  pay: 'Paid',
  void: 'Voided',
})
VerbEventDescription
payPaidMark invoice as paid
voidVoidedVoid the invoice

Payment

Money movement — charges, refunds.

export const Payment = Noun('Payment', {
  amount: 'number!',
  currency: 'string',
  status: 'Pending | Succeeded | Failed | Refunded',
  method: 'string',
  customer: '-> Customer.payments',
  invoice: '-> Invoice',
  stripePaymentId: 'string##',
  refund: 'Refunded',
})
VerbEventDescription
refundRefundedRefund the payment

On this page