LogoShip Superfast

Billing

Billing page — subscription plans, checkout, customer portal, and payment history.

Overview

The billing page lives at /dashboard/billing (app/dashboard/billing/page.tsx). It shows the team's current plan, available upgrades, and payment history. Billing is tied to the active team, not individual users.

All payments go through Dodo Payments. There are no in-app purchases — checkout happens on Dodo's hosted page, and webhooks sync the data back to Convex.

What each role sees

RoleSees plansCan upgradeCan manage billingSees history
OwnerYesYesYesYes
AdminYesYesYesYes
MemberNoNoNoYes (if payments exist)

Members see a notice saying "Plan upgrades are managed by your team owner or admin."

Page sections

Team billing summary

A card showing the team name, current plan badge (Free / Pro / Max), and member count. Owners and admins also see a "Manage Billing" button that opens the Dodo customer portal.

Plan cards

Two cards side by side — Pro and Max — each showing:

  • Plan name and price
  • Feature list with checkmarks
  • Action button (Upgrade / Current Plan / Switch)

The button logic depends on your current plan:

Current planPro card buttonMax card button
Free"Upgrade to Pro" → checkout"Upgrade to Max" → checkout
Pro"Current Plan" (disabled)"Upgrade to Max" → portal
Max"Switch to Pro" → portal"Current Plan" (disabled)

Checkout flow

For first-time subscribers (upgrading from Free):

const createCheckout = useAction(api.payments.createCheckout);

const result = await createCheckout({
  product_cart: [{ product_id: plan.productId, quantity: 1 }],
  returnUrl: window.location.origin + "/dashboard/billing",
  teamId,
});

// Redirect to Dodo's hosted checkout page
window.location.href = result.checkout_url;

After payment, the user is redirected back to /dashboard/billing. A webhook from Dodo updates the team's plan in the database.

Plan changes (upgrade/downgrade)

For users already on a paid plan, changes go through the Dodo customer portal:

const getPortal = useAction(api.payments.getCustomerPortal);

const result = await getPortal({ send_email: false, teamId });
window.open(result.portal_url, "_blank");

The portal handles proration, plan switching, and cancellation.

Billing history

A table of all payments for the active team:

  • Plan badge
  • Amount (formatted from cents)
  • Date
  • Status badge (Active, Succeeded, Cancelled, Refunded, etc.)

Key files

FilePurpose
app/dashboard/billing/page.tsxBilling page (plans, checkout, history)
components/providers/team-provider.tsxuseTeam() — active team context

On this page