Features
Organization
Billing & Subscriptions

Billing & Subscriptions

The OEC.SH platform offers flexible subscription plans with transparent pricing and comprehensive billing management. Every organization has a subscription that determines available features, resource quotas, and support levels.

Overview

The billing system manages:

  • Subscription Plans: Five tiers from Free to Enterprise with different features and limits
  • Billing Intervals: Monthly or annual billing (20% discount on annual)
  • Stripe Integration: PCI-compliant payment processing via Stripe Checkout and Customer Portal
  • Plan Changes: Instant upgrades, scheduled downgrades with validation
  • Subscription Lifecycle: Trial periods, pausing, resuming, and cancellation
  • Billable Items: Additional charges for BYOS servers, platform environments, and add-ons
  • Usage Tracking: Real-time monitoring of resource consumption
  • Invoices: Automated invoice generation with PDF downloads

All billing operations require appropriate permissions (org.billing.view or org.billing.manage).


Subscription Plans

Plan Overview

OEC.SH offers five subscription plans designed for different organization sizes and needs:

PlanMonthly PriceAnnual PriceAnnual Savings
Free$0$0-
Starter$29$278.4020% ($69.60)
Professional$79$758.4020% ($189.60)
Business$199$1,910.4020% ($477.60)
EnterpriseCustomCustomNegotiable

Note: All annual plans include a 20% discount compared to monthly billing.


Free Plan

Perfect for individuals and small teams getting started with Odoo deployment.

Pricing: $0/month (forever free)

Included Resources:

  • 1 BYOS server included
  • 0 platform environments included
  • Maximum 2 projects
  • Maximum 5 environments
  • Maximum 2 team members

Per-Environment Limits:

  • CPU: 2.0 cores max
  • RAM: 4,096 MB (4 GB) max
  • Disk: 40 GB max

Features:

  • Daily backups (7-day retention)
  • Basic monitoring (Netdata)
  • Community support
  • Standard backup frequency
  • No cloning/staging environments
  • No custom domains
  • No SSO authentication
  • No SLA guarantee

Best For: Personal projects, testing, proof-of-concept deployments


Starter Plan

Ideal for small businesses running a few Odoo instances.

