Shipflash logoDocs

Customer Billing

Customer billing separates what customers can see from what operators can investigate.

On this page

Customer and operator surfaces

SurfaceWhat it showsNotes
/billing/historyCustomer-owned purchases, payments, invoices, refunds, and subscription changes in one activity container.Uses normalized activity rows and does not expose raw webhook payloads or operator controls.
/api/billing/portalProvider-hosted portal launch for the signed-in customer.Uses the active provider when a customer record exists.
/billing/customersOperator list of provider customers.Super-admin oriented customer operations.
/billing/customers/[providerCustomerId]Customer ledger detail, subscriptions, invoices, charges, refunds, disputes, entitlements, and webhooks.Provider capability differences are shown in UI.
/api/system/billing/exportCustomer CSV and Stripe invoice CSV exports.Invoice export is Stripe-only.

Code-backed customer billing map

Customer-facing billing activity

Signed-in customers see account-owned normalized billing activity, not raw provider payloads.

  • src/app/(dashboard)/billing/page.tsx
  • src/features/billing/server/queries/activity.ts
  • src/features/billing/ui/hub/activity.tsx

Portal access

Portal launch uses the active provider and current customer record through a server service.

  • src/app/api/billing/portal/route.ts
  • src/features/billing/server/services/portal.ts
  • src/features/billing/server/providers/stripe/portal.ts
  • src/features/billing/server/providers/lemonsqueezy/portal.ts

Operator investigation

Customer detail pages, sync, refunds, and exports are admin-oriented workflows with provider capability differences.

  • src/app/(dashboard)/billing/customers/[providerCustomerId]/page.tsx
  • src/features/billing/server/queries/customer-history.ts
  • src/features/billing/server/services/customer-sync.ts
  • src/features/billing/server/services/admin-refund.ts
  • src/features/billing/server/export/service.ts

Customer billing support flow

  1. 1

    Start from local history

    Customer and operator pages read projected subscriptions, orders, invoices, charges, refunds, disputes, and entitlements from local tables.

    • src/features/billing/server/queries/current-profile-history.ts
    • src/features/billing/server/queries/customer-history.ts
  2. 2

    Use portal for customer changes

    The portal service resolves the active provider and customer before returning a provider-hosted URL.

    • src/features/billing/server/services/portal.ts
  3. 3

    Sync provider state deliberately

    Provider refresh and refunds stay behind admin services so support actions remain auditable and provider-aware.

    • src/features/billing/server/services/customer-sync.ts
    • src/features/billing/server/services/admin-refund.ts
  4. 4

    Export from local projections

    CSV exports are assembled from local customer and invoice rows after authorization checks.

    • src/features/billing/server/export/request/auth.ts
    • src/features/billing/server/export/service.ts

Portal request schema

Source: src/features/billing/server/services/portal.ts

Portal requests accept only relative return paths so provider redirects stay inside the app.

ts
const PortalBodySchema = z.object({
  return_path: z
    .string()
    .trim()
    .max(300)
    .refine((value) => value.startsWith("/") && !value.startsWith("//"), {
      message: "return_path must be a relative path",
    })
    .optional(),
  source: z.string().trim().max(80).optional().default("dashboard_billing_hub"),
});

Support workflow

  • Start from the customer detail page when investigating billing support.
  • Use Stripe sync only when the active provider supports it.
  • Use local projections as the dashboard source of truth after webhooks arrive.
  • Keep refunds and disputes behind super-admin checks.