2025-12-03






2025-12-03

TLDR; Šiandienos tema: Pay per read + Gemini Writing Engine (Available in Chrome Web Store)

Sveika, elektroerdve,

Pagaliau mano kurtasis Chrome plėtinys prieinamas visiems norintiems jį įsidiegti savo naršyklėse !

Gemini Writing Engine nuoroda: https://chromewebstore.google.com/detail/eecpliadmoekeegbbhaelnlebnbpojmf?utm_source=item-share-cb

Visa reikiama dokumentacija, aprašai bei programinis kodas Github paskyroje: https://github.com/Sparky4567/gemini_extension

Bet dabar trumpai apie Pay per read monetizaciją.

Pay per read (ir http status 402)

Tad kas gi tas vadinamasis “Pay per read”?

Tai yra būdas apmokestinti savo tinklaraščio įrašus fiksuota suma, įkainojant įrašus minimaliu vienkartiniu mokesčiu.

Trumpai tariant, “moku už tai, ką skaitau”.

Bet kaip tai integruoti į savo tinklaraštį? Koks metodas patogiausias? Paywall as a service?

Paywall as a service skamba patraukliai, tačiau jei būtų įmanoma sukurti tokią sistemą naudojant Cloudflare skriptus?

Tai įmanoma, kadangi AI įrankių pagalba prototipą galima sukurti per keletą minučių.

Pavyzdinis kodas

// ===============================
// CLOUDLARE WORKER: PAYWALL SYSTEM
// Hybrid model: blur preview + scroll lock + 3 free reads per month
// ===============================

export default {
  async fetch(request, env) {
    const url = new URL(request.url)
    const path = url.pathname

    // Protect only blog content
    if (!path.startsWith('/blog/') || !path.startsWith('/archive/')) {
      return fetch(request)
    }

    // 1. Get / create anonymous user ID
    let uid = getCookie(request, 'uid')
    if (!uid) {
      uid = crypto.randomUUID()
      return setCookie(
        new Response('', { status: 302, headers: { Location: url.pathname } }),
        'uid',
        uid
      )
    }

    const articleId = path.replace('/blog/', '')

    // 2. Load user state
    const userKey = `user:${uid}`
    const userData = await env.USERS.get(userKey, { type: 'json' }) || {
      freeReads: 0,
      month: getMonthKey()
    }

    // Reset monthly reads
    if (userData.month !== getMonthKey()) {
      userData.freeReads = 0
      userData.month = getMonthKey()
    }

    // 3. Paid unlock?
    const hasPaid = await env.ENTITLEMENTS.get(`unlock:${uid}:${articleId}`)
    if (hasPaid) {
      return fetch(request)
    }

    // 4. Free reads available?
    if (userData.freeReads < 3) {
      userData.freeReads++
      await env.USERS.put(userKey, JSON.stringify(userData))
      return fetch(request)
    }

    // 5. Out of free reads → return hybrid paywall wrapper
    const article = await fetch(request).then(r => r.text())
    return new Response(paywallWrapper(article, articleId), {
      status: 402,
      headers: { 'Content-Type': 'text/html' }
    })
  }
}


// =======================================
// HELPERS
// =======================================
function getMonthKey() {
  const now = new Date()
  return `${now.getFullYear()}-${now.getMonth()}`
}

function getCookie(request, name) {
  const cookie = request.headers.get('Cookie') || ''
  const match = cookie.match(new RegExp(name + '=([^;]+)'))
  return match ? match[1] : null
}

function setCookie(response, name, value) {
  response.headers.append(
    'Set-Cookie',
    `${name}=${value}; Path=/; Max-Age=31536000; SameSite=Lax`
  )
  return response
}


// =======================================
// HYBRID PAYWALL WRAPPER
// (blur preview + scroll lock)
// =======================================

function paywallWrapper(articleHtml, articleId) {
  return `
  <html>
  <head>
    <title>Payment Required</title>
    <style>
      body {
        margin: 0;
        font-family: sans-serif;
      }

      /* Blur effect for preview */
      .content-blurred {
        filter: blur(6px);
        opacity: 0.6;
        transition: 0.3s;
        pointer-events: none;
      }

      /* Locked zone overlay */
      .lock-overlay {
        position: fixed;
        left: 0;
        bottom: 0;
        width: 100%;
        padding: 24px;
        background: rgba(0, 0, 0, 0.85);
        color: #fff;
        text-align: center;
        backdrop-filter: blur(10px);
        z-index: 99999;
        border-top: 1px solid #444;
      }

      .paywall-button {
        padding: 12px 22px;
        background: #4CAF50;
        color: #fff;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        margin-top: 12px;
      }
    </style>
  </head>
  <body>

    <!-- Article content wrapped and blurred -->
    <div id="content" class="content-blurred">
      ${articleHtml}
    </div>

    <script>
      let locked = false;

      // Scroll-lock threshold (pixels)
      const LIMIT = 1200;

      window.addEventListener('scroll', () => {
        if (locked) return;

        if (window.scrollY > LIMIT) {
          locked = true;
          document.body.style.overflow = 'hidden';

          const overlay = document.createElement('div');
          overlay.className = 'lock-overlay';
          overlay.innerHTML = \`
            <div>You’ve reached your monthly limit.</div>
            <div>This article is partially blurred. Unlock to continue reading.</div>
            <button class="paywall-button" onclick="purchase()">Unlock for €0.10</button>
          \`;
          document.body.appendChild(overlay);
        }
      });

      async function purchase() {
        const res = await fetch('/_pay/${articleId}', { method: 'POST' });
        if (res.ok) location.reload();
      }
    </script>

  </body>
  </html>
  `;
}


// =======================================
// PAYMENT ENDPOINT (Worker-side)
// Add this inside "fetch" near the top
// =======================================
//
// if (url.pathname.startsWith('/_pay/')) {
//   const articleId = url.pathname.replace('/_pay/', '')
//   await env.ENTITLEMENTS.put(`unlock:${uid}:${articleId}`, '1', { expirationTtl: 31536000 })
//   return new Response('ok')
// }
//

P.s. Prieš naudojant “Pay per read” būtina atsižvelgti į svetainės lankomumą bei skaitytojų kiekio ir peržiūros rodiklius, kadangi įtraukus tokį straipsnių apmokestinimo būdą dalis skaitovų gali būti prarasta.

Iki sekančio susiskaitymo.

Šis ekranas trumpam išsijungia, bet kažkur įsijungia kitas.

#Žymos
#post #artefaktas_eu #personal #opinion #cloudflare #skriptas #paywall #pay_per_read #integracija

Asmeninė nuomonė.

Artefaktas.eu is a personal digital garden exploring technology, creativity, and the craft of building things online. Posts range from reflections on blogging tools and web frameworks to thoughts on AI, productivity, and digital minimalism — always with a mix of humor, curiosity, and hands-on experimentation.

Author: Artefaktas About author: I’m a creator-blogger driven by curiosity, blending writing, art, music, code, and the elegance of math and physics into everything I do.

Linktree link: https://linktr.ee/artefaktas

Tinklaraštį galima prenumeruoti net keletu skirtingų būdų:

- Naudojant RSS sklaidos kanalo nuorodą: https://artefaktas.eu/rss.xml

- Naudojantis follow.it forma

Pirkimas - teisė pernaudoti straipsnio turinį savoms reikmėms

Kaina: 3 EUR

Loading More Trees widget…

🔞 Age Verification

Please enter your birth year to continue: