← back to NationalPaperHangers

views/admin/billing.ejs

135 lines

<%- include('../partials/head', { title, admin: true }) %>
<%- include('partials/admin-header') %>
<section class="admin-main">
  <header class="admin-page-head"><h1>Billing</h1></header>
  <% if (upgradePrompt) { %><div class="callout callout-warn">Calendar bookings require Pro or higher. Pick a plan to enable customer self-booking.</div><% } %>
  <div class="callout">
    <strong>Current tier:</strong> <%= installer.tier %> · <strong>Status:</strong> <%= installer.subscription_status %>
    <% if (installer.current_period_end) { %> · renews <%= new Date(installer.current_period_end).toLocaleDateString() %><% } %>
    <% if (!stripeLive) { %><br><span class="muted">Stripe is not yet configured. Upgrades below run in mock mode and toggle the tier locally for testing.</span><% } %>
  </div>

  <h2>Plans</h2>
  <div class="pricing-grid">
    <% ['pro','signature','enterprise'].forEach(function(tier){ %>
      <article class="price-card <%= installer.tier === tier ? 'price-card-current' : '' %>">
        <h3><%= tier.charAt(0).toUpperCase() + tier.slice(1) %></h3>
        <% priceTable.filter(p => p.tier === tier).forEach(function(p){ %>
          <p class="price"><%= p.label %></p>
          <form method="post" action="/admin/billing/checkout" class="inline-form">
            <input type="hidden" name="_csrf" value="<%= csrfToken %>">
            <input type="hidden" name="tier" value="<%= p.tier %>">
            <input type="hidden" name="cadence" value="<%= p.cadence %>">
            <button type="submit" class="btn <%= installer.tier === tier ? 'btn-ghost' : 'btn-primary' %> btn-sm">
              <%= installer.tier === tier ? 'Current' : 'Choose ' + p.cadence %>
            </button>
          </form>
        <% }); %>
      </article>
    <% }); %>
  </div>

  <% if (installer.stripe_customer_id) { %>
    <form method="post" action="/admin/billing/portal" class="inline-form" style="margin-top:24px">
      <input type="hidden" name="_csrf" value="<%= csrfToken %>">
      <button type="submit" class="btn btn-ghost">Manage subscription in Stripe portal ↗</button>
    </form>
  <% } %>

  <h2 style="margin-top:48px">Booking deposits &amp; payouts</h2>
  <%
    var _payoutsActive = !!installer.stripe_account_id && !!installer.stripe_account_charges_enabled;
    var _depositLabel = '$' + ((defaultDepositCents || 9900) / 100).toFixed(0);
    var _feePct = ((defaultPlatformFeeBps || 2500) / 100).toFixed(0);
  %>
  <p class="muted">
    NPH collects a <strong><%= _depositLabel %></strong> reservation deposit from every customer at booking. NPH retains a <strong><%= _feePct %>%</strong> lead-coordination fee; the balance routes to your bank via Stripe Connect.
  </p>

  <% if (!_payoutsActive) { %>
    <div class="callout callout-warn">
      <strong>Payouts are not set up.</strong> Deposits collected for your bookings are held in NPH's account and will transfer once you complete Stripe onboarding.
      <form method="post" action="/admin/connect/onboard" class="inline-form" style="margin-top:8px">
        <input type="hidden" name="_csrf" value="<%= csrfToken %>">
        <button type="submit" class="btn btn-primary btn-sm">Set up payouts →</button>
      </form>
    </div>
  <% } else { %>
    <div class="callout callout-ok">
      <strong>✓ Payouts active.</strong> Connect account <code><%= (installer.stripe_account_id || '').slice(0,12) %>…</code>
      <% if (installer.stripe_account_payouts_enabled) { %> · Payouts enabled<% } %>
      <form method="post" action="/admin/connect/onboard" class="inline-form" style="margin-top:8px">
        <input type="hidden" name="_csrf" value="<%= csrfToken %>">
        <button type="submit" class="btn btn-ghost btn-sm">Manage in Stripe ↗</button>
      </form>
    </div>
  <% } %>

  <% if (installerMarket) { %>
    <div class="stat-grid" style="margin-top:24px">
      <div class="stat">
        <span class="stat-label">Deposits paid</span>
        <span class="stat-value">$<%= (installerMarket.deposits_paid_cents/100).toFixed(2) %></span>
        <span class="stat-sub muted"><%= installerMarket.count_paid %> bookings</span>
      </div>
      <div class="stat">
        <span class="stat-label">Platform fee</span>
        <span class="stat-value">$<%= (installerMarket.platform_fee_cents/100).toFixed(2) %></span>
        <span class="stat-sub muted">NPH retained</span>
      </div>
      <div class="stat">
        <span class="stat-label">Your share</span>
        <span class="stat-value">$<%= ((installerMarket.deposits_paid_cents - installerMarket.platform_fee_cents)/100).toFixed(2) %></span>
        <span class="stat-sub muted"><%= _payoutsActive ? 'transferred via Stripe' : 'held — set up payouts' %></span>
      </div>
      <% if (installerMarket.count_pending > 0) { %>
        <div class="stat">
          <span class="stat-label">Awaiting payment</span>
          <span class="stat-value"><%= installerMarket.count_pending %></span>
          <span class="stat-sub muted">customer hasn't confirmed</span>
        </div>
      <% } %>
    </div>
  <% } %>

  <% if (typeof platformMarket !== 'undefined' && platformMarket) { %>
    <h2 style="margin-top:48px">Platform totals (NPH staff)</h2>
    <p class="muted">Rolled up across every studio with a paid booking.</p>
    <div class="stat-grid" style="margin-top:16px">
      <div class="stat">
        <span class="stat-label">Deposits collected</span>
        <span class="stat-value">$<%= (platformMarket.deposits_paid_cents/100).toFixed(2) %></span>
        <span class="stat-sub muted"><%= platformMarket.count_paid %> bookings</span>
      </div>
      <div class="stat">
        <span class="stat-label">Platform fee earned</span>
        <span class="stat-value">$<%= (platformMarket.platform_fee_cents/100).toFixed(2) %></span>
        <span class="stat-sub muted">NPH revenue</span>
      </div>
      <div class="stat">
        <span class="stat-label">Transferred to studios</span>
        <span class="stat-value">$<%= (platformMarket.transferred_cents/100).toFixed(2) %></span>
        <span class="stat-sub muted">via Stripe Connect</span>
      </div>
      <div class="stat">
        <span class="stat-label">Held — pending Connect</span>
        <span class="stat-value">$<%= (platformMarket.payouts_pending_cents/100).toFixed(2) %></span>
        <span class="stat-sub muted">studios not yet onboarded</span>
      </div>
      <div class="stat">
        <span class="stat-label">Active studios</span>
        <span class="stat-value"><%= platformMarket.active_installers %></span>
        <span class="stat-sub muted">with at least one paid deposit</span>
      </div>
      <% if (platformMarket.count_failed > 0) { %>
        <div class="stat stat-warn">
          <span class="stat-label">Failed payments</span>
          <span class="stat-value"><%= platformMarket.count_failed %></span>
          <span class="stat-sub muted">card declined / abandoned</span>
        </div>
      <% } %>
    </div>
  <% } %>
</section>
<%- include('../partials/footer') %>