Skip to main content
Software applications typically ship with a billing page. This allows customers to change plan, cancel subscription and view their usage. The customer endpoint returns the current state of the customer, including their active subscriptions, one-time purchases, and feature balances.

Pricing table

When building a pricing table, you need to know what each plan means for the current customer — is it an upgrade, a downgrade, or their current plan? Is a free trial available? Pass a customerId when listing plans and each plan will include a customerEligibility object:
FieldTypeDescription
attachAction"activate" | "upgrade" | "downgrade" | "purchase" | "none"What happens when this plan is attached
status"active" | "scheduled" | undefinedThe customer’s current relationship to this plan
trialAvailablebooleanWhether the customer is eligible for the plan’s free trial
import { useListPlans, useCustomer } from "autumn-js/react";

const labels = {
  activate: "Subscribe",
  upgrade: "Upgrade",
  downgrade: "Downgrade",
  purchase: "Purchase",
};

const getLabel = (eligibility) => {

  if (eligibility?.attachAction === "none") {
    return eligibility.status === "scheduled" ? "Plan Scheduled" : "Current plan";
  }

  if (labels[eligibility?.attachAction]) {
    return labels[eligibility.attachAction];
  }

  return "Get started";
};

export default function PricingPage() {
  const { data: plans } = useListPlans();
  const { attach } = useCustomer();

  return plans?.map((plan) => (
    <button
      key={plan.id}
      disabled={plan.customerEligibility?.attachAction === "none"}
      onClick={() => attach({ planId: plan.id, redirectMode: "always" })}
    >
      {getLabel(plan.customerEligibility)}
    </button>
  ));
}
The React useListPlans hook automatically includes customer context from AutumnProvider, so customerEligibility is populated on every plan without extra configuration.

Switching plans

Switching plans uses billing.attach. See Attaching Plans for the full guide.
import { useCustomer } from "autumn-js/react";

export default function UpgradeButton() {
  const { attach } = useCustomer();

  return (
    <button onClick={() => attach({ planId: "pro" })}>
      Upgrade to Pro
    </button>
  );
}

Cancelling a plan

Cancel a subscription using billing.update with a cancelAction. See Subscription Lifecycle for the full guide on immediate vs end-of-cycle cancellations.
import { useCustomer } from "autumn-js/react";

const { updateSubscription } = useCustomer();

// Cancel at end of billing cycle
await updateSubscription({ 
  planId: "pro", 
  cancelAction: "cancel_end_of_cycle" 
});

Uncancelling a plan

If a subscription has a pending cancellation, a scheduled downgrade, or a scheduled plan switch, you can reverse it with cancelAction: "uncancel". A subscription is pending cancellation when canceledAt is not null while the subscription is still active.
import { useCustomer } from "autumn-js/react";

export default function BillingPage() {
  const { data: customer, updateSubscription } = useCustomer();

  const cancellingSub = customer?.subscriptions.find(
    (sub) => sub.status === "active" && sub.canceledAt !== null
  );

  return cancellingSub ? (
    <button onClick={() => updateSubscription({ 
      planId: cancellingSub.planId, 
      cancelAction: "uncancel" 
    })}>
      Keep plan
    </button>
  ) : null;
}

Active plans

Display the plan the user is currently on. Users can have multiple active subscriptions and purchases (e.g., main plan and add-ons).
  • subscriptions - Free and paid recurring plans
  • purchases - One-off plans (e.g., credit top-ups)
import { useCustomer } from "autumn-js/react";

const { data: customer } = useCustomer();

const active = customer?.subscriptions.filter(
  (sub) => sub.status === "active"
);

console.log(active?.map((sub) => sub.planId).join(", "));

Usage balances

Metered features have granted, usage, and remaining fields. Use these to display current usage and remaining balance.
import { useCustomer } from "autumn-js/react";

const { data: customer, refetch } = useCustomer();

const messages = customer?.balances.messages;

console.log(`${messages?.remaining} / ${messages?.granted}`);

// After tracking usage or changing plans, call refetch() to update balances
await refetch();

Stripe billing portal

The Stripe billing portal lets users manage their payment method, view past invoices, and cancel their plan.
Enable the billing portal in your Stripe settings.
import { useCustomer } from "autumn-js/react";

const { openCustomerPortal } = useCustomer();

// Opens Stripe billing portal in current tab
await openCustomerPortal({ 
  returnUrl: "https://your-app.com/billing" 
});

Usage timeseries chart

Autumn provides aggregate time series queries for usage data. Pass the response to a charting library like Recharts.
import { useAggregateEvents } from "autumn-js/react";

const { list, total } = useAggregateEvents({
  featureId: "messages",
  range: "30d",
});

// list: [{ period: 1234567890, values: { messages: 42 } }, ...]
// total: { messages: { count: 100, sum: 500 } }
You can also use the events.list method to get the raw event data and display it in a table.
Next: Deploy to production Once your billing page is in place, go through the production checklist to launch with real payments.

Deploy to production

A checklist to go live with confidence