[object Object]

← back to NationalPaperHangers

Bump platform fee 10%→25% + reframe as 'lead-coordination fee' in copy

bec9ac1d1d11606dc29068340f41de397228371c · 2026-05-06 15:51:49 -0700 · SteveStudio2

Steve's call 2026-05-06. NPH_PLATFORM_FEE_BPS default 1000→2500; enterprise
discount tier 700→1500. The label change from 'platform fee' / 'marketplace
fee' to 'lead-coordination fee' is intentional positioning — a 25% take rate
described as a marketplace cut invites contractor-licensing scrutiny in CA
($7026), whereas a referral/lead-coordination fee is the standard home-services
lead-gen model (HomeAdvisor, Angi, Yelp).

Touched: lib/stripe.js DEFAULT_PLATFORM_FEE_BPS, views/admin/dashboard.ejs
(2 callouts), views/admin/billing.ejs, views/public/installer.ejs reserve banner.
NPH_PLATFORM_FEE_BPS=2500 set on Kamatera prod .env. 26/26 tests pass.

Files touched

Diff

commit bec9ac1d1d11606dc29068340f41de397228371c
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 6 15:51:49 2026 -0700

    Bump platform fee 10%→25% + reframe as 'lead-coordination fee' in copy
    
    Steve's call 2026-05-06. NPH_PLATFORM_FEE_BPS default 1000→2500; enterprise
    discount tier 700→1500. The label change from 'platform fee' / 'marketplace
    fee' to 'lead-coordination fee' is intentional positioning — a 25% take rate
    described as a marketplace cut invites contractor-licensing scrutiny in CA
    ($7026), whereas a referral/lead-coordination fee is the standard home-services
    lead-gen model (HomeAdvisor, Angi, Yelp).
    
    Touched: lib/stripe.js DEFAULT_PLATFORM_FEE_BPS, views/admin/dashboard.ejs
    (2 callouts), views/admin/billing.ejs, views/public/installer.ejs reserve banner.
    NPH_PLATFORM_FEE_BPS=2500 set on Kamatera prod .env. 26/26 tests pass.
---
 lib/stripe.js              | 5 ++++-
 views/admin/billing.ejs    | 4 ++--
 views/admin/dashboard.ejs  | 4 ++--
 views/public/installer.ejs | 4 ++--
 4 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/lib/stripe.js b/lib/stripe.js