Pricing:

  • Monthly: $29/month
  • Annual: 278.40/year(278.40/year (23.20/month equivalent)

Included Resources:

  • 1 BYOS server included
  • 0 platform environments included
  • Maximum 10 projects
  • Maximum 10 environments
  • Maximum 5 team members

Per-Environment Limits:

  • CPU: 4.0 cores max
  • RAM: 8,192 MB (8 GB) max
  • Disk: 80 GB max

Features:

  • Daily backups (30-day retention)
  • Full monitoring (Netdata)
  • Email support (48-hour response)
  • Clone/staging environments enabled
  • Custom domains and SSL
  • No SSO authentication
  • No SLA guarantee

Best For: Small businesses, development agencies with 3-5 clients


Professional Plan

Designed for growing teams managing multiple production Odoo deployments.

Pricing:

  • Monthly: $79/month
  • Annual: 758.40/year(758.40/year (63.20/month equivalent)

Included Resources:

  • 3 BYOS servers included
  • 1 platform environment included (Small size)
  • Maximum 25 projects
  • Maximum 50 environments
  • Maximum 15 team members

Per-Environment Limits:

  • CPU: 8.0 cores max
  • RAM: 16,384 MB (16 GB) max
  • Disk: 160 GB max

Features:

  • Hourly backups (90-day retention)
  • Advanced monitoring (Netdata with alerting)
  • Priority email support (24-hour response)
  • Clone/staging environments enabled
  • Custom domains and SSL
  • PostgreSQL read replicas (Odoo 18/19 only)
  • SSO authentication (SAML, OAuth)
  • 99.5% uptime SLA

Best For: Development agencies, mid-size businesses with 10-20 deployments


Business Plan

For larger organizations with extensive Odoo infrastructure.

Pricing:

  • Monthly: $199/month
  • Annual: 1,910.40/year(1,910.40/year (159.20/month equivalent)

Included Resources:

  • 5 BYOS servers included
  • 2 platform environments included (Small size)
  • Maximum 100 projects
  • Maximum 200 environments
  • Maximum 50 team members

Per-Environment Limits:

  • CPU: 16.0 cores max
  • RAM: 32,768 MB (32 GB) max
  • Disk: 320 GB max

Features:

  • Continuous backups (1-year retention)
  • Enterprise monitoring (Netdata + custom dashboards)
  • Priority phone support (12-hour response)
  • Clone/staging environments enabled
  • Custom domains and SSL
  • PostgreSQL read replicas (Odoo 18/19 only)
  • SSO authentication (SAML, OAuth)
  • 99.9% uptime SLA
  • Dedicated account manager

Best For: Large agencies, enterprises with 50+ deployments


Enterprise Plan

Fully customizable plan for organizations with specific requirements.

Pricing: Custom (contact sales)

Included Resources:

  • Unlimited BYOS servers
  • Unlimited platform environments
  • Unlimited projects
  • Unlimited team members
  • Custom resource limits per environment

Features:

  • Custom backup retention (multi-year)
  • White-label monitoring options
  • Dedicated support (4-hour SLA)
  • All Professional/Business features
  • Custom integrations
  • On-premise deployment options
  • 99.95% uptime SLA
  • Dedicated infrastructure
  • Custom contract terms

Best For: Large enterprises, ISVs, hosting providers

Contact: sales@oec.sh for custom pricing


Plan Features Matrix

Comprehensive comparison of features across all plans:

FeatureFreeStarterProfessionalBusinessEnterprise
Limits
Max Projects21025100Unlimited
Max Environments51050200Unlimited
Max Servers1135Unlimited
Max Team Members251550Unlimited
Included BYOS Servers1135Custom
Included Platform Envs0012Custom
Per-Environment Resources
Max CPU Cores2.04.08.016.0Custom
Max RAM (MB)4,0968,19216,38432,768Custom
Max Disk (GB)4080160320Custom
Backup & Monitoring
Backup FrequencyDailyDailyHourlyContinuousCustom
Backup Retention7 days30 days90 days1 yearCustom
Monitoring (Netdata)✅ Basic✅ Full✅ Advanced✅ Enterprise✅ White-label
Real-time Alerts
Advanced Features
Clone/Staging Envs
Custom Domains/SSL
PostgreSQL Replicas
SSO Authentication
Multi-cloud Backup
Support & SLA
Support LevelCommunityEmailPriority EmailPhoneDedicated
Response Time-48 hours24 hours12 hours4 hours
Uptime SLA--99.5%99.9%99.95%
Account Manager

Viewing Current Plan

Billing Overview Page

Access your organization's billing information:

Navigation: Dashboard → Settings → Billing

Permission Required: org.billing.view

The billing overview displays:

  1. Current Plan Details

    • Plan name and tier
    • Billing interval (monthly/annual)
    • Current status (trial, active, past_due, etc.)
    • Trial end date (if applicable)
    • Next billing date
  2. Usage Metrics

    • Projects: X used / Y limit
    • Environments: X used / Y limit
    • Servers: X used / Y limit
    • Team members: X used / Y limit
    • CPU allocation: X cores used / Y cores limit
    • RAM allocation: X GB used / Y GB limit
    • Disk allocation: X GB used / Y GB limit
  3. Cost Breakdown

    • Base plan cost: $X/month
    • Additional items cost: $Y/month
    • Total monthly cost: $Z/month
    • Annual savings (if applicable): $A/year
  4. Billable Items

    • List of BYOS servers with pricing
    • List of platform environments with pricing
    • List of add-ons with pricing

API Endpoint

GET /api/v1/organizations/{org_id}/billing/summary

Authentication: Required (JWT token)

Authorization: org.billing.view permission

Response: 200 OK

{
  "subscription": {
    "id": "uuid",
    "plan": "professional",
    "status": "active",
    "billing_interval": "annual",
    "trial_ends_at": null,
    "current_period_end": "2025-01-15T00:00:00Z",
    "pending_plan": null,
    "can_pause": true
  },
  "plan_limits": {
    "max_servers": 3,
    "max_environments": 50,
    "max_projects": 25,
    "max_members": 15,
    "included_byos_servers": 3,
    "included_platform_envs": 1
  },
  "usage": {
    "servers": 2,
    "environments": 12,
    "projects": 5,
    "members": 8
  },
  "billing": {
    "base_plan_cost": 79.00,
    "items_cost": 19.00,
    "total_monthly": 98.00,
    "currency": "USD"
  },
  "items": [
    {
      "id": "uuid",
      "type": "byos_server",
      "name": "Production Server",
      "tier": "standard",
      "price": 19.00,
      "quantity": 1
    }
  ]
}

Upgrading Plan

Upgrade Process

Plan upgrades take effect immediately with prorated billing.

Steps:

  1. Navigate to: Dashboard → Settings → Billing
  2. Click "Upgrade Plan"
  3. Select higher tier plan
  4. Choose billing interval (monthly/annual)
  5. Review proration calculation
  6. Confirm upgrade
  7. Stripe processes payment adjustment
  8. Quotas updated immediately
  9. New features enabled instantly

Permission Required: org.billing.manage

Restrictions:

  • Can only upgrade to higher-tier plans
  • Cannot downgrade using upgrade endpoint
  • Must be organization owner or admin

Proration Calculation

When upgrading mid-cycle, Stripe automatically calculates proration:

Example: Upgrading from Starter (29/mo)toProfessional(29/mo) to Professional (79/mo) on day 15 of 30-day cycle:

  1. Unused Starter time: 15 days × (29/30)=29/30) = 14.50 credit
  2. Professional cost for remaining period: 15 days × (79/30)=79/30) = 39.50
  3. Immediate charge: 39.5039.50 - 14.50 = $25.00

Result: You pay 25immediately,then25 immediately, then 79 on next billing date.

API Endpoint

POST /api/v1/organizations/{org_id}/billing/subscription/upgrade

Authentication: Required (JWT token)

Authorization: org.billing.manage permission

Request Body:

{
  "new_plan": "professional",
  "billing_interval": "annual"
}

Response: 200 OK

{
  "id": "uuid",
  "organization_id": "uuid",
  "base_plan": "professional",
  "status": "active",
  "billing_interval": "annual",
  "monthly_price": 79.00,
  "annual_price": 758.40,
  "is_annual": true,
  "current_period_start": "2024-12-15T00:00:00Z",
  "current_period_end": "2025-12-15T00:00:00Z",
  "stripe_customer_id": "cus_XXX",
  "stripe_subscription_id": "sub_XXX",
  "pending_plan": null
}

Error Responses:

  • 400 Bad Request: Invalid plan, already on higher plan, or validation error
  • 403 Forbidden: Missing org.billing.manage permission
  • 502 Bad Gateway: Stripe API error

Downgrading Plan

Downgrade Process

Plan downgrades are scheduled for the end of the current billing period to avoid service disruption.

Steps:

  1. Navigate to: Dashboard → Settings → Billing
  2. Click "Change Plan"
  3. Select lower tier plan
  4. Review usage validation
  5. Confirm resource requirements met
  6. Schedule downgrade for period end
  7. Continue using current plan until then
  8. Downgrade applies automatically at renewal

Permission Required: org.billing.manage

Important: Current usage must fit within new plan limits.

Downgrade Validation

Before downgrading, the system validates:

  1. Projects: Current count ≤ new plan limit
  2. Environments: Current count ≤ new plan limit
  3. Servers: Current count ≤ new plan limit
  4. Team Members: Current count ≤ new plan limit

If validation fails, you must remove resources before downgrading:

Error: You have 15 environments but Starter allows 10.
Please remove 5 environments first.

Scheduled Downgrade

After scheduling:

  • Current plan remains active until period end
  • pending_plan field shows scheduled plan
  • pending_plan_effective_at shows when it takes effect
  • Full access to current plan features until then
  • No immediate billing changes

Example Timeline:

  • December 15: Request downgrade Professional → Starter
  • December 15-31: Continue using Professional features
  • December 31: Downgrade automatically applied
  • January 1: Billed at Starter rate ($29/month)

API Endpoint

POST /api/v1/organizations/{org_id}/billing/subscription/downgrade

Authentication: Required (JWT token)

Authorization: org.billing.manage permission

Request Body:

{
  "new_plan": "starter",
  "billing_interval": "monthly"
}

Response: 200 OK

{
  "id": "uuid",
  "organization_id": "uuid",
  "base_plan": "professional",
  "status": "active",
  "billing_interval": "monthly",
  "monthly_price": 79.00,
  "pending_plan": "starter",
  "pending_plan_effective_at": "2024-12-31T23:59:59Z",
  "current_period_end": "2024-12-31T23:59:59Z"
}

Error Responses:

  • 400 Bad Request: Usage exceeds new plan limits, invalid plan, or already on lower plan
  • 403 Forbidden: Missing org.billing.manage permission

Cancelling Subscription

Cancellation Options

Two cancellation modes:

  1. End of Period (Recommended): Cancel at billing cycle end, retain access until then
  2. Immediate: Cancel now, lose access immediately (not recommended)

Permission Required: org.billing.manage

End-of-Period Cancellation

Process:

  1. Navigate to: Dashboard → Settings → Billing
  2. Click "Cancel Subscription"
  3. Select "Cancel at period end"
  4. Provide cancellation reason (optional)
  5. Confirm cancellation
  6. Continue using service until period end
  7. Subscription expires at renewal date
  8. Data retained for 30 days

Timeline:

  • Cancellation date: Immediately marked as "cancelling"
  • Service access: Until current_period_end
  • Final billing: No future charges
  • Data retention: 30 days after expiration
  • Data deletion: Automatic after retention period

Immediate Cancellation

Process:

  1. Navigate to: Dashboard → Settings → Billing
  2. Click "Cancel Subscription"
  3. Select "Cancel immediately"
  4. Acknowledge loss of access
  5. Confirm cancellation
  6. Subscription terminated instantly
  7. No refunds for unused time
  8. Data retained for 30 days

Warning: Immediate cancellation:

  • Stops all environments immediately
  • No refund for remaining billing period
  • Lose access to all features instantly
  • Not recommended for production use

Reactivation

To reactivate a cancelled subscription:

  1. Must be within 30-day retention period
  2. Navigate to: Dashboard → Settings → Billing
  3. Click "Reactivate Subscription"
  4. Select new plan
  5. Add payment method
  6. Subscription reactivates immediately

After 30 days, all data is permanently deleted and cannot be recovered.

API Endpoint

POST /api/v1/organizations/{org_id}/billing/subscription/cancel

Authentication: Required (JWT token)

Authorization: org.billing.manage permission

Request Body:

{
  "immediate": false,
  "reason": "Migrating to on-premise solution"
}

Response: 200 OK

{
  "id": "uuid",
  "organization_id": "uuid",
  "base_plan": "professional",
  "status": "cancelling",
  "cancel_at_period_end": true,
  "cancelled_at": "2024-12-15T10:30:00Z",
  "cancel_at": "2024-12-31T23:59:59Z",
  "current_period_end": "2024-12-31T23:59:59Z",
  "data_deletion_at": null
}

Immediate Cancellation Response:

{
  "status": "expired",
  "cancel_at_period_end": false,
  "cancelled_at": "2024-12-15T10:30:00Z",
  "data_deletion_at": "2025-01-14T10:30:00Z"
}

Payment Methods

Stripe Customer Portal

OEC.SH uses Stripe Customer Portal for PCI-compliant payment management.

Features:

  • Add/remove credit/debit cards
  • Update default payment method
  • View payment history
  • Download invoice PDFs
  • Update billing information
  • Manage subscription directly

Security:

  • PCI-DSS Level 1 compliant
  • No card data stored on OEC.SH servers
  • Stripe handles all payment processing
  • HMAC signature verification on webhooks

Accessing Customer Portal

From Dashboard:

  1. Navigate to: Dashboard → Settings → Billing
  2. Click "Manage Payment Methods"
  3. Redirected to Stripe Customer Portal
  4. Make changes in Stripe interface
  5. Click "Return to OEC.SH" when done

Permission Required: org.billing.manage

Managing Payment Methods

In Stripe Customer Portal:

  1. Add Payment Method:

    • Click "Add payment method"
    • Enter card details
    • Set as default (optional)
    • Save changes
  2. Update Default Method:

    • Select existing card
    • Click "Set as default"
    • Future charges use this card
  3. Remove Payment Method:

    • Select card to remove
    • Click "Remove"
    • Cannot remove if only payment method
  4. Update Billing Address:

    • Click "Update information"
    • Edit billing details
    • Save changes

API Endpoint

POST /api/v1/organizations/{org_id}/billing/portal

Authentication: Required (JWT token)

Authorization: org.billing.manage permission

Request Body:

{
  "return_url": "https://app.oec.sh/dashboard/settings/billing"
}

Response: 200 OK

{
  "portal_url": "https://billing.stripe.com/session/XXX"
}

Usage:

// Frontend code
const response = await api.post(
  `/organizations/${orgId}/billing/portal`,
  { return_url: window.location.href }
);
 
// Redirect to Stripe
window.location.href = response.portal_url;

Billing History

Invoice Management

All invoices are automatically generated by Stripe and stored in OEC.SH.

Invoice Information:

  • Invoice number (unique identifier)
  • Billing period (start and end dates)
  • Line items (base plan, billable items, add-ons)
  • Subtotal, tax, and total amounts
  • Payment status (paid, pending, failed)
  • Payment date (if paid)
  • PDF download link

Viewing Invoices

In Dashboard:

  1. Navigate to: Dashboard → Settings → Billing
  2. Scroll to "Billing History" section
  3. View list of all invoices
  4. Click "Download PDF" for any invoice

Permission Required: org.billing.view or org.billing.invoices

Invoice Statuses

StatusDescription
draftInvoice created but not finalized
pendingAwaiting payment
paidSuccessfully paid
failedPayment attempt failed
refundedPayment refunded
voidInvoice cancelled

Failed Payment Handling

When a payment fails:

  1. Immediate Actions:

    • Subscription status: activepast_due
    • Email notification sent
    • Stripe retries payment automatically
  2. Retry Schedule (Stripe default):

    • Retry 1: 3 days after failure
    • Retry 2: 5 days after failure
    • Retry 3: 7 days after failure
    • Retry 4: 9 days after failure
  3. After All Retries Fail:

    • Subscription status: past_duesuspended
    • All environments stopped
    • Data preserved for 30 days
    • Email notification sent
  4. Resolution:

    • Update payment method in Customer Portal
    • Stripe automatically retries with new card
    • Subscription reactivates on successful payment
    • Environments restart automatically

Accessing Stripe Customer Portal

From the Customer Portal you can:

  • View complete invoice history
  • Download PDF invoices
  • See upcoming charges
  • View payment receipts
  • Export invoice data

Invoice Records in Database

Invoices are synchronized from Stripe via webhooks:

Webhook Events:

  • invoice.paid: Invoice successfully paid
  • invoice.payment_failed: Payment failed
  • invoice.finalized: Invoice finalized and sent

Invoice Fields:

  • Stripe invoice ID (reference to Stripe)
  • Organization and subscription references
  • Invoice number (human-readable)
  • Period start and end dates
  • Amount, currency, and payment status
  • Line items (JSONB)
  • PDF URL (hosted by Stripe)
  • Payment timestamps

Usage Tracking

Current Usage Overview

The platform tracks resource consumption in real-time for billing and quota enforcement.

Tracked Metrics:

  1. Entity Counts:

    • Active projects
    • Active environments
    • Active servers (VMs)
    • Active team members
  2. Resource Allocation:

    • Total CPU cores allocated
    • Total RAM allocated (MB)
    • Total disk allocated (GB)
  3. Usage Percentages:

    • Projects: X / Y limit (Z%)
    • Environments: X / Y limit (Z%)
    • CPU: X / Y cores (Z%)
    • RAM: X / Y GB (Z%)
    • Disk: X / Y GB (Z%)

Usage Dashboard

Location: Dashboard → Settings → Billing → Usage

Visual Indicators:

  • Green (0-70%): Normal usage
  • Yellow (70-90%): Approaching limit
  • Red (90-100%): At or over limit

Warning Thresholds:

  • 80% usage: Warning email sent
  • 95% usage: Critical alert email
  • 100% usage: Quota exceeded, creation blocked

Usage by Resource Type

Projects Usage:

Active Projects: 8 / 25 (32%)
Status: Normal
Action: Can create 17 more projects

Environments Usage:

Active Environments: 45 / 50 (90%)
Status: Critical
Action: Approaching limit, consider upgrading

Resource Allocation:

CPU: 24.5 cores / 50 cores (49%)
RAM: 92 GB / 120 GB (77%)
Disk: 850 GB / 1,000 GB (85%)
Status: Normal

Usage Breakdown by Project

View resource usage per project:

ProjectEnvironmentsCPURAMDisk
Production5 envs12 cores48 GB400 GB
Staging3 envs6 cores24 GB200 GB
Development2 envs4 cores16 GB150 GB
Total10 envs22 cores88 GB750 GB

API Endpoint

GET /api/v1/organizations/{org_id}/billing/usage

Authentication: Required (JWT token)

Authorization: org.billing.view permission

Response: 200 OK

{
  "usage": {
    "projects": {
      "used": 8,
      "limit": 25,
      "percentage": 32
    },
    "environments": {
      "used": 45,
      "limit": 50,
      "percentage": 90
    },
    "servers": {
      "used": 3,
      "limit": 3,
      "percentage": 100
    },
    "members": {
      "used": 12,
      "limit": 15,
      "percentage": 80
    },
    "cpu": {
      "used": 24.5,
      "limit": 50.0,
      "percentage": 49,
      "unit": "cores"
    },
    "ram": {
      "used": 94208,
      "limit": 122880,
      "percentage": 77,
      "unit": "MB"
    },
    "disk": {
      "used": 850,
      "limit": 1000,
      "percentage": 85,
      "unit": "GB"
    }
  },
  "warnings": [
    {
      "type": "environments",
      "message": "Approaching environment limit (90%). Consider upgrading plan.",
      "severity": "warning"
    },
    {
      "type": "servers",
      "message": "Server limit reached (100%). Upgrade plan to add more servers.",
      "severity": "critical"
    }
  ]
}

Quota Enforcement

Hard Limits vs Soft Limits

Hard Limits (Cannot exceed):

  • Maximum projects per plan
  • Maximum environments per plan
  • Maximum servers per plan
  • Maximum team members per plan
  • Per-environment CPU limit
  • Per-environment RAM limit
  • Per-environment disk limit

Soft Limits (Can temporarily exceed):

  • None - all limits are hard enforced

Quota Exceeded Behavior

When attempting to create resources that exceed quota:

Example: Creating Project Beyond Limit

POST /api/v1/organizations/{org_id}/projects

Response: 400 Bad Request

{
  "detail": "Project limit reached (25). Please upgrade your plan or contact support.",
  "error_code": "QUOTA_EXCEEDED",
  "current_usage": 25,
  "limit": 25,
  "resource_type": "projects"
}

Example: Creating Environment with Excessive Resources

POST /api/v1/projects/{project_id}/environments
{
  "cpu_cores": 10.0,
  "ram_mb": 20480,
  "disk_gb": 200
}

Response: 400 Bad Request

{
  "detail": "Requested CPU (10.0 cores) exceeds per-environment limit (8.0 cores) for Professional plan.",
  "error_code": "RESOURCE_LIMIT_EXCEEDED",
  "requested": 10.0,
  "limit": 8.0,
  "resource_type": "cpu_cores"
}

Warnings Before Hitting Limits

Warning Emails:

  1. 80% Usage Warning:

    • Subject: "Approaching resource limit for [Resource Type]"
    • Content: Current usage, limit, and upgrade recommendation
    • Frequency: Once when threshold crossed
  2. 95% Critical Alert:

    • Subject: "Critical: Resource limit almost reached"
    • Content: Urgent upgrade recommendation
    • Frequency: Daily until resolved
  3. 100% Quota Exceeded:

    • Subject: "Resource limit reached - Upgrades blocked"
    • Content: Cannot create more resources, must upgrade
    • Frequency: Per failed creation attempt

Quota Integration with QuotaService

All resource creation endpoints call QuotaService before proceeding:

# Example quota check before creating environment
quota_service = QuotaService(db)
quota_check = await quota_service.can_create_environment(
    organization_id=org_id,
    cpu_cores=requested_cpu,
    ram_mb=requested_ram,
    disk_gb=requested_disk
)
 
if not quota_check.allowed:
    raise HTTPException(
        status_code=400,
        detail=quota_check.reason
    )
 
# Proceed with environment creation

Quota Validation Steps:

  1. Count Check: Verify entity count (projects, environments, etc.) under limit
  2. Resource Check: Verify requested resources don't exceed per-environment limits
  3. Total Check: Verify organization's total allocated resources under quota
  4. Replica Check: If creating replica, count 30% CPU/RAM, 100% disk towards quota

Stripe Webhooks

Webhook Endpoint

OEC.SH receives real-time updates from Stripe via webhooks.

Endpoint: POST /api/v1/webhooks/stripe

Authentication: HMAC signature verification

Webhook Secret: Configured in Stripe Dashboard and STRIPE_WEBHOOK_SECRET environment variable

Event Types Handled

The platform processes these Stripe webhook events:

1. checkout.session.completed

Trigger: Customer completes Stripe Checkout

Actions:

  • Link Stripe customer ID to organization
  • Link Stripe subscription ID to subscription
  • Activate subscription (status: trialactive)
  • Update plan details from checkout metadata
  • Send welcome email

2. customer.subscription.updated

Trigger: Subscription modified in Stripe

Actions:

  • Update subscription status (active, past_due, canceled, etc.)
  • Update billing period dates (current_period_start, current_period_end)
  • Update cancellation flags (cancel_at_period_end, cancel_at)
  • Sync plan changes
  • Sync billing interval changes

3. customer.subscription.deleted

Trigger: Subscription cancelled in Stripe

Actions:

  • Set subscription status to expired
  • Record cancellation timestamp
  • Schedule data deletion (30 days)
  • Stop all environments
  • Send cancellation confirmation email

4. invoice.paid

Trigger: Invoice successfully paid

Actions:

  • Create invoice record in database
  • Link to subscription
  • Store invoice PDF URL
  • Clear past_due status if set
  • Record payment timestamp
  • Send payment receipt email

5. invoice.payment_failed

Trigger: Payment attempt failed

Actions:

  • Set subscription status to past_due
  • Create failed invoice record
  • Send payment failure notification
  • Stripe automatically retries per retry schedule

6. customer.subscription.paused

Trigger: Subscription paused (via pause API)

Actions:

  • Set subscription status to paused
  • Record pause timestamp
  • Extract resume date from webhook
  • Stop all environments
  • Send pause confirmation email

7. customer.subscription.resumed

Trigger: Subscription resumed after pause

Actions:

  • Set subscription status to active
  • Clear pause timestamps
  • Restart environments
  • Send resume confirmation email

Webhook Security

Signature Verification:

import stripe
 
# Verify webhook signature
try:
    event = stripe.Webhook.construct_event(
        payload,
        stripe_signature_header,
        STRIPE_WEBHOOK_SECRET
    )
except stripe.error.SignatureVerificationError:
    # Invalid signature - reject webhook
    return 401

Security Measures:

  • HMAC-SHA256 signature verification
  • Reject webhooks with invalid/missing signatures
  • Timestamp validation (reject old webhooks)
  • Idempotency handling (ignore duplicate events)

Webhook Retry Logic

Stripe Retry Behavior:

  • Retry failed webhooks automatically
  • Exponential backoff (1 hour, 2 hours, 4 hours, etc.)
  • Max retries: 3 attempts over 3 days
  • After 3 days: Webhook marked as failed in Stripe Dashboard

OEC.SH Handling:

  • Return 200 OK even on processing errors
  • Log errors for manual investigation
  • Don't raise exceptions (prevents infinite retries)
  • Use database transactions to ensure consistency

Monitoring Webhooks

In Stripe Dashboard:

  • View all webhook delivery attempts
  • See success/failure status
  • Retry failed webhooks manually
  • View webhook payload and response

In OEC.SH Logs:

INFO: Received Stripe webhook
INFO: Processing customer.subscription.updated (event_id: evt_XXX)
INFO: Subscription updated (subscription_id: uuid, status: active)

Annual Billing

Annual Discount

Discount: 20% off compared to monthly billing

Calculation:

  • Monthly billing: Plan price × 12 months
  • Annual billing: Plan price × 12 months × 0.8
  • Savings: Plan price × 12 months × 0.2

Annual vs Monthly Comparison

PlanMonthlyAnnual (Monthly × 12)Annual PriceSavings
Starter$29/mo$348/year$278.40/year$69.60 (20%)
Professional$79/mo$948/year$758.40/year$189.60 (20%)
Business$199/mo$2,388/year$1,910.40/year$477.60 (20%)

Example: Professional plan annual savings:

Monthly billing: $79 × 12 = $948/year
Annual billing: $758.40/year
Savings: $189.60/year (equivalent to 2.4 months free)

Switching to Annual Billing

From Monthly to Annual:

  1. Navigate to: Dashboard → Settings → Billing
  2. Click "Switch to Annual Billing"
  3. Review savings calculation
  4. Confirm switch
  5. Stripe creates prorated invoice
  6. Annual billing starts immediately

Proration: When switching mid-cycle, you receive credit for unused monthly time:

Current date: December 15
Monthly billing: $79/month, next due December 31
Days remaining: 16 days

Credit for unused monthly: 16 days × ($79/30) = $42.13
Annual billing cost: $758.40
Immediate charge: $758.40 - $42.13 = $716.27

Next billing: December 15, 2025 ($758.40)

Annual Commitment Terms

Commitment:

  • Billed upfront for full year
  • Cannot downgrade during annual term
  • Can upgrade anytime (prorated)
  • Cancellation: No refund for unused time

Refund Policy:

  • No refunds after 14-day trial
  • Cancel before annual renewal to avoid next charge
  • Cancellation takes effect at term end
  • Access maintained until term expiration

Auto-Renewal:

  • Annual subscriptions auto-renew yearly
  • Email reminder sent 7 days before renewal
  • Can disable auto-renewal in Stripe Customer Portal
  • If disabled, subscription expires at term end

Enterprise Plan

Custom Pricing

Enterprise plans are fully customized to organization needs.

Pricing Factors:

  • Number of environments
  • Resource requirements (CPU, RAM, disk)
  • Number of team members
  • Support SLA requirements
  • Backup retention requirements
  • Custom feature needs
  • Contract term length (1-3 years)
  • Volume discounts

Typical Enterprise Pricing: 500500-5,000/month depending on scale

Volume Discounts

EnvironmentsDiscount
200-50010% off
500-1,00015% off
1,000-2,00020% off
2,000+25% off

Dedicated Support

Enterprise Support Includes:

  • Dedicated Account Manager: Single point of contact for all needs
  • 24/7 Phone Support: Direct phone line with 4-hour response SLA
  • Quarterly Business Reviews: Strategic planning and optimization sessions
  • Custom Onboarding: Dedicated onboarding team and migration assistance
  • Training Sessions: Live training for administrators and developers
  • Architecture Review: Review deployment architecture and provide recommendations
  • Priority Feature Requests: Influence product roadmap

SLA Guarantees

Uptime SLA: 99.95% uptime guarantee

Calculation:

99.95% uptime = 0.05% downtime per year
= 4.38 hours downtime per year
= 21.9 minutes downtime per month

SLA Credits:

Uptime %Credit
< 99.95%10% monthly fee
< 99.90%25% monthly fee
< 99.50%50% monthly fee
< 99.00%100% monthly fee

Exclusions:

  • Scheduled maintenance (with 7-day notice)
  • Customer-caused issues
  • Third-party service failures
  • Force majeure events

Contact Sales Workflow

To Request Enterprise Plan:

  1. Visit: https://oec.sh/enterprise (opens in a new tab)
  2. Fill out enterprise contact form:
    • Organization name
    • Number of Odoo instances
    • Number of users
    • Current hosting setup
    • Timeline for migration
    • Special requirements
  3. Sales team responds within 1 business day
  4. Schedule discovery call (30 minutes)
  5. Receive custom proposal within 1 week
  6. Negotiate terms and finalize contract
  7. Sign SOW (Statement of Work)
  8. Begin onboarding process

Contact: sales@oec.sh or +1 (555) 123-4567


Permissions

Billing Permissions

Access to billing features is controlled by organization permissions.

org.billing.view

Description: View billing information and invoices

Allows:

  • View current plan and subscription details
  • View usage metrics and quota limits
  • View billable items list
  • View billing summary
  • View invoice history
  • Download invoice PDFs

Does NOT Allow:

  • Changing plans
  • Managing payment methods
  • Cancelling subscription
  • Pausing subscription

Default Roles:

  • org_owner: ✅ Has permission
  • org_admin: ✅ Has permission
  • org_member: ❌ No permission
  • project_admin: ❌ No permission
  • project_member: ❌ No permission

org.billing.manage

Description: Manage subscriptions and payment methods

Allows:

  • All permissions from org.billing.view
  • Upgrade plan
  • Downgrade plan
  • Start trial
  • Cancel subscription
  • Pause subscription
  • Resume subscription
  • Create checkout sessions (initiate payment)
  • Access Stripe Customer Portal
  • Update payment methods
  • Update billing information

Does NOT Allow:

  • Viewing other organizations' billing

Default Roles:

  • org_owner: ✅ Has permission
  • org_admin: ✅ Has permission (can be removed if desired)
  • org_member: ❌ No permission
  • project_admin: ❌ No permission
  • project_member: ❌ No permission

org.billing.invoices

Description: Download and export invoices (accounting teams)

Allows:

  • View invoice history
  • Download invoice PDFs
  • Export invoice data
  • View payment receipts

Does NOT Allow:

  • Managing subscriptions
  • Changing payment methods

Default Roles:

  • Typically granted to accounting/finance team members
  • Not included by default (must be explicitly granted)

Permission Hierarchy

org.billing.manage (full billing control)
  ├─ org.billing.view (read-only access)
  └─ org.billing.invoices (invoice access)

Checking Permissions

In Frontend:

import { useAbilities } from '@/contexts/AbilityContext';
 
function BillingPage() {
  const { can } = useAbilities({ organizationId });
 
  // Check view permission
  if (!can('org.billing.view')) {
    return <AccessDenied />;
  }
 
  // Check manage permission for actions
  const canManage = can('org.billing.manage');
 
  return (
    <div>
      <BillingSummary />
      {canManage && (
        <div>
          <UpgradeButton />
          <CancelButton />
        </div>
      )}
    </div>
  );
}

In Backend:

from core.permissions import check_permission
 
# Check view permission
has_permission = await check_permission(
    db=db,
    user=current_user,
    permission_code="org.billing.view",
    organization_id=organization_id
)
 
if not has_permission:
    raise HTTPException(
        status_code=403,
        detail="You don't have permission to view billing."
    )

API Reference

Complete Endpoint Documentation

All billing API endpoints with full details.

1. List Plans

GET /api/v1/billing/plans

Description: Get list of all available subscription plans

Authentication: None (public endpoint)

Response: 200 OK

{
  "plans": [
    {
      "plan": "free",
      "name": "Free",
      "description": "Perfect for getting started",
      "monthly_price": 0.00,
      "annual_price": 0.00,
      "max_servers": 1,
      "max_environments": 5,
      "max_projects": 2,
      "max_members": 2,
      "included_byos_servers": 1,
      "included_platform_envs": 0,
      "backup_frequency": "daily",
      "clone_enabled": false,
      "monitoring_enabled": true,
      "priority_support": false,
      "custom_domains": false,
      "sso_enabled": false,
      "is_active": true
    }
  ]
}

2. Get Pricing

GET /api/v1/billing/prices

Description: Get complete pricing information for all products

Authentication: None (public endpoint)

Response: 200 OK

{
  "base_plans": [...],
  "byos_tiers": [
    {
      "tier": "standard",
      "name": "Standard",
      "price": 19,
      "max_environments": 5
    }
  ],
  "environment_sizes": [
    {
      "size": "small",
      "name": "Small",
      "price": 39,
      "cpu": 1.0,
      "ram_mb": 2048,
      "disk_gb": 20
    }
  ],
  "addons": [
    {
      "addon": "storage_gb",
      "price": 0.10,
      "unit": "monthly"
    }
  ]
}

3. Get Subscription

GET /api/v1/organizations/&#123;org_id&#125;/billing/subscription

Description: Get subscription details for organization

Authentication: Required (JWT)

Authorization: org.billing.view

Response: 200 OK (see "Viewing Current Plan" section)

4. Get Billing Summary

GET /api/v1/organizations/&#123;org_id&#125;/billing/summary

Description: Get complete billing summary with usage and costs

Authentication: Required (JWT)

Authorization: org.billing.view

Response: 200 OK (see "Viewing Current Plan" section)

5. Start Trial

POST /api/v1/organizations/&#123;org_id&#125;/billing/subscription/trial

Description: Start 14-day trial with Professional features

Authentication: Required (JWT)

Authorization: org.billing.manage

Response: 200 OK (subscription object)

6. Upgrade Plan

POST /api/v1/organizations/&#123;org_id&#125;/billing/subscription/upgrade

Description: Upgrade to higher plan (immediate)

Authentication: Required (JWT)

Authorization: org.billing.manage

Request Body: (see "Upgrading Plan" section)

Response: 200 OK (subscription object)

7. Downgrade Plan

POST /api/v1/organizations/&#123;org_id&#125;/billing/subscription/downgrade

Description: Downgrade to lower plan (end of period)

Authentication: Required (JWT)

Authorization: org.billing.manage

Request Body: (see "Downgrading Plan" section)

Response: 200 OK (subscription object)

8. Pause Subscription

POST /api/v1/organizations/&#123;org_id&#125;/billing/subscription/pause

Description: Pause subscription for 1-3 months

Authentication: Required (JWT)

Authorization: org.billing.manage

Request Body:

{
  "duration_months": 1
}

Response: 200 OK (subscription object with paused_at and resumes_at)

9. Resume Subscription

POST /api/v1/organizations/&#123;org_id&#125;/billing/subscription/resume

Description: Resume paused subscription

Authentication: Required (JWT)

Authorization: org.billing.manage

Response: 200 OK (subscription object with status: "active")

10. Cancel Subscription

POST /api/v1/organizations/&#123;org_id&#125;/billing/subscription/cancel

Description: Cancel subscription (immediate or end of period)

Authentication: Required (JWT)

Authorization: org.billing.manage

Request Body: (see "Cancelling Subscription" section)

Response: 200 OK (subscription object)

11. Create Checkout Session

POST /api/v1/organizations/&#123;org_id&#125;/billing/checkout

Description: Create Stripe checkout session for payment

Authentication: Required (JWT)

Authorization: org.billing.manage

Request Body:

{
  "plan": "professional",
  "billing_interval": "annual",
  "success_url": "https://app.oec.sh/dashboard/settings/billing?success=true",
  "cancel_url": "https://app.oec.sh/dashboard/settings/billing?cancelled=true"
}

Response: 200 OK

{
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_XXX",
  "session_id": "cs_XXX"
}

Usage: Redirect user to checkout_url to complete payment

12. Create Customer Portal Session

POST /api/v1/organizations/&#123;org_id&#125;/billing/portal

Description: Create Stripe Customer Portal session

Authentication: Required (JWT)

Authorization: org.billing.manage

Request Body: (see "Payment Methods" section)

Response: 200 OK (see "Payment Methods" section)

13. List Billable Items

GET /api/v1/organizations/&#123;org_id&#125;/billing/items?active_only=true

Description: List all billable items (servers, environments, add-ons)

Authentication: Required (JWT)

Authorization: org.billing.view

Query Parameters:

  • active_only (boolean): Filter to active items only (default: true)

Response: 200 OK

[
  {
    "id": "uuid",
    "organization_id": "uuid",
    "subscription_id": "uuid",
    "item_type": "byos_server",
    "reference_id": "uuid",
    "reference_name": "Production Server",
    "tier": "standard",
    "quantity": 1,
    "unit_price": 19.00,
    "status": "active",
    "stripe_subscription_item_id": "si_XXX",
    "started_at": "2024-01-01T00:00:00Z",
    "cancelled_at": null,
    "created_at": "2024-01-01T00:00:00Z"
  }
]

Best Practices

Monitor Usage Proactively

  1. Check Usage Weekly:

    • Review Dashboard → Settings → Billing → Usage
    • Look for resources approaching limits
    • Plan upgrades before hitting quotas
  2. Set Up Alerts:

    • Enable email notifications for quota warnings
    • Monitor 80% and 95% threshold alerts
    • Act on warnings before reaching 100%
  3. Track Trends:

    • Monitor usage growth over time
    • Predict when upgrades will be needed
    • Budget for plan changes

Set Up Billing Alerts

Recommended Alerts:

  • 80% Quota Usage: Warning notification
  • 95% Quota Usage: Critical notification
  • 100% Quota Usage: Blocking notification
  • Payment Failed: Immediate action required
  • Trial Ending: 3-day advance notice
  • Subscription Renewal: 7-day advance notice

Email Preferences:

  • Navigate to: Dashboard → Settings → Notifications
  • Enable billing alerts
  • Configure alert recipients (multiple email addresses)
  • Set alert frequency (immediate, daily digest)

Review Plan Periodically

Quarterly Review Checklist:

  1. Usage Assessment:

    • ✅ Review current resource usage
    • ✅ Compare against plan limits
    • ✅ Identify underutilized capacity
    • ✅ Identify overages or constraints
  2. Cost Optimization:

    • ✅ Calculate total monthly cost
    • ✅ Compare against actual usage
    • ✅ Identify cost-saving opportunities
    • ✅ Consider annual billing for savings
  3. Plan Alignment:

    • ✅ Current plan matches needs?
    • ✅ Should upgrade for more capacity?
    • ✅ Should downgrade to save costs?
    • ✅ Enterprise plan needed?
  4. Feature Utilization:

    • ✅ Using all paid features?
    • ✅ Need features from higher tiers?
    • ✅ Can eliminate unused features?

Annual Billing for Savings

When to Choose Annual:

  • ✅ Stable resource needs for next 12 months
  • ✅ Want to save 20% on subscription cost
  • ✅ Predictable budget planning
  • ✅ Committed to platform long-term

When to Choose Monthly:

  • ❌ Uncertain resource needs
  • ❌ Testing platform or plan fit
  • ❌ May need to scale up/down frequently
  • ❌ Prefer payment flexibility

Switching Strategy:

  • Start with monthly billing during onboarding (first 3 months)
  • Switch to annual after confirming plan fits needs
  • Maximize savings once usage stabilizes

Right-Sizing Resources

Environment Sizing:

  • Start with smaller environments
  • Monitor performance metrics
  • Upgrade when consistently over 80% CPU/RAM
  • Don't over-provision "just in case"

Plan Selection:

  • Choose plan based on actual usage + 20-30% buffer
  • Don't jump to highest tier prematurely
  • Upgrade when consistently at 85-90% of limits
  • Review annually for optimization

Troubleshooting

Payment Failed

Symptoms:

  • Email: "Payment failed for your OEC.SH subscription"
  • Subscription status: past_due
  • Dashboard shows payment warning banner

Causes:

  • Expired credit card
  • Insufficient funds
  • Card declined by bank
  • Incorrect billing address
  • Bank fraud protection triggered

Resolution Steps:

  1. Update Payment Method:

    • Go to: Dashboard → Settings → Billing
    • Click "Manage Payment Methods"
    • Add new payment method or update existing
    • Set as default payment method
  2. Contact Your Bank:

    • Verify card has sufficient funds
    • Check for fraud alerts or blocks
    • Confirm international payments allowed (Stripe processes in US)
  3. Retry Payment:

    • Stripe automatically retries with updated payment method
    • Or click "Retry Payment" in Customer Portal
  4. Contact Support:

    • If payment still fails after updating card
    • Email: billing@oec.sh
    • Provide organization ID and error details

Timeline:

  • Day 0: Payment fails, subscription status → past_due
  • Day 3: Automatic retry #1
  • Day 5: Automatic retry #2
  • Day 7: Automatic retry #3
  • Day 9: Automatic retry #4
  • Day 10: All retries exhausted, subscription → suspended
  • Day 40: Data deletion if not resolved

Quota Exceeded After Downgrade

Symptoms:

  • Scheduled downgrade fails to apply
  • Error: "Current usage exceeds new plan limits"
  • Subscription stays on current plan
  • Email notification about downgrade failure

Causes:

  • Resources added after scheduling downgrade
  • Forgot to remove resources before downgrade
  • Didn't account for all resource types

Resolution Steps:

  1. Check Current Usage:

    • Go to: Dashboard → Settings → Billing → Usage
    • Compare usage vs new plan limits
    • Identify resources to remove
  2. Remove Excess Resources:

    • Environments: Delete or deactivate environments
    • Projects: Archive or delete unused projects
    • Servers: Remove excess BYOS servers
    • Members: Remove inactive team members
  3. Reschedule Downgrade:

    • After removing resources, downgrade will apply automatically
    • Or manually retry downgrade via API/UI

Example:

Target Plan: Starter (10 environments max)
Current Usage: 15 environments
Action Required: Remove 5 environments

Steps:
1. Navigate to: Dashboard → Projects
2. Identify 5 least-used environments
3. Stop and delete each environment
4. Verify usage now shows 10 environments
5. Downgrade will apply at period end

Webhook Delivery Failures

Symptoms:

  • Stripe shows webhook delivery failures
  • Subscription status out of sync
  • Invoices missing from OEC.SH
  • Payment status not updated

Causes:

  • OEC.SH server temporarily unavailable
  • Network connectivity issues
  • Webhook secret misconfigured
  • Firewall blocking Stripe IPs

Resolution Steps:

  1. Check Webhook Status in Stripe:

    • Log in to Stripe Dashboard
    • Navigate to: Developers → Webhooks
    • View webhook endpoint status
    • Check recent delivery attempts
  2. Retry Failed Webhooks:

    • In Stripe Dashboard, click on failed webhook
    • Click "Resend" button
    • Verify successful delivery
  3. Verify Webhook Configuration:

    • Webhook URL: https://api.oec.sh/api/v1/webhooks/stripe
    • Events to send: All subscription and invoice events
    • Webhook secret matches STRIPE_WEBHOOK_SECRET env var
  4. Check Server Logs:

    • Look for webhook processing errors
    • Verify HMAC signature validation passing
    • Check for database transaction errors
  5. Contact Support:

    • If webhooks continue failing
    • Email: support@oec.sh
    • Provide webhook event IDs and timestamps

Portal Session Expired

Symptoms:

  • Clicking "Manage Payment Methods" shows error
  • Stripe Customer Portal link doesn't work
  • Error: "This session has expired"

Causes:

  • Portal session expires after 24 hours
  • Browser cached old session URL
  • Session created but not used immediately

Resolution Steps:

  1. Generate New Session:

    • Go back to: Dashboard → Settings → Billing
    • Click "Manage Payment Methods" again
    • New session created with fresh 24-hour expiration
  2. Use Session Immediately:

    • Click link and complete changes within 24 hours
    • Don't bookmark portal URLs (they expire)

Note: Portal sessions are single-use and time-limited for security.


Related Documentation


Support

For billing and payment assistance:

Response Times:

  • Free: Community support only
  • Starter: 48 hours (email)
  • Professional: 24 hours (email)
  • Business: 12 hours (phone + email)
  • Enterprise: 4 hours (dedicated support)