StarterApp Docs
Troubleshooting

Billing

Resolve UseAutumn feature checks and subscription issues

Billing issues stem from UseAutumn configuration, feature ID mismatches, or environment variable problems. Verify configuration in this order: environment variables, feature IDs, customer identification.

Feature Check Returns False

Symptoms: ctx.check({ featureId }) returns allowed: false for users with active subscriptions.

Diagnosis: Feature ID mismatch, configuration not pushed, or customer not identified in UseAutumn.

Solution:

Verify feature ID matches autumn.config.ts:

autumn.config.ts
export const dashboard = feature({
  id: "dashboard", // Must match exactly in ctx.check()
  name: "Dashboard Access",
  type: "boolean",
});

Push configuration to UseAutumn:

npx atmn push

Verify AUTUMN_SECRET_KEY is set:

# Check local env
grep AUTUMN_SECRET_KEY .env.local

# Sync to Convex
npx convex env set AUTUMN_SECRET_KEY <same-value>

Customer Not Identified

Symptoms: Feature checks fail even after checkout. UseAutumn dashboard shows no customer record.

Diagnosis: identify function in convex/autumn.ts not returning customer data.

Solution:

Verify identify function returns proper structure:

convex/autumn.ts
export const autumn = new Autumn(components.autumn, {
  secretKey: process.env.AUTUMN_SECRET_KEY,
  identify: async (ctx) => {
    const user = await ctx.auth.getUserIdentity();
    if (!user) return null;
    return {
      customerId: user.subject, // BetterAuth user ID
      customerData: {
        name: user.name,
        email: user.email,
      },
    };
  },
});

Checkout Not Working

Symptoms: billing.checkout() throws errors or redirects to wrong URL.

Diagnosis: Product ID doesn't exist in UseAutumn or environment not configured.

Solution:

Verify product exists in autumn.config.ts:

autumn.config.ts
export const paid = product({
  id: "starterapp-demo-paid", // Use this exact ID
  name: "Paid",
  items: [
    priceItem({ price: 10, interval: "month" }),
    featureItem({ feature_id: dashboard.id, included_usage: 1 }),
  ],
});

Push to UseAutumn:

npx atmn push

Use correct product ID in code:

await billing.checkout({
  productId: "starterapp-demo-paid", // Must match autumn.config.ts
  successUrl: window.location.origin + "/dashboard",
});

Usage Tracking Not Working

Symptoms: ctx.track() doesn't decrement usage limits. Users can exceed quotas.

Diagnosis: Feature not configured as type: "single_use" or tracking not called after check.

Solution:

Configure feature for usage tracking:

autumn.config.ts
export const apiCalls = feature({
  id: "api_calls",
  name: "API Calls",
  type: "single_use", // Required for usage tracking
});

Track usage after successful action:

convex/api.ts
export const callApi = userAction({
  handler: async (ctx) => {
    const result = await ctx.check({ featureId: "api_calls" });
    if (!result.data?.allowed) {
      throw new AppError("USAGE_EXCEEDED", "API call limit reached");
    }

    // Perform action
    const data = await externalApiCall();

    // Track usage AFTER successful action
    await ctx.track({ featureId: "api_calls", value: 1 });

    return data;
  },
});

Subscription Status Stale

Symptoms: billing.useCustomer() shows old subscription data after changes.

Diagnosis: Client-side cache not invalidating after subscription updates.

Solution:

UseAutumn updates happen via webhooks. Verify webhook endpoint is accessible:

Check Convex logs for webhook deliveries. Force refresh customer data by signing out and back in.