Ignore:
Timestamp:
07/21/2026 02:07:18 AM (7 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
9019a3e
Parents:
603d246
git-author:
Robin <roboburr@…> (07/21/2026 02:07:07 AM)
git-committer:
Robin <roboburr@…> (07/21/2026 02:07:18 AM)
Message:

Feature: a way to actually become a supporter, and a styled expired page

Bart's test surfaced two gaps in the visitor flow:

  • After "Allow" on Patreon, a non-supporter landed on "Nog geen supporter" with no way to actually pledge. The page now shows a primary "Word supporter op Patreon" button. The site owner sets their public Patreon page in Beheer -> Betaalde posts (new field); it's also linked from the gate itself ("Nog geen supporter? Word het op Patreon").
  • The invalid/expired callback replied with a raw res.send() plain-text line. It now renders the normal paid-result page (reason 'expired').

Changed files:
src/config/database.js

  • paid_patreon.patreon_url column (additive)

src/services/PaidPatreonService.js

  • patreonUrl in config/status/save (undefined keeps, empty clears) + patreonUrl(siteId) helper

src/routes/admin-paid.js

  • save patreon_url from the form

src/views/pages/admin-paid.ejs

  • "Openbare Patreon-pagina" field

src/routes/paid.js

  • expired callback renders paid-result; pass patronUrl to the result pages

src/routes/posts.js

  • pass pgPatronUrl to the gate

src/views/pages/paid-gate.ejs

  • "Word supporter" join line under the unlock button

src/views/pages/paid-result.ejs

  • "Word supporter op Patreon" primary button, 'expired' reason, ghost back button always has a label

test/paid-patron.test.js

  • patreonUrl set/keep/clear semantics

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/PaidPatreonService.js

    r603d246 rc3d12a6  
    2121    tokenExp: row.token_exp || 0,
    2222    defaultMinCents: row.default_min_cents || 0,
     23    patreonUrl: row.patreon_url || null,
    2324  };
    2425}
     
    3637    tokenExp: c.tokenExp || 0,
    3738    hasSecret: !!c.clientSecret,
     39    patreonUrl: c.patreonUrl || null,
    3840  };
     41}
     42
     43// The owner's public Patreon page, for the "Word supporter" link. Null when unset.
     44export function patreonUrl(siteId) {
     45  const c = getOwnerConfig(siteId);
     46  return c && c.patreonUrl ? c.patreonUrl : null;
    3947}
    4048
     
    5260    tokenExp: patch.tokenExp ?? cur.tokenExp ?? 0,
    5361    defaultMinCents: patch.defaultMinCents ?? cur.defaultMinCents ?? 0,
     62    // undefined = keep (e.g. token refresh doesn't touch it); null/'' = clear.
     63    patreonUrl: patch.patreonUrl !== undefined ? (patch.patreonUrl || null) : (cur.patreonUrl ?? null),
    5464  };
    5565  db.prepare(`INSERT INTO paid_patreon
    56       (site_id, client_id, client_secret_enc, campaign_id, access_token_enc, refresh_token_enc, token_exp, default_min_cents, updated_at)
    57       VALUES (?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP)
     66      (site_id, client_id, client_secret_enc, campaign_id, access_token_enc, refresh_token_enc, token_exp, default_min_cents, patreon_url, updated_at)
     67      VALUES (?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP)
    5868    ON CONFLICT(site_id) DO UPDATE SET
    5969      client_id=excluded.client_id, client_secret_enc=excluded.client_secret_enc,
    6070      campaign_id=excluded.campaign_id, access_token_enc=excluded.access_token_enc,
    6171      refresh_token_enc=excluded.refresh_token_enc, token_exp=excluded.token_exp,
    62       default_min_cents=excluded.default_min_cents, updated_at=CURRENT_TIMESTAMP`)
     72      default_min_cents=excluded.default_min_cents, patreon_url=excluded.patreon_url, updated_at=CURRENT_TIMESTAMP`)
    6373    .run(
    6474      siteId,
     
    7080      merged.tokenExp || 0,
    7181      Math.max(0, parseInt(merged.defaultMinCents, 10) || 0),
     82      merged.patreonUrl || null,
    7283    );
    7384}
     
    171182export default {
    172183  getOwnerConfig, ownerStatus, saveOwnerConfig, disconnect,
    173   defaultMinCents, needsRefresh, refreshCreatorToken, creatorAccessToken,
     184  defaultMinCents, patreonUrl, needsRefresh, refreshCreatorToken, creatorAccessToken,
    174185  pickCampaignMembership, verifyPatron,
    175186};
Note: See TracChangeset for help on using the changeset viewer.