thumbgate 1.16.20 → 1.16.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/api/server.js CHANGED
@@ -13,6 +13,9 @@ const {
13
13
  const POSTHOG_API_PATHS = new Set(['/capture', '/batch', '/decide', '/e', '/engage']);
14
14
  const POSTHOG_INGEST_HOST = 'us.i.posthog.com';
15
15
  const POSTHOG_STATIC_PATH_PREFIX = '/static/';
16
+ const FIRST_FAILURE_RULE_CHECKOUT_URL = 'https://buy.stripe.com/4gM6oHgH2bTw4lH6i73sI0z';
17
+ const QUICK_READ_CHECKOUT_URL = 'https://buy.stripe.com/aFa8wPgH29Lo4lH35V3sI0w';
18
+ const WORKFLOW_TEARDOWN_CHECKOUT_URL = 'https://buy.stripe.com/7sYfZhgH29LodWhdKz3sI0v';
16
19
 
17
20
  function getPosthogProxyPath(pathname) {
18
21
  return pathname.slice('/ingest'.length) || '/';
@@ -1462,6 +1465,9 @@ function buildCheckoutIntentHref(baseUrl, metadata = {}, overrides = {}) {
1462
1465
 
1463
1466
  function renderCheckoutIntentPage({
1464
1467
  confirmHref,
1468
+ firstRuleCheckoutHref,
1469
+ quickReadCheckoutHref,
1470
+ workflowTeardownCheckoutHref,
1465
1471
  workflowIntakeHref,
1466
1472
  teamOptionsHref,
1467
1473
  diagnosticCheckoutHref,
@@ -1470,6 +1476,15 @@ function renderCheckoutIntentPage({
1470
1476
  workflowSprintPriceDollars = 1500,
1471
1477
  }) {
1472
1478
  const safeConfirmHref = escapeHtmlAttribute(confirmHref);
1479
+ const safeFirstRuleCheckoutHref = firstRuleCheckoutHref
1480
+ ? escapeHtmlAttribute(firstRuleCheckoutHref)
1481
+ : '';
1482
+ const safeQuickReadCheckoutHref = quickReadCheckoutHref
1483
+ ? escapeHtmlAttribute(quickReadCheckoutHref)
1484
+ : '';
1485
+ const safeWorkflowTeardownCheckoutHref = workflowTeardownCheckoutHref
1486
+ ? escapeHtmlAttribute(workflowTeardownCheckoutHref)
1487
+ : '';
1473
1488
  const safeWorkflowIntakeHref = escapeHtmlAttribute(workflowIntakeHref);
1474
1489
  const safeTeamOptionsHref = escapeHtmlAttribute(teamOptionsHref);
1475
1490
  const safeDiagnosticCheckoutHref = diagnosticCheckoutHref
@@ -1484,7 +1499,16 @@ function renderCheckoutIntentPage({
1484
1499
  const sprintAction = safeSprintCheckoutHref
1485
1500
  ? `<a data-i="workflow_sprint_checkout" href="${safeSprintCheckoutHref}">Start $${workflowSprintPriceDollars} sprint</a>`
1486
1501
  : '';
1487
- return `<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>body{background:#0a0a0a;color:#eee;font-family:system-ui,sans-serif}div{max-width:560px;margin:12vh auto}a{display:block;margin:10px 0;padding:12px;border:1px solid #374151;color:inherit;text-align:center}.primary{background:#22d3ee;color:#000}</style><div><h1>Choose the right paid path.</h1><p>Pick Pro, diagnostic, sprint, or intake.</p><a class="primary" data-i="pro_checkout_confirmed" href="${safeConfirmHref}">Continue to Stripe</a>${diagnosticAction}${sprintAction}<a data-i="workflow_sprint_intake" href="${safeWorkflowIntakeHref}">Send workflow first</a><a data-i="team_paid_path" href="${safeTeamOptionsHref}">See diagnostic and sprint options</a><p>Stripe checkout.</p><a href="/">Back</a></div><script>addEventListener('click',e=>{let a=e.target.closest('[data-i]');if(a&&navigator.sendBeacon)navigator.sendBeacon('/v1/telemetry/ping',new Blob([JSON.stringify({eventType:'checkout_interstitial_cta_clicked',clientType:'web',page:'/checkout/pro',ctaId:a.dataset.i,ctaPlacement:'checkout_interstitial'})],{type:'application/json'}))})</script>`;
1502
+ const firstRuleAction = safeFirstRuleCheckoutHref
1503
+ ? `<a data-i="first_failure_rule_checkout" href="${safeFirstRuleCheckoutHref}">Pay $1 first rule</a>`
1504
+ : '';
1505
+ const quickReadAction = safeQuickReadCheckoutHref
1506
+ ? `<a data-i="quick_read_checkout" href="${safeQuickReadCheckoutHref}">Pay $19 quick read</a>`
1507
+ : '';
1508
+ const teardownAction = safeWorkflowTeardownCheckoutHref
1509
+ ? `<a data-i="workflow_teardown_checkout" href="${safeWorkflowTeardownCheckoutHref}">Pay $99 teardown</a>`
1510
+ : '';
1511
+ return `<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>body{background:#0a0a0a;color:#eee;font-family:system-ui,sans-serif}div{max-width:560px;margin:12vh auto}a{display:block;margin:10px 0;padding:12px;border:1px solid #374151;color:inherit;text-align:center}.primary{background:#22d3ee;color:#000}.high-ticket{border-color:#4ade80;color:#4ade80}</style><div><h1>Choose the right paid path.</h1><p>For one repeated workflow failure, start with the diagnostic or sprint. Use Pro only when you need the local self-serve dashboard.</p>${diagnosticAction.replace('<a ', '<a class="high-ticket" ')}${sprintAction.replace('<a ', '<a class="high-ticket" ')}<a class="primary" data-i="pro_checkout_confirmed" href="${safeConfirmHref}">Pay in Stripe</a>${teardownAction}${quickReadAction}${firstRuleAction}<a data-i="workflow_sprint_intake" href="${safeWorkflowIntakeHref}">Send workflow first</a><a data-i="team_paid_path" href="${safeTeamOptionsHref}">See options</a><p>Stripe checkout.</p><a href="/">Back</a></div><script>addEventListener('click',e=>{let a=e.target.closest('[data-i]');if(a&&navigator.sendBeacon)navigator.sendBeacon('/v1/telemetry/ping',new Blob([JSON.stringify({eventType:'checkout_interstitial_cta_clicked',clientType:'web',page:'/checkout/pro',ctaId:a.dataset.i,ctaPlacement:'checkout_interstitial'})],{type:'application/json'}))})</script>`;
1488
1512
  }
1489
1513
 
1490
1514
  function buildCheckoutBootstrapBody(parsed, req, journeyState = resolveJourneyState(req, parsed)) {
@@ -2963,6 +2987,9 @@ function renderCheckoutSuccessPage(runtimeConfig) {
2963
2987
  }
2964
2988
 
2965
2989
  function renderCheckoutCancelledPage(runtimeConfig) {
2990
+ const firstFailureRuleCheckoutUrl = escapeHtmlAttribute(FIRST_FAILURE_RULE_CHECKOUT_URL);
2991
+ const quickReadCheckoutUrl = escapeHtmlAttribute(QUICK_READ_CHECKOUT_URL);
2992
+ const workflowTeardownCheckoutUrl = escapeHtmlAttribute(WORKFLOW_TEARDOWN_CHECKOUT_URL);
2966
2993
  const diagnosticCheckoutUrl = runtimeConfig.sprintDiagnosticCheckoutUrl
2967
2994
  ? escapeHtmlAttribute(runtimeConfig.sprintDiagnosticCheckoutUrl)
2968
2995
  : '';
@@ -2973,18 +3000,21 @@ function renderCheckoutCancelledPage(runtimeConfig) {
2973
3000
  const workflowSprintPriceDollars = runtimeConfig.workflowSprintPriceDollars || 1500;
2974
3001
  const workflowSprintIntakeUrl = `${escapeHtmlAttribute(runtimeConfig.appOrigin)}/#workflow-sprint-intake`;
2975
3002
  const recoveryOfferLinks = [
2976
- `<a id="send-workflow-first" href="${workflowSprintIntakeUrl}" data-recovery-offer="workflow_sprint_intake" data-offer-price="0">Send workflow first</a>`,
2977
3003
  diagnosticCheckoutUrl
2978
3004
  ? `<a href="${diagnosticCheckoutUrl}" data-recovery-offer="sprint_diagnostic" data-offer-price="${sprintDiagnosticPriceDollars}">Book $${sprintDiagnosticPriceDollars} diagnostic</a>`
2979
3005
  : '',
2980
3006
  workflowSprintCheckoutUrl
2981
3007
  ? `<a href="${workflowSprintCheckoutUrl}" data-recovery-offer="workflow_sprint" data-offer-price="${workflowSprintPriceDollars}">Start $${workflowSprintPriceDollars} sprint</a>`
2982
3008
  : '',
3009
+ `<a href="${workflowTeardownCheckoutUrl}" data-recovery-offer="workflow_teardown" data-offer-price="99">Pay $99 teardown</a>`,
3010
+ `<a href="${quickReadCheckoutUrl}" data-recovery-offer="quick_read" data-offer-price="19">Pay $19 quick read</a>`,
3011
+ `<a href="${firstFailureRuleCheckoutUrl}" data-recovery-offer="first_failure_rule" data-offer-price="1">Pay $1 first rule</a>`,
3012
+ `<a id="send-workflow-first" href="${workflowSprintIntakeUrl}" data-recovery-offer="workflow_sprint_intake" data-offer-price="0">Send workflow first</a>`,
2983
3013
  ].filter(Boolean).join('\n ');
2984
3014
  const recoveryOfferCard = recoveryOfferLinks
2985
3015
  ? `<div class="card recovery-card">
2986
3016
  <h2>Need help deciding?</h2>
2987
- <p>If Pro is not the right next step, send the workflow first. We can qualify the blocker, confirm the proof plan, and route you to the diagnostic or sprint only when the scope is real.</p>
3017
+ <p>If Pro is not the right next step, start smaller or send the workflow first. We can qualify the blocker, confirm the proof plan, and route you to the diagnostic or sprint only when the scope is real.</p>
2988
3018
  <div class="actions">
2989
3019
  ${recoveryOfferLinks}
2990
3020
  </div>
@@ -3106,7 +3136,7 @@ function renderCheckoutCancelledPage(runtimeConfig) {
3106
3136
  </div>
3107
3137
  <div class="actions">
3108
3138
  <button type="button" id="submit-reason">Send feedback</button>
3109
- <a id="retry-checkout" href="/checkout/pro" class="secondary" data-recovery-offer="pro_trial_retry" data-offer-price="19">Restart $19 Pro trial</a>
3139
+ <a id="retry-checkout" href="/checkout/pro" class="secondary" data-recovery-offer="pro_pay_now_retry" data-offer-price="19">Restart $19 Pro checkout</a>
3110
3140
  <a href="${runtimeConfig.appOrigin}" class="secondary">Return to Context Gateway</a>
3111
3141
  </div>
3112
3142
  <p class="note" id="status">No feedback sent yet.</p>
@@ -4413,8 +4443,8 @@ async function addContext(){
4413
4443
  const isConfirmedCheckout = confirmParam === '1'
4414
4444
  || confirmParam === 'true'
4415
4445
  || req.method === 'POST';
4416
- if (!isConfirmedCheckout) {
4417
- const eventType = botClassification.isBot ? 'checkout_bot_deflected' : 'checkout_interstitial_view';
4446
+ if (!isConfirmedCheckout && botClassification.isBot) {
4447
+ const eventType = 'checkout_bot_deflected';
4418
4448
  appendBestEffortTelemetry(FEEDBACK_DIR, {
4419
4449
  eventType,
4420
4450
  clientType: 'web',
@@ -4449,6 +4479,27 @@ async function addContext(){
4449
4479
  ctaPlacement: 'checkout_interstitial',
4450
4480
  planId: 'team',
4451
4481
  });
4482
+ const firstRuleCheckoutHref = buildCheckoutIntentHref(FIRST_FAILURE_RULE_CHECKOUT_URL, analyticsMetadata, {
4483
+ utmMedium: 'checkout_interstitial_paid_path',
4484
+ utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_first_failure_rule',
4485
+ ctaId: 'checkout_interstitial_first_failure_rule_checkout',
4486
+ ctaPlacement: 'checkout_interstitial',
4487
+ planId: 'first_failure_rule',
4488
+ });
4489
+ const quickReadCheckoutHref = buildCheckoutIntentHref(QUICK_READ_CHECKOUT_URL, analyticsMetadata, {
4490
+ utmMedium: 'checkout_interstitial_paid_path',
4491
+ utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_quick_read',
4492
+ ctaId: 'checkout_interstitial_quick_read_checkout',
4493
+ ctaPlacement: 'checkout_interstitial',
4494
+ planId: 'quick_read',
4495
+ });
4496
+ const workflowTeardownCheckoutHref = buildCheckoutIntentHref(WORKFLOW_TEARDOWN_CHECKOUT_URL, analyticsMetadata, {
4497
+ utmMedium: 'checkout_interstitial_paid_path',
4498
+ utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_workflow_teardown',
4499
+ ctaId: 'checkout_interstitial_workflow_teardown_checkout',
4500
+ ctaPlacement: 'checkout_interstitial',
4501
+ planId: 'workflow_teardown',
4502
+ });
4452
4503
  const diagnosticCheckoutHref = hostedConfig.sprintDiagnosticCheckoutUrl
4453
4504
  ? buildCheckoutIntentHref(hostedConfig.sprintDiagnosticCheckoutUrl, analyticsMetadata, {
4454
4505
  utmMedium: 'checkout_interstitial_paid_path',
@@ -4469,6 +4520,9 @@ async function addContext(){
4469
4520
  : '';
4470
4521
  const html = renderCheckoutIntentPage({
4471
4522
  confirmHref: buildCheckoutConfirmHref(parsed),
4523
+ firstRuleCheckoutHref,
4524
+ quickReadCheckoutHref,
4525
+ workflowTeardownCheckoutHref,
4472
4526
  workflowIntakeHref,
4473
4527
  teamOptionsHref,
4474
4528
  diagnosticCheckoutHref,
@@ -4481,20 +4535,19 @@ async function addContext(){
4481
4535
  return;
4482
4536
  }
4483
4537
 
4484
- const normalizedCheckoutEmail = normalizeCheckoutCustomerEmail(bootstrapBody.customerEmail);
4538
+ const rawCheckoutEmail = normalizeNullableText(bootstrapBody.customerEmail);
4539
+ const normalizedCheckoutEmail = normalizeCheckoutCustomerEmail(rawCheckoutEmail);
4485
4540
  if (!normalizedCheckoutEmail) {
4486
4541
  appendBestEffortTelemetry(FEEDBACK_DIR, {
4487
- eventType: 'checkout_email_gate_shown',
4542
+ eventType: 'checkout_email_deferred_to_stripe',
4488
4543
  clientType: 'web',
4489
4544
  traceId,
4490
4545
  page: '/checkout/pro',
4491
4546
  planId: analyticsMetadata.planId,
4492
- }, req.headers, 'checkout_email_gate_shown');
4493
- const { html, headers } = renderCheckoutIntentGate(parsed, responseHeaders);
4494
- sendHtml(res, 200, html, headers);
4495
- return;
4547
+ reason: rawCheckoutEmail ? 'invalid_customer_email' : 'missing_customer_email',
4548
+ }, req.headers, 'checkout_email_deferred_to_stripe');
4496
4549
  }
4497
- bootstrapBody.customerEmail = normalizedCheckoutEmail;
4550
+ bootstrapBody.customerEmail = normalizedCheckoutEmail || undefined;
4498
4551
 
4499
4552
  appendBestEffortTelemetry(FEEDBACK_DIR, {
4500
4553
  eventType: 'checkout_bootstrap',