T
Tenanto
Documentation / Billing

Billing

Updated Jan 25, 2026

Billing & Subscriptions Guide

Tenanto uses Stripe for subscription billing. This guide covers setting up Stripe and managing subscriptions.


Stripe Setup

Step 1: Create Stripe Account

  1. Go to stripe.com and create an account
  2. Complete business verification
  3. Enable test mode for development

Step 2: Get API Keys

  1. Go to DevelopersAPI keys
  2. Copy your keys:
    • Publishable key (pk_test_xxx or pk_live_xxx)
    • Secret key (sk_test_xxx or sk_live_xxx)

Step 3: Configure Environment

Add to your .env file:

# Stripe API Keys
STRIPE_KEY=pk_test_your_publishable_key
STRIPE_SECRET=sk_test_your_secret_key

# Webhook Secret (see Step 5)
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret

Step 4: Create Products and Prices

In Stripe Dashboard:

  1. Go to ProductsAdd product
  2. Create products for each plan:

Basic Plan:

Pro Plan:

Enterprise Plan:

Step 5: Configure Price IDs

Add to .env:

STRIPE_PRICE_BASIC=price_xxx_basic
STRIPE_PRICE_PRO=price_xxx_pro
STRIPE_PRICE_ENTERPRISE=price_xxx_enterprise

Step 6: Set Up Webhooks

  1. Go to DevelopersWebhooks
  2. Click Add endpoint
  3. Enter your webhook URL: https://yourdomain.com/stripe/webhook
  4. Select events to listen for:
    • customer.subscription.created
    • customer.subscription.updated
    • customer.subscription.deleted
    • customer.subscription.trial_will_end
    • invoice.payment_failed
    • invoice.paid
  5. Copy the Signing secret to STRIPE_WEBHOOK_SECRET

Plan Configuration

Plans are configured in config/billing.php:

'plans' => [
    'basic' => [
        'name' => 'Basic',
        'stripe_price_id' => env('STRIPE_PRICE_BASIC'),
        'features' => [
            'users' => 5,
            'teams' => 1,
            'projects' => 10,
            'api_access' => false,
            'custom_domain' => false,
            'priority_support' => false,
        ],
    ],
    'pro' => [
        'name' => 'Pro',
        'stripe_price_id' => env('STRIPE_PRICE_PRO'),
        'features' => [
            'users' => 25,
            'teams' => 5,
            'projects' => 50,
            'api_access' => true,
            'custom_domain' => true,
            'priority_support' => true,
        ],
    ],
    'enterprise' => [
        'name' => 'Enterprise',
        'stripe_price_id' => env('STRIPE_PRICE_ENTERPRISE'),
        'features' => [
            'users' => -1,  // -1 = unlimited
            'teams' => -1,
            'projects' => -1,
            'api_access' => true,
            'custom_domain' => true,
            'priority_support' => true,
        ],
    ],
],

Adding a Custom Plan

  1. Create product/price in Stripe
  2. Add environment variable: STRIPE_PRICE_CUSTOM=price_xxx
  3. Add plan to config/billing.php
  4. Add enum case to app/Domain/Billing/Enums/SubscriptionPlan.php

Trial Periods

Configure trial settings in config/billing.php:

'trial' => [
    'enabled' => true,
    'days' => 14,
],

How Trials Work

  1. New tenants automatically get a trial period
  2. Trial ends at trial_ends_at date
  3. 3 days before expiry, users receive a notification
  4. After trial, tenant must subscribe or loses premium features

Subscription Lifecycle

Creating a Subscription

  1. User visits /billing/plans
  2. Clicks "Subscribe" on a plan
  3. Redirected to Stripe Checkout
  4. After payment, redirected back
  5. Webhook confirms subscription
  6. Tenant's plan is updated

Upgrading/Downgrading

  1. User visits /billing
  2. Clicks "Change Plan"
  3. Selects new plan
  4. Stripe prorates the charge
  5. Plan changes immediately

Canceling

  1. User visits /billing
  2. Clicks "Cancel Subscription"
  3. Subscription marked for cancellation
  4. Access continues until period end
  5. After period, reverts to free tier

Grace Period

Configure grace period in config/billing.php:

'grace_days' => 3,

After subscription ends, tenants have grace days before losing premium features.


Checking Plan Features

In your code, check if a tenant has a feature:

use App\Domain\Billing\Services\PlanFeatureService;

// Check if tenant can add more users
$featureService = app(PlanFeatureService::class);
$canAddUser = $featureService->canUseFeature($tenant, 'users');

// Get feature limit
$maxUsers = $featureService->getFeatureLimit($tenant, 'users');
// Returns -1 for unlimited

Available Feature Checks


Testing with Stripe

Test Card Numbers

Card Number Use Case
Visa 4242 4242 4242 4242 Successful payment
Visa 4000 0000 0000 0002 Declined
Visa 4000 0000 0000 3220 3D Secure required

Use any future expiry date and any 3-digit CVC.

Testing Webhooks Locally

Use Stripe CLI:

# Install Stripe CLI
# https://stripe.com/docs/stripe-cli

# Forward webhooks to local
stripe listen --forward-to localhost/stripe/webhook

# Use the provided webhook secret

Customer Portal

Stripe Customer Portal allows users to:

Access via /billing → "Manage Subscription" button.

Configuring Customer Portal

  1. Go to Stripe Dashboard → Settings → Customer Portal
  2. Enable features:
    • Invoice history
    • Update payment methods
    • Cancel subscriptions
  3. Customize branding

Notifications

Tenanto sends automatic notifications for:

Event Notification
Subscription created SubscriptionCreated email
Trial ending (3 days) TrialEnding email
Payment failed PaymentFailed email
Subscription cancelled SubscriptionCancelled email

Customizing Notifications

Notification templates are in:

resources/views/vendor/notifications/

Or modify notification classes in:

app/Domain/Billing/Notifications/

Troubleshooting

"Webhook signature verification failed"

"No such price" error

Subscription not updating after payment


Going Live

Before going live:

  1. Replace test keys with live keys in .env
  2. Update webhook endpoint to live
  3. Test a real transaction with a small amount
  4. Enable live mode in Stripe Dashboard

Next Steps