Solavel Solavel Docs

Subscriptions and billing

docs/solavel/subscriptions.md

Who can use this: Organization Owners (and Solavel staff via the admin shell). Managers and Members cannot reach billing. URL / Route: /portal/orgs/{organization}/billing/*, /portal/orgs/{organization}/plan*, /admin/subscriptions/* Plan / feature gate: Always on (the prices and limits depend on the plans configured by Solavel staff)

Purpose

This page covers everything money-related: choosing or changing a plan, paying with a card via Tap, viewing invoices, downloading receipts, replacing a card, and canceling. Billing is centralized — even Solabooks subscriptions are paid here, not inside Solabooks.

How a subscription is structured

  • An organization has at most one active subscription at a time.
  • A subscription is linked to a project plan — for example, "Solabooks / Premium" — and a billing cycle (monthly).
  • The card-charging step is handled by the Tap payment gateway. After Tap finishes, it redirects back to portal.orgs.billing.tap.return, and the receipt-side webhook arrives at POST /api/webhooks/tap.

Behind the scenes the table is client_subscriptions(client_id, project_plan_id, status, starts_at, billing_cycle_starts_at, billing_cycle_ends_at). The active project plans are seeded from database/seeders/ProjectPlanSeeder.php.

Step by step

Pick or upgrade a plan

  1. From the sidebar, open Billing → Subscriptions (portal.orgs.billing.subscriptions.index).
  2. Click Change plan (or Upgrade). You arrive at portal.orgs.plan.show.
  3. Pick a tier. The form posts to portal.orgs.plan.update. If the tier costs more than the current one, you go through Tap to authorize the new card charge. If it is free or already paid for, the change applies immediately.

Cancel at the end of the period

  1. Open Billing → Subscriptions.
  2. Click Cancel at period end (POST portal.orgs.billing.subscription.cancel_at_period_end). Access continues until billing_cycle_ends_at, then the subscription becomes inactive.
  3. To undo a pending cancellation, click Resume (POST portal.orgs.billing.subscription.resume) before the period ends.

There is also an immediate-cancel route at portal.orgs.plan.cancel. It ends access right away — use only when the user explicitly asks for it.

Replace or remove a card

  • List cards: portal.orgs.billing.payment_methods.index.
  • Replace the default: POST portal.orgs.billing.payment_methods.replace. Tap captures the new card.
  • Set default: POST portal.orgs.billing.payment_methods.{paymentMethod}.default.
  • Remove a card: DELETE portal.orgs.billing.payment_methods.{paymentMethod}.destroy. You cannot remove the only card on file while a subscription is active.

View invoices and payments

  • Invoices list: portal.orgs.billing.invoices.index.
  • Invoice detail / download PDF: portal.orgs.billing.invoices.show.
  • Payments list: portal.orgs.billing.payments.index.

Pay an outstanding invoice

If a card charge fails, the invoice goes to pending and the subscription marks past_due. Use Billing → Overview (portal.orgs.billing.show) to see the prompt and click Pay now, which calls POST portal.orgs.billing.tap.initiate to start a new Tap charge.

Tap payment flow

Every card charge follows the same shape:

  1. POST portal.orgs.billing.tap.initiate — Solavel asks Tap for a redirect URL.
  2. The user is sent to Tap's hosted checkout.
  3. Tap calls GET portal.orgs.billing.tap.return after the user completes (or cancels) the form.
  4. Asynchronously, Tap calls the webhook at POST /api/webhooks/tap. Solavel verifies the signature inside the controller, marks the invoice paid, and activates the subscription.

The same flow is used for the new-organization wizard at portal.orgs.create.checkout.

Risk: POST /api/webhooks/tap is reachable without auth — only Tap's signature check protects it. Do not expose the webhook URL outside of Tap's own dashboard.

Plan upgrade flow during onboarding

When a new organization is being created, the plan choice happens inside the Create organization wizard (see Organizations). Free plans skip the checkout step. Paid plans go through Tap and return to portal.orgs.create.checkout.return, which finishes activation.

Admin-side subscription tools

For Solavel staff:

  • /admin/subscriptions (admin.subscriptions.index) — searchable list of every subscription.
  • /admin/subscriptions/{subscription} (admin.subscriptions.show) — detail view with status timeline and links into the related client and organization.

Direct subscription mutations (cancel, reactivate) for staff use the client.subscriptions.* API:

  • GET /subscriptions (client.subscriptions.index)
  • POST /subscriptions/{subscription}/cancel (client.subscriptions.cancel)
  • POST /subscriptions/{subscription}/reactivate (client.subscriptions.reactivate)

These are used from inside the admin shell — they are not advertised in the sidebar.

Note: an older /admin/subscriptions/sync-logs route was removed in November 2025; the sidebar entry was dropped in commit 60d5d85.

Permissions / restrictions

  • See billing pages: requires manage-subscriptions permission. Held by Owners and by Solavel staff. Managers and Members do not see the Billing menu.
  • Cancel at period end / resume: requires manage-subscriptions.
  • Replace card: requires manage-subscriptions.
  • View invoices: requires manage-subscriptions or view-client-subscription.
  • Admin-side subscription read: view-client-subscription. Admin-side cancel/edit: edit-client-subscription.

Common problems

  • "Your card was declined." — Tap shows the reason in its return page. Try a different card from portal.orgs.billing.payment_methods.index.
  • The subscription says past_due — the renewal charge failed. Pay the open invoice from portal.orgs.billing.show.
  • After Tap return, you are still on a free plan. The webhook has not yet fired or has failed. Check admin.system-event-logs.index for the Tap webhook entry. The page auto-refreshes once the webhook arrives.
  • Cannot cancel because there is an outstanding invoice. Pay the invoice first, or contact Solavel support.

Related

Source: docs/solavel/subscriptions.md ← All documentation