← 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 & 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') %>