added on local at 2026-07-01 13:47:22
| 1 | #!/usr/bin/perl | |
| 2 | #====================================================================== | |
| 3 | # AffSoft -- public landing page + 8 marketing/legal pages. | |
| 4 | # | |
| 5 | # Consolidates about.cgi, contact.cgi, faq.cgi, features.cgi, help.cgi, | |
| 6 | # privacy.cgi, terms.cgi, why.cgi into this file via SCRIPT_NAME | |
| 7 | # dispatch. Each of the 8 files is now a 3-line wrapper that `do`s | |
| 8 | # this file. Larger marketing pages (tour, tutorials, pricing) stay | |
| 9 | # standalone by design. | |
| 10 | # | |
| 11 | # Default entry (index.cgi): | |
| 12 | # Signed in -> 302 to /dashboard.cgi | |
| 13 | # Signed out -> render the affiliate-program marketing landing page. | |
| 14 | #====================================================================== | |
| 15 | use strict; | |
| 16 | use warnings; | |
| 17 | ||
| 18 | use lib '/var/www/vhosts/3dshawn.com/affiliate.3dshawn.com'; | |
| 19 | use CGI; | |
| 20 | use CGI::Carp qw(fatalsToBrowser); | |
| 21 | use MODS::Login; | |
| 22 | use MODS::AffSoft::Wrapper; | |
| 23 | ||
| 24 | my $auth = MODS::Login->new; | |
| 25 | my $wrap = MODS::AffSoft::Wrapper->new; | |
| 26 | my $userinfo = $auth->login_verify; | |
| 27 | ||
| 28 | my $entry = ($ENV{SCRIPT_NAME} || '') =~ m{/([^/]+)\.cgi$} ? $1 : 'index'; | |
| 29 | ||
| 30 | if ($entry eq 'about') { _mkt_about(); } | |
| 31 | elsif ($entry eq 'contact') { _mkt_contact(); } | |
| 32 | elsif ($entry eq 'faq') { _mkt_faq(); } | |
| 33 | elsif ($entry eq 'features') { _mkt_features(); } | |
| 34 | elsif ($entry eq 'help') { _mkt_help(); } | |
| 35 | elsif ($entry eq 'privacy') { _mkt_privacy(); } | |
| 36 | elsif ($entry eq 'terms') { _mkt_terms(); } | |
| 37 | elsif ($entry eq 'why') { _mkt_why(); } | |
| 38 | else { _mkt_landing(); } | |
| 39 | exit; | |
| 40 | ||
| 41 | #====================================================================== | |
| 42 | # / (index.cgi) : landing page + logged-in redirect | |
| 43 | #====================================================================== | |
| 44 | sub _mkt_landing { | |
| 45 | if ($userinfo && $userinfo->{user_id}) { | |
| 46 | print "Status: 302 Found\r\nLocation: /dashboard.cgi\r\n\r\n"; | |
| 47 | return; | |
| 48 | } | |
| 49 | ||
| 50 | print "Content-Type: text/html; charset=utf-8\r\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\r\nPragma: no-cache\r\nExpires: 0\r\n\r\n"; | |
| 51 | ||
| 52 | my $body = <<'HTML'; | |
| 53 | <!-- ── Hero ───────────────────────────────────────────────────── --> | |
| 54 | <section class="hero"> | |
| 55 | <div class="hero-grid-bg"></div> | |
| 56 | <div class="hero-glow-1"></div> | |
| 57 | <div class="hero-glow-2"></div> | |
| 58 | <div class="hero-glow-3"></div> | |
| 59 | ||
| 60 | <div class="hero-inner"> | |
| 61 | <div> | |
| 62 | <span class="hero-pill"> | |
| 63 | <span class="dot"></span> Trackable links · Attribution · Payouts · Branded portals | |
| 64 | </span> | |
| 65 | <h1 class="hero-title" style="font-size:clamp(28px,4.2vw,52px);line-height:1.12;letter-spacing:-0.02em;max-width:780px"> | |
| 66 | No more tracking affiliates in spreadsheets and chasing down payouts. <span class="grad">AffSoft mints the links, attributes the sales, and runs the payouts — automatically.</span> | |
| 67 | </h1> | |
| 68 | <p class="hero-sub" style="max-width:680px"> | |
| 69 | Trackable referral links, real-time conversion attribution with a real cookie window, custom commission tiers, | |
| 70 | automated monthly payouts, and branded affiliate portals. Built for SaaS, ecom, agencies, and creators. | |
| 71 | </p> | |
| 72 | <div class="hero-cta"> | |
| 73 | <a href="/signup.cgi" class="btn btn-primary btn-lg">Start free trial</a> | |
| 74 | <a href="/tour.cgi" class="btn btn-secondary btn-lg">See the platform</a> | |
| 75 | </div> | |
| 76 | <div class="hero-trust"> | |
| 77 | <div class="hero-trust-item"> | |
| 78 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg> | |
| 79 | No credit card required | |
| 80 | </div> | |
| 81 | <div class="hero-trust-item"> | |
| 82 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg> | |
| 83 | Server-to-server webhook · HMAC ready | |
| 84 | </div> | |
| 85 | <div class="hero-trust-item"> | |
| 86 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg> | |
| 87 | Every click + conversion is a row | |
| 88 | </div> | |
| 89 | </div> | |
| 90 | </div> | |
| 91 | ||
| 92 | <!-- Stacked floating dashboard cards --> | |
| 93 | <div class="hero-visual"> | |
| 94 | <div class="float-card fc-1"> | |
| 95 | <div class="flex justify-between items-center mb-1"> | |
| 96 | <span class="text-xs text-dim" style="letter-spacing:1.5px;text-transform:uppercase">Commissions · MTD</span> | |
| 97 | <span class="pill success" style="padding:2px 8px;font-size:10px">+24.6%</span> | |
| 98 | </div> | |
| 99 | <div style="font-family:var(--font-display);font-size:28px;font-weight:700;color:#fff">$4,287.40</div> | |
| 100 | <div class="text-xs text-dim mt-1">Across 38 affiliates · 142 conversions</div> | |
| 101 | </div> | |
| 102 | ||
| 103 | <div class="float-card fc-2"> | |
| 104 | <div class="text-xs text-dim mb-2" style="letter-spacing:1.5px;text-transform:uppercase">Top affiliates</div> | |
| 105 | <div class="flex items-center gap-3 mb-1"> | |
| 106 | <span class="dot-status green"></span> | |
| 107 | <span class="text-sm fw-600">@anna.creates</span> | |
| 108 | <span class="text-xs text-dim" style="margin-left:auto">$842.10</span> | |
| 109 | </div> | |
| 110 | <div class="flex items-center gap-3 mb-1"> | |
| 111 | <span class="dot-status green"></span> | |
| 112 | <span class="text-sm fw-600">@growth-loop</span> | |
| 113 | <span class="text-xs text-dim" style="margin-left:auto">$618.00</span> | |
| 114 | </div> | |
| 115 | <div class="flex items-center gap-3 mb-1"> | |
| 116 | <span class="dot-status yellow"></span> | |
| 117 | <span class="text-sm fw-600">@reviewbox</span> | |
| 118 | <span class="text-xs text-dim" style="margin-left:auto">$494.20</span> | |
| 119 | </div> | |
| 120 | <div class="flex items-center gap-3"> | |
| 121 | <span class="dot-status green"></span> | |
| 122 | <span class="text-sm fw-600">@indiestack</span> | |
| 123 | <span class="text-xs text-dim" style="margin-left:auto">$370.50</span> | |
| 124 | </div> | |
| 125 | </div> | |
| 126 | ||
| 127 | <div class="float-card fc-3"> | |
| 128 | <div class="flex items-center justify-between mb-2"> | |
| 129 | <span class="text-xs text-dim" style="letter-spacing:1.5px;text-transform:uppercase">Approval queue</span> | |
| 130 | <span class="pill brand" style="padding:2px 8px;font-size:10px">7 PENDING</span> | |
| 131 | </div> | |
| 132 | <div class="flex justify-between mb-1"> | |
| 133 | <span class="text-sm">Conv #2148 · @anna.creates</span> | |
| 134 | <span class="text-sm fw-600 tabular">$24.90</span> | |
| 135 | </div> | |
| 136 | <div class="progress mb-2"><div class="progress-fill" style="width:62%"></div></div> | |
| 137 | <div class="flex justify-between mb-1"> | |
| 138 | <span class="text-sm">Conv #2149 · @growth-loop</span> | |
| 139 | <span class="text-sm fw-600 tabular text-success">$48.00</span> | |
| 140 | </div> | |
| 141 | <div class="progress"><div class="progress-fill" style="width:94%"></div></div> | |
| 142 | <div class="text-xs text-dim mt-1">One-click approve · refund-protected commissions</div> | |
| 143 | </div> | |
| 144 | </div> | |
| 145 | </div> | |
| 146 | </section> | |
| 147 | ||
| 148 | ||
| 149 | <!-- ── What you save vs the rest ────────────────────────────── --> | |
| 150 | <section class="section" style="padding:48px 24px 24px"> | |
| 151 | <div style="max-width:920px;margin:0 auto"> | |
| 152 | <div style="text-align:center;font-size:11px;text-transform:uppercase;letter-spacing:2px;color:#94a3b8;font-weight:700;margin-bottom:8px">Why merchants switch</div> | |
| 153 | <h2 style="text-align:center;font-size:clamp(22px,2.8vw,30px);margin:0 0 28px;font-weight:700">Same features. Lower bill. No payout cut.</h2> | |
| 154 | <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px"> | |
| 155 | <div style="background:rgba(148,163,184,0.06);border:1px solid rgba(148,163,184,0.15);border-radius:12px;padding:22px"> | |
| 156 | <div style="font-size:10px;text-transform:uppercase;letter-spacing:1.5px;color:#94a3b8;font-weight:700;margin-bottom:14px">Most affiliate platforms</div> | |
| 157 | <ul style="list-style:none;padding:0;margin:0;font-size:13px;color:#cbd5e1;line-height:1.7"> | |
| 158 | <li style="display:flex;gap:8px;margin-bottom:6px"><span style="color:#94a3b8;flex-shrink:0">×</span><span>$89–$300/mo, per-seat scaling</span></li> | |
| 159 | <li style="display:flex;gap:8px;margin-bottom:6px"><span style="color:#94a3b8;flex-shrink:0">×</span><span>2–5% cut on every payout</span></li> | |
| 160 | <li style="display:flex;gap:8px;margin-bottom:6px"><span style="color:#94a3b8;flex-shrink:0">×</span><span>Affiliate portals locked to enterprise</span></li> | |
| 161 | <li style="display:flex;gap:8px;margin-bottom:6px"><span style="color:#94a3b8;flex-shrink:0">×</span><span>Merchant has to manually approve every signup</span></li> | |
| 162 | <li style="display:flex;gap:8px;margin-bottom:0"><span style="color:#94a3b8;flex-shrink:0">×</span><span>Annual contracts</span></li> | |
| 163 | </ul> | |
| 164 | </div> | |
| 165 | <div style="background:linear-gradient(135deg,rgba(168,85,247,0.10) 0%,rgba(124,58,237,0.10) 100%);border:1px solid rgba(168,85,247,0.4);border-radius:12px;padding:22px"> | |
| 166 | <div style="font-size:10px;text-transform:uppercase;letter-spacing:1.5px;color:#a855f7;font-weight:700;margin-bottom:14px">AffSoft</div> | |
| 167 | <ul style="list-style:none;padding:0;margin:0;font-size:13px;color:#e6ecf6;line-height:1.7"> | |
| 168 | <li style="display:flex;gap:8px;margin-bottom:6px"><span style="color:#22c55e;flex-shrink:0">✓</span><span>Free forever, paid from <span class="dlr">$</span>9/mo</span></li> | |
| 169 | <li style="display:flex;gap:8px;margin-bottom:6px"><span style="color:#22c55e;flex-shrink:0">✓</span><span>0% cut on payouts — ever</span></li> | |
| 170 | <li style="display:flex;gap:8px;margin-bottom:6px"><span style="color:#22c55e;flex-shrink:0">✓</span><span>Branded affiliate portals included</span></li> | |
| 171 | <li style="display:flex;gap:8px;margin-bottom:6px"><span style="color:#22c55e;flex-shrink:0">✓</span><span>Public marketplace to get found by affiliates</span></li> | |
| 172 | <li style="display:flex;gap:8px;margin-bottom:0"><span style="color:#22c55e;flex-shrink:0">✓</span><span>Monthly billing · cancel any time</span></li> | |
| 173 | </ul> | |
| 174 | </div> | |
| 175 | </div> | |
| 176 | </div> | |
| 177 | </section> | |
| 178 | ||
| 179 | <!-- ── Stack strip ───────────────────────────────────────────── --> | |
| 180 | <section class="section" style="padding-top:0"> | |
| 181 | <div class="card" style="padding:28px 32px;display:flex;align-items:center;justify-content:space-between;gap:32px;flex-wrap:wrap"> | |
| 182 | <div class="text-xs text-dim" style="letter-spacing:2px;text-transform:uppercase;font-weight:700">Drop-in for</div> | |
| 183 | <div class="flex items-center gap-6" style="flex-wrap:wrap;font-family:var(--font-display);color:var(--col-text-2);font-size:18px;letter-spacing:1px"> | |
| 184 | <span>Stripe Checkout</span><span class="text-dim">·</span> | |
| 185 | <span>WooCommerce</span><span class="text-dim">·</span> | |
| 186 | <span>Shopify</span><span class="text-dim">·</span> | |
| 187 | <span>Gumroad</span><span class="text-dim">·</span> | |
| 188 | <span>Lemon Squeezy</span><span class="text-dim">·</span> | |
| 189 | <span>Paddle</span><span class="text-dim">·</span> | |
| 190 | <span>Custom backends</span> | |
| 191 | </div> | |
| 192 | </div> | |
| 193 | </section> | |
| 194 | ||
| 195 | ||
| 196 | <!-- ── How it works (3 steps) ───────────────────────────────── --> | |
| 197 | <section class="section" id="how"> | |
| 198 | <span class="section-eyebrow">How it works</span> | |
| 199 | <h2 class="section-title">Three steps. <span class="grad">No tag manager required.</span></h2> | |
| 200 | <p class="section-sub"> | |
| 201 | AffSoft is small on purpose. You launch a program, hand affiliates a link, | |
| 202 | and fire one webhook from your checkout. The rest is bookkeeping — and | |
| 203 | we do the bookkeeping. | |
| 204 | </p> | |
| 205 | ||
| 206 | <div class="feature-grid"> | |
| 207 | <div class="feature"> | |
| 208 | <div class="feature-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><text x="12" y="16" text-anchor="middle" font-size="11" font-weight="700" stroke="none" fill="currentColor">1</text></svg></div> | |
| 209 | <div class="feature-title">Create a program</div> | |
| 210 | <p class="feature-desc">Name it, pick a commission model (percent of order, flat per sale, or CPA), and set the attribution window. Public discover-page listing is optional.</p> | |
| 211 | </div> | |
| 212 | <div class="feature"> | |
| 213 | <div class="feature-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><text x="12" y="16" text-anchor="middle" font-size="11" font-weight="700" stroke="none" fill="currentColor">2</text></svg></div> | |
| 214 | <div class="feature-title">Affiliates grab links</div> | |
| 215 | <p class="feature-desc">12-character tokens with optional <code>?s=sub_id</code> for campaign tagging. Affiliates self-serve from their own dashboard — you don’t mint links by hand.</p> | |
| 216 | </div> | |
| 217 | <div class="feature"> | |
| 218 | <div class="feature-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><text x="12" y="16" text-anchor="middle" font-size="11" font-weight="700" stroke="none" fill="currentColor">3</text></svg></div> | |
| 219 | <div class="feature-title">POST a conversion</div> | |
| 220 | <p class="feature-desc">Server-to-server: <code>POST /conv.cgi</code> from your checkout success handler. We match against the visitor’s most-recent in-window click and write a pending row.</p> | |
| 221 | </div> | |
| 222 | </div> | |
| 223 | </section> | |
| 224 | ||
| 225 | ||
| 226 | <!-- ── Attribution module deep-dive ─────────────────────────── --> | |
| 227 | <section class="section" id="attribution"> | |
| 228 | <div class="dash-grid" style="align-items:center"> | |
| 229 | <div> | |
| 230 | <span class="section-eyebrow">Attribution Module</span> | |
| 231 | <h2 class="section-title" style="font-size:38px">Real attribution. <span class="grad">No mystery math.</span></h2> | |
| 232 | <p class="section-sub" style="margin-bottom:24px"> | |
| 233 | Every click writes a row. Every conversion runs the same matching query | |
| 234 | you could write yourself: most-recent click in this program for this | |
| 235 | visitor inside the window. Click trail, attribution decision, and | |
| 236 | commission calculation are all auditable in SQL. | |
| 237 | </p> | |
| 238 | <ul class="price-list" style="margin:0 0 28px"> | |
| 239 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Per-program cookie window (1–365 days)</li> | |
| 240 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Last-click match against most-recent in-window click</li> | |
| 241 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Commission stored in cents — zero rounding drift</li> | |
| 242 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Sub-id passthrough for campaign attribution</li> | |
| 243 | </ul> | |
| 244 | <a href="/signup.cgi" class="btn btn-primary">Start free trial →</a> | |
| 245 | </div> | |
| 246 | ||
| 247 | <div class="card card-elevated" style="padding:0;overflow:hidden"> | |
| 248 | <div style="padding:18px 22px;border-bottom:1px solid var(--col-border);display:flex;align-items:center;justify-content:space-between"> | |
| 249 | <div class="flex items-center gap-3"> | |
| 250 | <div class="module-icon" style="width:30px;height:30px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 3v18h18"/><path d="m7 14 4-4 4 4 5-5"/></svg></div> | |
| 251 | <span class="module-title">Click trail · visitor 9F2A…</span> | |
| 252 | </div> | |
| 253 | <span class="pill brand">Matched</span> | |
| 254 | </div> | |
| 255 | <div class="table-wrap" style="border:none;border-radius:0"> | |
| 256 | <table class="table"> | |
| 257 | <thead><tr><th>When</th><th>Link</th><th>Affiliate</th><th class="num">Status</th></tr></thead> | |
| 258 | <tbody> | |
| 259 | <tr><td class="text-dim">12:04</td><td><strong>r/8kQp…</strong></td><td>@anna.creates</td><td><span class="pill success"><span class="dot-status green"></span>Counted</span></td></tr> | |
| 260 | <tr><td class="text-dim">11:48</td><td><strong>r/8kQp…</strong></td><td>@anna.creates</td><td><span class="pill info">Click</span></td></tr> | |
| 261 | <tr><td class="text-dim">11:12</td><td><strong>m/X2Vy…</strong></td><td>@growth-loop</td><td><span class="pill info">Click (older)</span></td></tr> | |
| 262 | <tr><td class="text-dim">10:36</td><td><strong>r/8kQp…</strong></td><td>@anna.creates</td><td><span class="pill info">Click</span></td></tr> | |
| 263 | <tr><td class="text-dim">—</td><td colspan="2"><em>Conversion @ 12:14 · matched most-recent click in window</em></td><td class="num text-success">+$24.90</td></tr> | |
| 264 | </tbody> | |
| 265 | </table> | |
| 266 | </div> | |
| 267 | </div> | |
| 268 | </div> | |
| 269 | </section> | |
| 270 | ||
| 271 | ||
| 272 | <!-- ── Payouts strip ─────────────────────────────────────────── --> | |
| 273 | <section class="section" id="payouts"> | |
| 274 | <div class="dash-grid" style="grid-template-columns:1fr 1fr;align-items:center"> | |
| 275 | <div class="card card-elevated" style="padding:0;overflow:hidden"> | |
| 276 | <div style="height:280px;background:linear-gradient(160deg, #160e28 0%, #2b1853 100%);position:relative;overflow:hidden"> | |
| 277 | <div style="position:absolute;inset:0;background-image:radial-gradient(rgba(255,255,255,0.06) 1px, transparent 1px);background-size:32px 32px"></div> | |
| 278 | <div style="position:absolute;top:24px;left:24px;color:#fff;font-family:var(--font-display);font-size:22px;font-weight:600">Payout #142</div> | |
| 279 | <div style="position:absolute;top:54px;left:24px;color:var(--col-text-3);font-size:12px">Manual mode · PayPal txn 8AB29</div> | |
| 280 | <div style="position:absolute;bottom:24px;left:24px;right:24px;display:grid;grid-template-columns:repeat(3,1fr);gap:12px"> | |
| 281 | <div style="padding:14px;background:rgba(124,58,237,0.20);border:1px solid rgba(124,58,237,0.45);border-radius:10px"> | |
| 282 | <div style="font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:var(--col-violet-bright);font-weight:700">Approved</div> | |
| 283 | <div style="font-family:var(--font-display);font-size:18px;font-weight:700;color:#fff">$842.10</div> | |
| 284 | </div> | |
| 285 | <div style="padding:14px;background:rgba(124,58,237,0.20);border:1px solid rgba(124,58,237,0.45);border-radius:10px"> | |
| 286 | <div style="font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:var(--col-violet-bright);font-weight:700">Refunded</div> | |
| 287 | <div style="font-family:var(--font-display);font-size:18px;font-weight:700;color:#fff">$0.00</div> | |
| 288 | </div> | |
| 289 | <div style="padding:14px;background:rgba(34,197,94,0.15);border:1px solid rgba(34,197,94,0.40);border-radius:10px"> | |
| 290 | <div style="font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:#86efac;font-weight:700">Sent</div> | |
| 291 | <div style="font-family:var(--font-display);font-size:18px;font-weight:700;color:#fff">$842.10</div> | |
| 292 | </div> | |
| 293 | </div> | |
| 294 | </div> | |
| 295 | <div style="padding:18px 22px;display:flex;justify-content:space-between;align-items:center"> | |
| 296 | <div> | |
| 297 | <div class="text-xs text-dim" style="letter-spacing:1.5px;text-transform:uppercase">Period</div> | |
| 298 | <div style="font-family:var(--font-display);font-size:16px;font-weight:700;color:#fff">Jun 01 – Jun 30</div> | |
| 299 | </div> | |
| 300 | <div> | |
| 301 | <div class="text-xs text-dim" style="letter-spacing:1.5px;text-transform:uppercase">Conversions</div> | |
| 302 | <div style="font-family:var(--font-display);font-size:16px;font-weight:700;color:#fff">37</div> | |
| 303 | </div> | |
| 304 | <div> | |
| 305 | <div class="text-xs text-dim" style="letter-spacing:1.5px;text-transform:uppercase">Status</div> | |
| 306 | <div style="font-family:var(--font-display);font-size:16px;font-weight:700;color:var(--col-success)">Sent</div> | |
| 307 | </div> | |
| 308 | </div> | |
| 309 | </div> | |
| 310 | ||
| 311 | <div> | |
| 312 | <span class="section-eyebrow">Payouts Module</span> | |
| 313 | <h2 class="section-title" style="font-size:38px">Approve. Pay. <span class="grad">Paper trail.</span></h2> | |
| 314 | <p class="section-sub" style="margin-bottom:24px"> | |
| 315 | Conversions land as <em>pending</em>. You approve or reject. When you | |
| 316 | cut a payout, AffSoft sums the affiliate’s approved-but-unpaid | |
| 317 | commissions, writes the payout row, and links every conversion to it | |
| 318 | through a <code>payout_lines</code> table — so refunds and audits | |
| 319 | stay sane. | |
| 320 | </p> | |
| 321 | <ul class="price-list" style="margin:0 0 28px"> | |
| 322 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> One-click approve / reject / refund per conversion</li> | |
| 323 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Manual-mode payouts today (PayPal txn / wire ref / Stripe id)</li> | |
| 324 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Stripe Connect automated transfers (rolling out)</li> | |
| 325 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Affiliate-side earnings dashboard included</li> | |
| 326 | </ul> | |
| 327 | <a href="/signup.cgi" class="btn btn-primary">Launch a program →</a> | |
| 328 | </div> | |
| 329 | </div> | |
| 330 | </section> | |
| 331 | ||
| 332 | ||
| 333 | <!-- ── Discover / affiliate-side strip ───────────────────────── --> | |
| 334 | <section class="section" id="discover"> | |
| 335 | <span class="section-eyebrow">For affiliates</span> | |
| 336 | <h2 class="section-title">Both sides of the deal. <span class="grad">One account.</span></h2> | |
| 337 | <p class="section-sub"> | |
| 338 | Every AffSoft account is both a merchant and an affiliate. Run your own | |
| 339 | program AND promote someone else’s from the same login. Discover | |
| 340 | public programs, mint your own tracking links, and watch earnings land | |
| 341 | in real time. | |
| 342 | </p> | |
| 343 | ||
| 344 | <div class="dash-grid-3"> | |
| 345 | <div class="card card-elevated"> | |
| 346 | <div class="card-head"><div class="card-title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg> Discover programs</div></div> | |
| 347 | <p class="text-secondary text-sm">Browse every public program on the platform. Filter by commission rate, attribution window, or category. Join with one click; auto-approve or merchant-review per program.</p> | |
| 348 | </div> | |
| 349 | ||
| 350 | <div class="card card-elevated"> | |
| 351 | <div class="card-head"><div class="card-title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg> Mint your links</div></div> | |
| 352 | <p class="text-secondary text-sm">Self-service from your affiliate dashboard. Add as many sub-id tags as you want for campaign-level attribution — we keep them all in the click row.</p> | |
| 353 | </div> | |
| 354 | ||
| 355 | <div class="card card-elevated"> | |
| 356 | <div class="card-head"><div class="card-title"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg> Watch earnings</div></div> | |
| 357 | <p class="text-secondary text-sm">Pending / approved / paid broken out per program. Payout history with every line item the merchant reconciled. No black boxes between you and the cheque.</p> | |
| 358 | </div> | |
| 359 | </div> | |
| 360 | </section> | |
| 361 | ||
| 362 | ||
| 363 | <!-- ── Pricing teaser ─────────────────────────────────────────── --> | |
| 364 | <section class="section" id="pricing"> | |
| 365 | <span class="section-eyebrow">Pricing</span> | |
| 366 | <h2 class="section-title">Honest pricing. <span class="grad">No per-conversion tax.</span></h2> | |
| 367 | <p class="section-sub">Pick a plan based on program count and affiliate roster size. No nickel-and-diming on tracked conversions, no take rate on commissions paid.</p> | |
| 368 | ||
| 369 | <div class="pricing-grid"> | |
| 370 | <div class="price-card"> | |
| 371 | <div class="price-tier">Free</div> | |
| 372 | <div class="price-amt">$0<span class="per"> /mo</span></div> | |
| 373 | <div class="price-desc">Kick the tires · one program, 5 affiliates</div> | |
| 374 | <ul class="price-list"> | |
| 375 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> 1 program</li> | |
| 376 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Up to 5 affiliates</li> | |
| 377 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Server-to-server webhook</li> | |
| 378 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Manual payouts</li> | |
| 379 | </ul> | |
| 380 | <a href="/signup.cgi" class="btn btn-secondary btn-block" style="margin-top:auto">Start free</a> | |
| 381 | </div> | |
| 382 | ||
| 383 | <div class="price-card"> | |
| 384 | <div class="price-tier">Starter</div> | |
| 385 | <div class="price-amt">$9<span class="per"> /mo</span></div> | |
| 386 | <div class="price-desc">Small programs that need real attribution</div> | |
| 387 | <ul class="price-list"> | |
| 388 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> 3 programs</li> | |
| 389 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> 50 affiliates</li> | |
| 390 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Sub-id passthrough</li> | |
| 391 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> CSV exports</li> | |
| 392 | </ul> | |
| 393 | <a href="/signup.cgi" class="btn btn-secondary btn-block" style="margin-top:auto">Start trial</a> | |
| 394 | </div> | |
| 395 | ||
| 396 | <div class="price-card featured"> | |
| 397 | <div class="price-tier">Pro</div> | |
| 398 | <div class="price-amt">$29<span class="per"> /mo</span></div> | |
| 399 | <div class="price-desc">Most folks land here</div> | |
| 400 | <ul class="price-list"> | |
| 401 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> 10 programs</li> | |
| 402 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> 500 affiliates</li> | |
| 403 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> HMAC-signed webhooks</li> | |
| 404 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Stripe Connect payouts</li> | |
| 405 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Priority email support</li> | |
| 406 | </ul> | |
| 407 | <a href="/signup.cgi" class="btn btn-primary btn-block" style="margin-top:auto">Start 14-day trial</a> | |
| 408 | </div> | |
| 409 | ||
| 410 | <div class="price-card"> | |
| 411 | <div class="price-tier">Studio</div> | |
| 412 | <div class="price-amt">$99<span class="per"> /mo</span></div> | |
| 413 | <div class="price-desc">Multi-brand operators</div> | |
| 414 | <ul class="price-list"> | |
| 415 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Unlimited programs</li> | |
| 416 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Unlimited affiliates</li> | |
| 417 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Team seats + 2FA</li> | |
| 418 | <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"/></svg> Per-program webhook secrets</li> | |
| 419 | </ul> | |
| 420 | <a href="/signup.cgi" class="btn btn-secondary btn-block" style="margin-top:auto">Start trial</a> | |
| 421 | </div> | |
| 422 | </div> | |
| 423 | ||
| 424 | <div style="text-align:center;margin-top:14px"> | |
| 425 | <a href="/pricing.cgi" class="text-sm" style="color:var(--col-violet-bright)">See full pricing →</a> | |
| 426 | </div> | |
| 427 | </section> | |
| 428 | ||
| 429 | ||
| 430 | <!-- ── Final CTA ──────────────────────────────────────────────── --> | |
| 431 | <section class="section"> | |
| 432 | <div class="card-glow card" style="text-align:center;padding:64px 32px"> | |
| 433 | <h2 class="section-title" style="margin:0 auto 16px">Ready to run referrals <span class="grad">like a real channel?</span></h2> | |
| 434 | <p class="section-sub" style="margin: 0 auto 32px">Start free trial. No credit card. First link minted in 60 seconds.</p> | |
| 435 | <div class="flex justify-center gap-3"> | |
| 436 | <a href="/signup.cgi" class="btn btn-primary btn-lg">Start free trial</a> | |
| 437 | <a href="/tour.cgi" class="btn btn-secondary btn-lg">Tour the product</a> | |
| 438 | </div> | |
| 439 | </div> | |
| 440 | </section> | |
| 441 | ||
| 442 | ||
| 443 | <!-- ── Footer ─────────────────────────────────────────────────── --> | |
| 444 | <footer class="site-foot"> | |
| 445 | <div class="foot-grid"> | |
| 446 | <div class="foot-col"> | |
| 447 | <div class="flex items-center gap-3 mb-2"> | |
| 448 | <div class="brand-mark" style="width:32px;height:32px;font-size:15px">A<span style="color:#a78bfa">S</span></div> | |
| 449 | <div class="brand-text"><span class="brand-name">AffSoft</span></div> | |
| 450 | </div> | |
| 451 | <p class="text-sm text-secondary" style="max-width:340px">The hosted affiliate platform that doesn’t make you build attribution from scratch.</p> | |
| 452 | </div> | |
| 453 | <div class="foot-col"> | |
| 454 | <h5>Platform</h5> | |
| 455 | <ul> | |
| 456 | <li><a href="/features.cgi">Features</a></li> | |
| 457 | <li><a href="/tour.cgi">Tour</a></li> | |
| 458 | <li><a href="/why.cgi">Why AffSoft</a></li> | |
| 459 | <li><a href="/pricing.cgi">Pricing</a></li> | |
| 460 | <li><a href="/faq.cgi">FAQ</a></li> | |
| 461 | </ul> | |
| 462 | </div> | |
| 463 | <div class="foot-col"> | |
| 464 | <h5>Company</h5> | |
| 465 | <ul> | |
| 466 | <li><a href="/about.cgi">About</a></li> | |
| 467 | <li><a href="/contact.cgi">Contact</a></li> | |
| 468 | </ul> | |
| 469 | </div> | |
| 470 | <div class="foot-col"> | |
| 471 | <h5>Legal & Trust</h5> | |
| 472 | <ul> | |
| 473 | <li><a href="/privacy.cgi">Privacy</a></li> | |
| 474 | <li><a href="/terms.cgi">Terms of Service</a></li> | |
| 475 | </ul> | |
| 476 | </div> | |
| 477 | </div> | |
| 478 | <div class="foot-bottom"> | |
| 479 | <span>© 2026 AffSoft · All rights reserved.</span> | |
| 480 | <span>Made for operators who treat referrals like a channel.</span> | |
| 481 | </div> | |
| 482 | </footer> | |
| 483 | HTML | |
| 484 | ||
| 485 | $wrap->render({ | |
| 486 | userinfo => undef, | |
| 487 | title => 'Affiliate program software for growth teams', | |
| 488 | body => $body, | |
| 489 | }); | |
| 490 | } | |
| 491 | ||
| 492 | #====================================================================== | |
| 493 | # /about.cgi | |
| 494 | #====================================================================== | |
| 495 | sub _mkt_about { | |
| 496 | print "Content-Type: text/html; charset=utf-8\r\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\r\nPragma: no-cache\r\nExpires: 0\r\n\r\n"; | |
| 497 | ||
| 498 | my $body = <<'HTML'; | |
| 499 | <div class="page-pad" style="padding:64px 32px"> | |
| 500 | <div style="color:#a78bfa;font-size:12px;text-transform:uppercase;letter-spacing:.1em;font-weight:600;margin-bottom:14px">About AffSoft</div> | |
| 501 | <h1 style="margin:0 0 18px;font-size:42px;letter-spacing:-.02em;line-height:1.1">Affiliate software that doesn't make you build attribution from scratch.</h1> | |
| 502 | <p style="color:#aaa;font-size:18px;line-height:1.6;margin:0 0 32px;max-width:600px">AffSoft is a hosted affiliate program platform. Spin up a referral program in minutes, hand out tracking links, and watch conversions land. We handle attribution windows, click + conversion bookkeeping, and merchant-side approval; you keep the product and the customer relationship.</p> | |
| 503 | ||
| 504 | <h2 style="margin:40px 0 12px;font-size:22px;letter-spacing:-.01em">Who it's for</h2> | |
| 505 | <p style="color:#bbb;line-height:1.6;margin:0 0 20px">SaaS founders, indie e-commerce stores, and growth teams who want a referral channel without standing up their own pixel, cookie store, and payout pipeline. If you'd rather spend your sprint shipping product than tracking sub_id parameters, AffSoft is for you.</p> | |
| 506 | ||
| 507 | <h2 style="margin:40px 0 12px;font-size:22px;letter-spacing:-.01em">Who it's not for</h2> | |
| 508 | <p style="color:#bbb;line-height:1.6;margin:0 0 20px">Enterprises that already have a real attribution stack. AffSoft is opinionated and lean by design — one tracking-link format, one webhook contract, one cookie name. If your compliance team needs custom data residency or your team needs SSO and SCIM today, you'll outgrow us.</p> | |
| 509 | ||
| 510 | <h2 style="margin:40px 0 12px;font-size:22px;letter-spacing:-.01em">Where we are</h2> | |
| 511 | <p style="color:#bbb;line-height:1.6;margin:0 0 28px">Early. The platform is live, attribution works, payouts go out manually while we wire the automated path. If you want a low-traffic pilot or you're rebuilding from a heavier tool, get in touch.</p> | |
| 512 | ||
| 513 | <div style="display:flex;gap:12px;margin-top:32px"> | |
| 514 | <a href="/signup.cgi" class="btn btn-primary">Start free trial</a> | |
| 515 | <a href="/pricing.cgi" class="btn" style="background:rgba(255,255,255,.08);color:#fff;border:1px solid rgba(255,255,255,.12)">See pricing</a> | |
| 516 | </div> | |
| 517 | </div> | |
| 518 | HTML | |
| 519 | ||
| 520 | $wrap->render({ | |
| 521 | userinfo => $userinfo, | |
| 522 | title => 'About', | |
| 523 | body => $body, | |
| 524 | }); | |
| 525 | } | |
| 526 | ||
| 527 | #====================================================================== | |
| 528 | # /contact.cgi -- template-driven | |
| 529 | #====================================================================== | |
| 530 | sub _mkt_contact { | |
| 531 | require MODS::Template; | |
| 532 | my $tfile = MODS::Template->new; | |
| 533 | $| = 1; | |
| 534 | print "Content-Type: text/html; charset=utf-8\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\nExpires: 0\n\n"; | |
| 535 | my $body = join('', $tfile->template('webstls_contact.html', {}, $userinfo)); | |
| 536 | $wrap->render({ userinfo => $userinfo, title => 'Contact', body => $body }); | |
| 537 | } | |
| 538 | ||
| 539 | #====================================================================== | |
| 540 | # /faq.cgi | |
| 541 | #====================================================================== | |
| 542 | sub _mkt_faq { | |
| 543 | print "Content-Type: text/html; charset=utf-8\r\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\r\nPragma: no-cache\r\nExpires: 0\r\n\r\n"; | |
| 544 | ||
| 545 | my @QAS = ( | |
| 546 | { | |
| 547 | q => 'Do I have to embed any JavaScript on my site?', | |
| 548 | a => 'No. Tracking links are HTTP redirects that drop an attribution cookie on the visitor. Conversions are server-to-server webhooks fired from your checkout. Nothing runs in your customers\' browsers from us.', | |
| 549 | }, | |
| 550 | { | |
| 551 | q => 'What\'s the attribution model?', | |
| 552 | a => 'Last-click within a configurable cookie window (1–365 days, per program). When a conversion comes in, we match the visitor_token to the most recent click on that program within the window and attribute it to that affiliate.', | |
| 553 | }, | |
| 554 | { | |
| 555 | q => 'How do you handle refunds?', | |
| 556 | a => 'Conversions land as pending. You approve them after your refund window closes. Until you approve, no commission becomes payable. Refunded conversions can be flipped to refunded status (separate from rejected) so audit trails stay clean.', | |
| 557 | }, | |
| 558 | { | |
| 559 | q => 'How are payouts made today?', | |
| 560 | a => 'Manual marking in v1 — you cut the check (or PayPal, or wire) and click "mark sent" so the affiliate dashboard reflects it. Stripe Connect transfers are scheduled for v1.1.', | |
| 561 | }, | |
| 562 | { | |
| 563 | q => 'Can one person be both a merchant and an affiliate?', | |
| 564 | a => 'Yes. Roles are implied by data ownership, not by an account flag. You can run programs you own and join programs other merchants run, all from one login. The dashboard shows both sides at once.', | |
| 565 | }, | |
| 566 | { | |
| 567 | q => 'Is the webhook authenticated?', | |
| 568 | a => 'v1: no. Anyone with your program_id and a valid visitor_token can POST a conversion. Pending status forces your manual approval before money moves, which limits the blast radius. v1.1 adds per-program HMAC signing of the conversion payload.', | |
| 569 | }, | |
| 570 | { | |
| 571 | q => 'What\'s the data retention policy?', | |
| 572 | a => 'Clicks and conversions are retained indefinitely for audit. Personal data (IPs, user agents) on click rows can be redacted on request — we keep aggregate counts and the visitor_token so attribution still works.', | |
| 573 | }, | |
| 574 | { | |
| 575 | q => 'Can I export everything?', | |
| 576 | a => 'Yes. Every dashboard view has a CSV export (coming in the next release). The underlying SQL schema is documented and you have read-only access to it on Studio plan.', | |
| 577 | }, | |
| 578 | { | |
| 579 | q => 'Do you charge a take-rate on conversions?', | |
| 580 | a => 'No. The platform fee is the monthly subscription. You keep 100% of what your affiliates earn and pay 100% to them. We don\'t skim conversions.', | |
| 581 | }, | |
| 582 | { | |
| 583 | q => 'What if AffSoft goes down?', | |
| 584 | a => 'Tracking links 502 instead of redirecting until we\'re back up — visitors will hit a brief error. Your conversion webhook is queue-and-retry-friendly on your side; we recommend retrying on 5xx. Status page is at status.affiliate.3dshawn.com (planned).', | |
| 585 | }, | |
| 586 | ); | |
| 587 | ||
| 588 | my $faq_html = ''; | |
| 589 | foreach my $qa (@QAS) { | |
| 590 | my $q = $qa->{q}; $q =~ s/'/'/g; | |
| 591 | my $a = $qa->{a}; | |
| 592 | $faq_html .= qq~ | |
| 593 | <details style="border:1px solid rgba(255,255,255,.08);border-radius:12px;background:rgba(20,20,30,.4);overflow:hidden"> | |
| 594 | <summary style="padding:18px 22px;cursor:pointer;font-weight:600;color:#fff;list-style:none;display:flex;justify-content:space-between;align-items:center;gap:12px"> | |
| 595 | <span>$q</span> | |
| 596 | <span style="color:#666;font-size:18px;flex-shrink:0">+</span> | |
| 597 | </summary> | |
| 598 | <div style="padding:0 22px 20px;color:#bbb;line-height:1.6;font-size:14px">$a</div> | |
| 599 | </details> | |
| 600 | ~; | |
| 601 | } | |
| 602 | ||
| 603 | my $body = <<HTML; | |
| 604 | <div class="page-pad" style="padding:64px 32px"> | |
| 605 | <div style="color:#a78bfa;font-size:12px;text-transform:uppercase;letter-spacing:.1em;font-weight:600;margin-bottom:14px">FAQ</div> | |
| 606 | <h1 style="margin:0 0 36px;font-size:42px;letter-spacing:-.02em;line-height:1.1">Questions, answered.</h1> | |
| 607 | ||
| 608 | <div style="display:flex;flex-direction:column;gap:10px"> | |
| 609 | $faq_html | |
| 610 | </div> | |
| 611 | ||
| 612 | <div style="margin-top:48px;padding:24px;border:1px dashed rgba(255,255,255,.12);border-radius:12px;color:#aaa"> | |
| 613 | Still have a question? Email <a href="mailto:programmershawn\@gmail.com" style="color:#a78bfa">programmershawn\@gmail.com</a>. | |
| 614 | </div> | |
| 615 | </div> | |
| 616 | HTML | |
| 617 | ||
| 618 | $wrap->render({ | |
| 619 | userinfo => $userinfo, | |
| 620 | title => 'FAQ', | |
| 621 | body => $body, | |
| 622 | }); | |
| 623 | } | |
| 624 | ||
| 625 | #====================================================================== | |
| 626 | # /features.cgi | |
| 627 | #====================================================================== | |
| 628 | sub _mkt_features { | |
| 629 | print "Content-Type: text/html; charset=utf-8\r\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\r\nPragma: no-cache\r\nExpires: 0\r\n\r\n"; | |
| 630 | ||
| 631 | my $body = <<'HTML'; | |
| 632 | <div class="page-pad" style="padding:64px 32px"> | |
| 633 | <div style="color:#a78bfa;font-size:12px;text-transform:uppercase;letter-spacing:.1em;font-weight:600;margin-bottom:14px;text-align:center">Features</div> | |
| 634 | <h1 style="margin:0 0 14px;font-size:42px;letter-spacing:-.02em;line-height:1.1;text-align:center">Everything you need to run a referral program. Nothing you don't.</h1> | |
| 635 | <p style="color:#aaa;font-size:18px;line-height:1.6;margin:0 auto 56px;max-width:660px;text-align:center">AffSoft is intentionally narrow. Every feature is something you'd have to build anyway. We built it once so you don't have to.</p> | |
| 636 | ||
| 637 | <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px"> | |
| 638 | ||
| 639 | <div style="padding:28px;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:rgba(20,20,30,.4)"> | |
| 640 | <div style="font-size:24px;margin-bottom:14px">🔗</div> | |
| 641 | <h3 style="margin:0 0 8px;font-size:18px">Per-affiliate tracking links</h3> | |
| 642 | <p style="color:#aaa;line-height:1.55;margin:0;font-size:14px">12-character tokens, optional <code style="color:#a78bfa">?s=sub_id</code> tags for campaign attribution. Mint as many as you want per program.</p> | |
| 643 | </div> | |
| 644 | ||
| 645 | <div style="padding:28px;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:rgba(20,20,30,.4)"> | |
| 646 | <div style="font-size:24px;margin-bottom:14px">📊</div> | |
| 647 | <h3 style="margin:0 0 8px;font-size:18px">Click + conversion attribution</h3> | |
| 648 | <p style="color:#aaa;line-height:1.55;margin:0;font-size:14px">Configurable cookie window per program (1–365 days). Last-click attribution against the most recent matching click within window.</p> | |
| 649 | </div> | |
| 650 | ||
| 651 | <div style="padding:28px;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:rgba(20,20,30,.4)"> | |
| 652 | <div style="font-size:24px;margin-bottom:14px">💰</div> | |
| 653 | <h3 style="margin:0 0 8px;font-size:18px">Three commission models</h3> | |
| 654 | <p style="color:#aaa;line-height:1.55;margin:0;font-size:14px">Percent of order, flat per sale, or CPA (cost per acquisition). Commission is computed at conversion time and stored in cents to avoid rounding drift.</p> | |
| 655 | </div> | |
| 656 | ||
| 657 | <div style="padding:28px;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:rgba(20,20,30,.4)"> | |
| 658 | <div style="font-size:24px;margin-bottom:14px">🍻</div> | |
| 659 | <h3 style="margin:0 0 8px;font-size:18px">Server-to-server webhook</h3> | |
| 660 | <p style="color:#aaa;line-height:1.55;margin:0;font-size:14px">POST <code style="color:#a78bfa">/conv.cgi</code> from your checkout. No JS pixels, no third-party scripts on your customers' browsers.</p> | |
| 661 | </div> | |
| 662 | ||
| 663 | <div style="padding:28px;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:rgba(20,20,30,.4)"> | |
| 664 | <div style="font-size:24px;margin-bottom:14px">🎬</div> | |
| 665 | <h3 style="margin:0 0 8px;font-size:18px">Self-service affiliate signup</h3> | |
| 666 | <p style="color:#aaa;line-height:1.55;margin:0;font-size:14px">List your program publicly and let affiliates join themselves. Auto-approve or require manual review — per program.</p> | |
| 667 | </div> | |
| 668 | ||
| 669 | <div style="padding:28px;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:rgba(20,20,30,.4)"> | |
| 670 | <div style="font-size:24px;margin-bottom:14px">🔒</div> | |
| 671 | <h3 style="margin:0 0 8px;font-size:18px">Approval-gated payouts</h3> | |
| 672 | <p style="color:#aaa;line-height:1.55;margin:0;font-size:14px">Conversions land as <em>pending</em>. You approve before they become payable — refund-protect your commission math.</p> | |
| 673 | </div> | |
| 674 | ||
| 675 | <div style="padding:28px;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:rgba(20,20,30,.4)"> | |
| 676 | <div style="font-size:24px;margin-bottom:14px">🛡</div> | |
| 677 | <h3 style="margin:0 0 8px;font-size:18px">Per-side dashboards</h3> | |
| 678 | <p style="color:#aaa;line-height:1.55;margin:0;font-size:14px">One account is both a merchant and an affiliate. Run your own program AND promote someone else's.</p> | |
| 679 | </div> | |
| 680 | ||
| 681 | <div style="padding:28px;border:1px solid rgba(255,255,255,.08);border-radius:14px;background:rgba(20,20,30,.4)"> | |
| 682 | <div style="font-size:24px;margin-bottom:14px">🔎</div> | |
| 683 | <h3 style="margin:0 0 8px;font-size:18px">Auditable</h3> | |
| 684 | <p style="color:#aaa;line-height:1.55;margin:0;font-size:14px">Every click, conversion, approval, and payout is a row. You can ask any question your auditor will and answer it in SQL.</p> | |
| 685 | </div> | |
| 686 | ||
| 687 | <div style="padding:28px;border:1px solid rgba(91,33,182,.35);border-radius:14px;background:linear-gradient(135deg, rgba(91,33,182,.10), rgba(46,16,101,.04))"> | |
| 688 | <div style="font-size:24px;margin-bottom:14px">🛡️</div> | |
| 689 | <h3 style="margin:0 0 8px;font-size:18px">Built-in fraud protection</h3> | |
| 690 | <p style="color:#aaa;line-height:1.55;margin:0;font-size:14px">Every conversion is scanned the moment it lands. We watch for cookie stuffing, self-referral, velocity abuse, duplicate orders, and a host of other patterns. Suspicious activity is held in a review queue with a plain-English explanation of <em>why</em> — you decide, automated payouts never touch unreviewed flags. Our detection model evolves with every confirmed-fraud decision.</p> | |
| 691 | </div> | |
| 692 | ||
| 693 | </div> | |
| 694 | ||
| 695 | <div style="text-align:center;margin-top:56px"> | |
| 696 | <a href="/signup.cgi" class="btn btn-primary btn-lg">Start free trial</a> | |
| 697 | <div style="color:#666;font-size:13px;margin-top:10px">No credit card required · 14-day full access</div> | |
| 698 | </div> | |
| 699 | </div> | |
| 700 | HTML | |
| 701 | ||
| 702 | $wrap->render({ | |
| 703 | userinfo => $userinfo, | |
| 704 | title => 'Features', | |
| 705 | body => $body, | |
| 706 | }); | |
| 707 | } | |
| 708 | ||
| 709 | #====================================================================== | |
| 710 | # /help.cgi -- login-gated. Renders webstls_help.html template. | |
| 711 | #====================================================================== | |
| 712 | sub _mkt_help { | |
| 713 | require MODS::Template; | |
| 714 | my $tfile = MODS::Template->new; | |
| 715 | $| = 1; | |
| 716 | ||
| 717 | # help.cgi is one of the few marketing-family pages that requires | |
| 718 | # login (it's a "where do I do X" router into the app itself). | |
| 719 | if (!$userinfo) { | |
| 720 | print "Status: 302 Found\nLocation: /login.cgi\n\n"; | |
| 721 | return; | |
| 722 | } | |
| 723 | ||
| 724 | print "Content-Type: text/html; charset=utf-8\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\nExpires: 0\n\n"; | |
| 725 | ||
| 726 | my $body = join('', $tfile->template('webstls_help.html', {}, $userinfo)); | |
| 727 | ||
| 728 | $wrap->render({ | |
| 729 | userinfo => $userinfo, | |
| 730 | page_key => '', | |
| 731 | title => 'Help', | |
| 732 | body => $body, | |
| 733 | }); | |
| 734 | } | |
| 735 | ||
| 736 | #====================================================================== | |
| 737 | # /privacy.cgi -- template-driven | |
| 738 | #====================================================================== | |
| 739 | sub _mkt_privacy { | |
| 740 | require MODS::Template; | |
| 741 | my $tfile = MODS::Template->new; | |
| 742 | $| = 1; | |
| 743 | print "Content-Type: text/html; charset=utf-8\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\nExpires: 0\n\n"; | |
| 744 | my $body = join('', $tfile->template('webstls_privacy.html', {}, $userinfo)); | |
| 745 | $wrap->render({ userinfo => $userinfo, title => 'Privacy Policy', body => $body }); | |
| 746 | } | |
| 747 | ||
| 748 | #====================================================================== | |
| 749 | # /terms.cgi -- template-driven | |
| 750 | #====================================================================== | |
| 751 | sub _mkt_terms { | |
| 752 | require MODS::Template; | |
| 753 | my $tfile = MODS::Template->new; | |
| 754 | $| = 1; | |
| 755 | print "Content-Type: text/html; charset=utf-8\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\nPragma: no-cache\nExpires: 0\n\n"; | |
| 756 | my $body = join('', $tfile->template('webstls_terms.html', {}, $userinfo)); | |
| 757 | $wrap->render({ userinfo => $userinfo, title => 'Terms of Service', body => $body }); | |
| 758 | } | |
| 759 | ||
| 760 | #====================================================================== | |
| 761 | # /why.cgi | |
| 762 | #====================================================================== | |
| 763 | sub _mkt_why { | |
| 764 | print "Content-Type: text/html; charset=utf-8\r\nCache-Control: no-store, no-cache, must-revalidate, max-age=0\r\nPragma: no-cache\r\nExpires: 0\r\n\r\n"; | |
| 765 | ||
| 766 | my $body = <<'HTML'; | |
| 767 | <div class="page-pad" style="padding:64px 32px"> | |
| 768 | <div style="color:#a78bfa;font-size:12px;text-transform:uppercase;letter-spacing:.1em;font-weight:600;margin-bottom:14px">Why AffSoft</div> | |
| 769 | <h1 style="margin:0 0 18px;font-size:42px;letter-spacing:-.02em;line-height:1.1">"Why don't I just build this myself?"</h1> | |
| 770 | <p style="color:#aaa;font-size:18px;line-height:1.6;margin:0 0 48px">You can. Here's what you're signing up for.</p> | |
| 771 | ||
| 772 | <div style="display:grid;grid-template-columns:1fr;gap:28px"> | |
| 773 | ||
| 774 | <div style="padding:24px 28px;border-left:3px solid #a78bfa;background:rgba(102,99,255,.04);border-radius:0 10px 10px 0"> | |
| 775 | <h3 style="margin:0 0 6px;font-size:18px;color:#fff">A redirect endpoint that records a click</h3> | |
| 776 | <p style="color:#bbb;line-height:1.55;margin:0;font-size:14px">Sounds simple. Then you need rate limiting, bot filtering, IPv6 packing, X-Forwarded-For chain parsing, attribution-cookie scoping for subdomains, and a way to never double-count the same click from a retry. Two weeks if you're lucky.</p> | |
| 777 | </div> | |
| 778 | ||
| 779 | <div style="padding:24px 28px;border-left:3px solid #a78bfa;background:rgba(102,99,255,.04);border-radius:0 10px 10px 0"> | |
| 780 | <h3 style="margin:0 0 6px;font-size:18px;color:#fff">A conversion webhook with deduplication</h3> | |
| 781 | <p style="color:#bbb;line-height:1.55;margin:0;font-size:14px">Your checkout retries. Stripe's webhook retries. Affiliates submit refunds 30 days later. Your dedup key has to survive all three. Without dedup you pay out the same conversion twice.</p> | |
| 782 | </div> | |
| 783 | ||
| 784 | <div style="padding:24px 28px;border-left:3px solid #a78bfa;background:rgba(102,99,255,.04);border-radius:0 10px 10px 0"> | |
| 785 | <h3 style="margin:0 0 6px;font-size:18px;color:#fff">Last-click attribution within an N-day window</h3> | |
| 786 | <p style="color:#bbb;line-height:1.55;margin:0;font-size:14px">Two affiliates link the same visitor. Visitor buys. Who gets paid? The one with the most recent click within the program's window — and you'd better have an index on <code>(visitor_token, occurred_at)</code> or every conversion does a table scan.</p> | |
| 787 | </div> | |
| 788 | ||
| 789 | <div style="padding:24px 28px;border-left:3px solid #a78bfa;background:rgba(102,99,255,.04);border-radius:0 10px 10px 0"> | |
| 790 | <h3 style="margin:0 0 6px;font-size:18px;color:#fff">An affiliate dashboard with their own stats</h3> | |
| 791 | <p style="color:#bbb;line-height:1.55;margin:0;font-size:14px">Affiliates want to see clicks, conversions, earnings, payout status. You'll build it three times before they're satisfied. We've already built it twice.</p> | |
| 792 | </div> | |
| 793 | ||
| 794 | <div style="padding:24px 28px;border-left:3px solid #a78bfa;background:rgba(102,99,255,.04);border-radius:0 10px 10px 0"> | |
| 795 | <h3 style="margin:0 0 6px;font-size:18px;color:#fff">A payout pipeline that ties to Stripe Connect</h3> | |
| 796 | <p style="color:#bbb;line-height:1.55;margin:0;font-size:14px">Approving conversions, batching them, kicking off transfers, reconciling failed payouts, surfacing the failure to the affiliate so they update their account. Six weeks. Minimum.</p> | |
| 797 | </div> | |
| 798 | ||
| 799 | <div style="padding:24px 28px;border-left:3px solid #a78bfa;background:rgba(102,99,255,.04);border-radius:0 10px 10px 0"> | |
| 800 | <h3 style="margin:0 0 6px;font-size:18px;color:#fff">All the tax + compliance work</h3> | |
| 801 | <p style="color:#bbb;line-height:1.55;margin:0;font-size:14px">1099-K thresholds, W-9 collection, GDPR-compliant cookie disclosure, the affiliate terms-of-service that protects you when one of them spam-blasts your URL. None of this ships product.</p> | |
| 802 | </div> | |
| 803 | ||
| 804 | <div style="padding:24px 28px;border-left:3px solid #a78bfa;background:rgba(102,99,255,.04);border-radius:0 10px 10px 0"> | |
| 805 | <h3 style="margin:0 0 6px;font-size:18px;color:#fff">Fraud detection & reviewer tooling</h3> | |
| 806 | <p style="color:#bbb;line-height:1.55;margin:0;font-size:14px">Cookie stuffers, self-referrers, velocity bots, duplicate-order resubmissions. The patterns mutate constantly. Real-time scanning + a reviewer UI that explains <em>why</em> each flag fired is a six-month engineering effort on its own — assuming you have the data to learn from.</p> | |
| 807 | </div> | |
| 808 | ||
| 809 | </div> | |
| 810 | ||
| 811 | <div style="margin-top:48px;padding:32px;background:linear-gradient(135deg,rgba(102,99,255,.10),rgba(167,139,250,.06));border:1px solid rgba(167,139,250,.25);border-radius:14px;text-align:center"> | |
| 812 | <h2 style="margin:0 0 10px;font-size:24px;letter-spacing:-.01em">Or skip all that.</h2> | |
| 813 | <p style="color:#bbb;margin:0 0 22px;max-width:480px;margin-left:auto;margin-right:auto">Sign up, create a program, share your tracking link. We handle the rest.</p> | |
| 814 | <a href="/signup.cgi" class="btn btn-primary btn-lg">Start free trial</a> | |
| 815 | </div> | |
| 816 | </div> | |
| 817 | HTML | |
| 818 | ||
| 819 | $wrap->render({ | |
| 820 | userinfo => $userinfo, | |
| 821 | title => 'Why AffSoft', | |
| 822 | body => $body, | |
| 823 | }); | |
| 824 | } |