index 3a7bb9b..f5657eb 100644
--- a/lib/stripe.js
+++ b/lib/stripe.js
@@ -120,7 +120,10 @@ function constructWebhookEvent(rawBody, signature) {
 // flagged for manual transfer at claim-time via metadata.
 
 const DEFAULT_DEPOSIT_CENTS = parseInt(process.env.NPH_DEFAULT_DEPOSIT_CENTS || '9900', 10);
-const DEFAULT_PLATFORM_FEE_BPS = parseInt(process.env.NPH_PLATFORM_FEE_BPS || '1000', 10);
+// 2500 bps = 25%. Set 2026-05-06 per Steve. Was 1000 (10%); the 25% rate
+// positions NPH as a referral/lead service rather than a marketplace cut —
+// see the lead-coordination legal framing in the v0.7 conversation.
+const DEFAULT_PLATFORM_FEE_BPS = parseInt(process.env.NPH_PLATFORM_FEE_BPS || '2500', 10);
 
 function platformFeeCents(amountCents, feeBps) {
   return Math.floor((amountCents * feeBps) / 10000);
diff --git a/views/admin/billing.ejs b/views/admin/billing.ejs
index 64459d1..3ac8480 100644
--- a/views/admin/billing.ejs
+++ b/views/admin/billing.ejs
@@ -40,10 +40,10 @@
   <%
     var _payoutsActive = !!installer.stripe_account_id && !!installer.stripe_account_charges_enabled;
     var _depositLabel = '$' + ((defaultDepositCents || 9900) / 100).toFixed(0);
-    var _feePct = ((defaultPlatformFeeBps || 1000) / 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> marketplace fee; the balance routes to your bank via Stripe Connect.
+    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) { %>
diff --git a/views/admin/dashboard.ejs b/views/admin/dashboard.ejs
index c0a2556..f692042 100644
--- a/views/admin/dashboard.ejs
+++ b/views/admin/dashboard.ejs
@@ -12,7 +12,7 @@
   %>
 
   <% if (typeof connectFlash !== 'undefined' && connectFlash === 'ok') { %>
-    <div class="callout callout-ok"><strong>Payouts ready.</strong> Stripe Connect onboarding complete — booking deposits will route to your bank account, net of NPH's <%= ((installer.tier === 'enterprise') ? '7' : '10') %>% platform fee.</div>
+    <div class="callout callout-ok"><strong>Payouts ready.</strong> Stripe Connect onboarding complete — booking deposits will route to your bank account, net of NPH's <%= ((installer.tier === 'enterprise') ? '15' : '25') %>% lead-coordination fee.</div>
   <% } else if (typeof connectFlash !== 'undefined' && connectFlash === 'mock') { %>
     <div class="callout">Stripe is in test mode — Connect onboarding ran in mock. Save real <code>STRIPE_SECRET_KEY</code> via the secrets manager to enable live payouts.</div>
   <% } %>
@@ -75,7 +75,7 @@
       </ol>
       <% if (!_payoutsActive) { %>
         <p class="muted" style="margin: 12px 0 0; font-size: 0.85rem;">
-          Customers pay their booking deposit through NPH. Once your Stripe payouts are set up, your share lands in your bank in 1–2 business days, net of NPH's <%= Math.round((installer.tier === 'enterprise' ? 700 : 1000) / 100) %>% marketplace fee.
+          Customers pay their booking deposit through NPH. Once your Stripe payouts are set up, your share lands in your bank in 1–2 business days, net of NPH's <%= Math.round((installer.tier === 'enterprise' ? 1500 : 2500) / 100) %>% lead-coordination fee.
         </p>
       <% } %>
     </div>
diff --git a/views/public/installer.ejs b/views/public/installer.ejs
index 88f38dc..a0e3354 100644
--- a/views/public/installer.ejs
+++ b/views/public/installer.ejs
@@ -15,7 +15,7 @@
     var _selfBookable = _calendarTier && installer.subscription_status === 'active';
     var _depositCents = parseInt(process.env.NPH_DEFAULT_DEPOSIT_CENTS || '9900', 10);
     var _depositLabel = '$' + (_depositCents / 100).toFixed(0);
-    var _feeBps = parseInt(process.env.NPH_PLATFORM_FEE_BPS || '1000', 10);
+    var _feeBps = parseInt(process.env.NPH_PLATFORM_FEE_BPS || '2500', 10);
     var _feePct = Math.round(_feeBps / 100);
   %>
   <% if (_selfBookable) { %>
@@ -23,7 +23,7 @@
       <div class="reserve-banner-inner">
         <div class="reserve-copy">
           <p class="reserve-kicker">Reserve a visit · <%= _depositLabel %> deposit</p>
-          <p class="reserve-body">Pay through NPH — your share goes straight to <%= installer.business_name %>. NPH retains a <%= _feePct %>% marketplace fee.</p>
+          <p class="reserve-body">Pay through NPH — your share goes straight to <%= installer.business_name %>. NPH retains a <%= _feePct %>% lead-coordination fee.</p>
         </div>
         <a href="/installer/<%= installer.slug %>/book" class="btn btn-primary btn-lg reserve-cta">Book a consultation →</a>
       </div>

← f21468c v0.7 #31: auto-email worker for installer_interest queue  ·  back to NationalPaperHangers  ·  Reposition NPH as referral/lead-coordination service (CSLB § 31cdca8 →