paywall-protect-widget 1.0.0

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/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # PaywallProtect Widget
2
+
3
+ Official JavaScript widget for PaywallProtect - bot detection and content protection.
4
+
5
+ ## Installation
6
+
7
+ ### Via CDN (Easiest - No Build Step)
8
+ ```html
9
+ <script
10
+ src="https://cdn.jsdelivr.net/npm/@paywallprotect/widget@1/dist/paywall-widget.min.js"
11
+ data-site-id="your_site_id"
12
+ data-api-key="your_api_key"
13
+ data-api-url="https://your-project.supabase.co/functions/v1"
14
+ async
15
+ ></script>
16
+ ```
17
+
18
+ ### Via npm (For Build Tools)
19
+ ```bash
20
+ npm install @paywallprotect/widget
21
+ ```
22
+ ```javascript
23
+ import PaywallProtect from '@paywallprotect/widget';
24
+
25
+ PaywallProtect.init({
26
+ siteId: 'your_site_id',
27
+ apiKey: 'your_api_key',
28
+ apiUrl: 'https://your-project.supabase.co/functions/v1'
29
+ });
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ### Mark Protected Content
35
+ ```html
36
+ <article class="paywall-protected">
37
+ Your premium content here
38
+ </article>
39
+ ```
40
+
41
+ Or use data attribute:
42
+ ```html
43
+ <div data-paywall="true">
44
+ Premium content
45
+ </div>
46
+ ```
47
+
48
+ ## API
49
+ ```javascript
50
+ // Manual initialization
51
+ PaywallProtect.init({
52
+ siteId: 'site_xxx',
53
+ apiKey: 'pk_live_xxx',
54
+ apiUrl: 'https://...',
55
+ subscribeUrl: '/pricing',
56
+ loginUrl: '/login',
57
+ mode: 'auto' // or 'always', 'never'
58
+ });
59
+
60
+ // Show paywall manually
61
+ PaywallProtect.showPaywall({
62
+ type: 'hard',
63
+ title: 'Premium Content',
64
+ message: 'Subscribe to continue'
65
+ });
66
+
67
+ // Hide paywall
68
+ PaywallProtect.hidePaywall();
69
+
70
+ // Reload protection
71
+ PaywallProtect.reload();
72
+ ```
73
+
74
+ ## License
75
+
76
+ MIT
@@ -0,0 +1,32 @@
1
+ var u=(r,n)=>()=>(n||r((n={exports:{}}).exports,n),n.exports);var p=(r,n,a)=>new Promise((e,t)=>{var o=i=>{try{c(a.next(i))}catch(s){t(s)}},l=i=>{try{c(a.throw(i))}catch(s){t(s)}},c=i=>i.done?e(i.value):Promise.resolve(i.value).then(o,l);c((a=a.apply(r,n)).next())});var y=u((g,d)=>{(function(r){"use strict";let n={version:"1.0.0",init:function(e){if(!e.siteId||!e.apiKey){console.error("[PaywallProtect] Missing siteId or apiKey");return}this.config={siteId:e.siteId,apiKey:e.apiKey,apiUrl:e.apiUrl||"",subscribeUrl:e.subscribeUrl||"/subscribe",loginUrl:e.loginUrl||"/login",mode:e.mode||"auto"},console.log("[PaywallProtect] Initializing...",{siteId:this.config.siteId}),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>this.start()):this.start()},start:function(){if(this.config.mode==="never"){console.log("[PaywallProtect] Disabled");return}if(this.config.mode==="always"){this.blurContent(),this.createPaywallModal({type:"hard"});return}this.checkAccess()},generateFingerprint:function(){return{userAgent:navigator.userAgent,language:navigator.language,platform:navigator.platform,hardwareConcurrency:navigator.hardwareConcurrency||0,deviceMemory:navigator.deviceMemory||0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,screen:{width:r.screen.width,height:r.screen.height,colorDepth:r.screen.colorDepth,pixelRatio:r.devicePixelRatio||1},timing:{pageLoadTime:Math.round(performance.now())},webdriver:navigator.webdriver||!1,canvas:this.getCanvasFingerprint(),webgl:this.getWebGLFingerprint(),touchSupport:"ontouchstart"in r,plugins:this.getPluginsList()}},getCanvasFingerprint:function(){try{let e=document.createElement("canvas"),t=e.getContext("2d");return t?(t.textBaseline="top",t.font="14px Arial",t.fillText("PaywallProtect",2,2),e.toDataURL().substring(0,100)):null}catch(e){return null}},getWebGLFingerprint:function(){try{let t=document.createElement("canvas").getContext("webgl");return t?{renderer:t.getParameter(t.RENDERER),vendor:t.getParameter(t.VENDOR)}:null}catch(e){return null}},getPluginsList:function(){try{return Array.from(navigator.plugins||[]).map(e=>e.name).slice(0,5)}catch(e){return[]}},checkAccess:function(){return p(this,null,function*(){try{let e=yield fetch(this.config.apiUrl+"/check-access",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({siteId:this.config.siteId,apiKey:this.config.apiKey,page:r.location.pathname,userAgent:navigator.userAgent,fingerprint:this.generateFingerprint(),referrer:document.referrer})});if(!e.ok){console.error("[PaywallProtect] API error:",e.status);return}let t=yield e.json();console.log("[PaywallProtect] Decision:",t),!t.allowed&&t.status==="blocked"?(this.blurContent(),this.createPaywallModal({type:"bot-blocked",message:t.reason})):t.showPaywall&&t.paywallConfig&&(this.blurContent(),this.createPaywallModal(t.paywallConfig))}catch(e){console.error("[PaywallProtect] Error:",e)}})},blurContent:function(){let t=document.querySelectorAll('.paywall-protected, [data-paywall="true"]');(t.length?t:[document.querySelector("main, article, .content")].filter(Boolean)).forEach(o=>{o.style.filter="blur(8px)",o.style.userSelect="none",o.style.pointerEvents="none"})},createPaywallModal:function(e){if(document.getElementById("paywall-protect-modal"))return;let t=e.type||"hard",o="";t==="hard"?o=`
2
+ <div class="paywall-icon">\u{1F512}</div>
3
+ <h2 id="paywall-title">${e.title||"Premium Content"}</h2>
4
+ <p>${e.message||"Subscribe to access this content."}</p>
5
+ <button id="paywall-subscribe" class="paywall-btn primary">Subscribe Now</button>
6
+ <p class="footer">Already a subscriber? <a href="${this.config.loginUrl}" id="paywall-login">Sign in</a></p>
7
+ `:t==="metered"?o=`
8
+ <div class="paywall-icon">\u{1F4CA}</div>
9
+ <h2 id="paywall-title">Free Article Limit Reached</h2>
10
+ <p>You've read <strong>${e.articlesRead||3} of ${e.freeLimit||3}</strong> free articles this month.</p>
11
+ <button id="paywall-subscribe" class="paywall-btn primary">Get Unlimited Access</button>
12
+ <button id="paywall-close" class="paywall-btn secondary">Maybe Later</button>
13
+ `:o=`
14
+ <div class="paywall-icon">\u{1F916}</div>
15
+ <h2 id="paywall-title">Access Denied</h2>
16
+ <p>${e.message||"Automated access is not permitted."}</p>
17
+ `;let l=document.createElement("div");if(l.id="paywall-protect-modal",l.innerHTML=`<div class="paywall-overlay"><div class="paywall-content">${o}</div></div>`,!document.getElementById("paywall-styles")){let s=document.createElement("style");s.id="paywall-styles",s.textContent=`
18
+ #paywall-protect-modal{position:fixed;top:0;left:0;width:100%;height:100%;z-index:999999;animation:fadeIn .3s}
19
+ .paywall-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.85);backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;padding:20px}
20
+ .paywall-content{background:#fff;border-radius:16px;padding:48px 40px;max-width:500px;width:100%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.3);animation:slideUp .3s}
21
+ .paywall-icon{font-size:64px;margin-bottom:24px}
22
+ #paywall-title{font-size:28px;font-weight:700;color:#1a1a1a;margin:0 0 16px 0}
23
+ .paywall-content p{font-size:16px;color:#666;line-height:1.6;margin:0 0 24px 0}
24
+ .paywall-btn{display:block;width:100%;padding:16px 32px;border:none;border-radius:8px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s;margin-bottom:12px}
25
+ .paywall-btn.primary{background:#3b82f6;color:#fff}
26
+ .paywall-btn.primary:hover{background:#2563eb}
27
+ .paywall-btn.secondary{background:transparent;color:#666;border:2px solid #e5e7eb}
28
+ .footer{margin-top:24px;font-size:14px;color:#666}
29
+ .footer a{color:#3b82f6;text-decoration:none}
30
+ @keyframes fadeIn{from{opacity:0}to{opacity:1}}
31
+ @keyframes slideUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
32
+ `,document.head.appendChild(s)}document.body.appendChild(l);let c=l.querySelector("#paywall-subscribe");c&&(c.onclick=()=>r.location.href=e.subscribeUrl||this.config.subscribeUrl);let i=l.querySelector("#paywall-close");i&&(i.onclick=()=>l.remove())},showPaywall:function(e){this.blurContent(),this.createPaywallModal(e||{type:"hard"})},hidePaywall:function(){let e=document.getElementById("paywall-protect-modal");e&&e.remove()},reload:function(){let e=document.getElementById("paywall-protect-modal");e&&e.remove(),this.start()}},a=document.currentScript||document.querySelector("script[data-site-id]");a&&a.dataset.siteId&&n.init({siteId:a.dataset.siteId,apiKey:a.dataset.apiKey,apiUrl:a.dataset.apiUrl,subscribeUrl:a.dataset.subscribeUrl,loginUrl:a.dataset.loginUrl,mode:a.dataset.mode}),r.PaywallProtect=n,typeof d!="undefined"&&d.exports&&(d.exports=n)})(typeof window!="undefined"?window:{})});export default y();
@@ -0,0 +1,32 @@
1
+ var PaywallProtect=(()=>{var u=(r,n)=>()=>(n||r((n={exports:{}}).exports,n),n.exports);var p=(r,n,a)=>new Promise((e,t)=>{var o=i=>{try{c(a.next(i))}catch(s){t(s)}},l=i=>{try{c(a.throw(i))}catch(s){t(s)}},c=i=>i.done?e(i.value):Promise.resolve(i.value).then(o,l);c((a=a.apply(r,n)).next())});var y=u((g,d)=>{(function(r){"use strict";let n={version:"1.0.0",init:function(e){if(!e.siteId||!e.apiKey){console.error("[PaywallProtect] Missing siteId or apiKey");return}this.config={siteId:e.siteId,apiKey:e.apiKey,apiUrl:e.apiUrl||"",subscribeUrl:e.subscribeUrl||"/subscribe",loginUrl:e.loginUrl||"/login",mode:e.mode||"auto"},console.log("[PaywallProtect] Initializing...",{siteId:this.config.siteId}),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>this.start()):this.start()},start:function(){if(this.config.mode==="never"){console.log("[PaywallProtect] Disabled");return}if(this.config.mode==="always"){this.blurContent(),this.createPaywallModal({type:"hard"});return}this.checkAccess()},generateFingerprint:function(){return{userAgent:navigator.userAgent,language:navigator.language,platform:navigator.platform,hardwareConcurrency:navigator.hardwareConcurrency||0,deviceMemory:navigator.deviceMemory||0,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,screen:{width:r.screen.width,height:r.screen.height,colorDepth:r.screen.colorDepth,pixelRatio:r.devicePixelRatio||1},timing:{pageLoadTime:Math.round(performance.now())},webdriver:navigator.webdriver||!1,canvas:this.getCanvasFingerprint(),webgl:this.getWebGLFingerprint(),touchSupport:"ontouchstart"in r,plugins:this.getPluginsList()}},getCanvasFingerprint:function(){try{let e=document.createElement("canvas"),t=e.getContext("2d");return t?(t.textBaseline="top",t.font="14px Arial",t.fillText("PaywallProtect",2,2),e.toDataURL().substring(0,100)):null}catch(e){return null}},getWebGLFingerprint:function(){try{let t=document.createElement("canvas").getContext("webgl");return t?{renderer:t.getParameter(t.RENDERER),vendor:t.getParameter(t.VENDOR)}:null}catch(e){return null}},getPluginsList:function(){try{return Array.from(navigator.plugins||[]).map(e=>e.name).slice(0,5)}catch(e){return[]}},checkAccess:function(){return p(this,null,function*(){try{let e=yield fetch(this.config.apiUrl+"/check-access",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({siteId:this.config.siteId,apiKey:this.config.apiKey,page:r.location.pathname,userAgent:navigator.userAgent,fingerprint:this.generateFingerprint(),referrer:document.referrer})});if(!e.ok){console.error("[PaywallProtect] API error:",e.status);return}let t=yield e.json();console.log("[PaywallProtect] Decision:",t),!t.allowed&&t.status==="blocked"?(this.blurContent(),this.createPaywallModal({type:"bot-blocked",message:t.reason})):t.showPaywall&&t.paywallConfig&&(this.blurContent(),this.createPaywallModal(t.paywallConfig))}catch(e){console.error("[PaywallProtect] Error:",e)}})},blurContent:function(){let t=document.querySelectorAll('.paywall-protected, [data-paywall="true"]');(t.length?t:[document.querySelector("main, article, .content")].filter(Boolean)).forEach(o=>{o.style.filter="blur(8px)",o.style.userSelect="none",o.style.pointerEvents="none"})},createPaywallModal:function(e){if(document.getElementById("paywall-protect-modal"))return;let t=e.type||"hard",o="";t==="hard"?o=`
2
+ <div class="paywall-icon">\u{1F512}</div>
3
+ <h2 id="paywall-title">${e.title||"Premium Content"}</h2>
4
+ <p>${e.message||"Subscribe to access this content."}</p>
5
+ <button id="paywall-subscribe" class="paywall-btn primary">Subscribe Now</button>
6
+ <p class="footer">Already a subscriber? <a href="${this.config.loginUrl}" id="paywall-login">Sign in</a></p>
7
+ `:t==="metered"?o=`
8
+ <div class="paywall-icon">\u{1F4CA}</div>
9
+ <h2 id="paywall-title">Free Article Limit Reached</h2>
10
+ <p>You've read <strong>${e.articlesRead||3} of ${e.freeLimit||3}</strong> free articles this month.</p>
11
+ <button id="paywall-subscribe" class="paywall-btn primary">Get Unlimited Access</button>
12
+ <button id="paywall-close" class="paywall-btn secondary">Maybe Later</button>
13
+ `:o=`
14
+ <div class="paywall-icon">\u{1F916}</div>
15
+ <h2 id="paywall-title">Access Denied</h2>
16
+ <p>${e.message||"Automated access is not permitted."}</p>
17
+ `;let l=document.createElement("div");if(l.id="paywall-protect-modal",l.innerHTML=`<div class="paywall-overlay"><div class="paywall-content">${o}</div></div>`,!document.getElementById("paywall-styles")){let s=document.createElement("style");s.id="paywall-styles",s.textContent=`
18
+ #paywall-protect-modal{position:fixed;top:0;left:0;width:100%;height:100%;z-index:999999;animation:fadeIn .3s}
19
+ .paywall-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.85);backdrop-filter:blur(8px);display:flex;align-items:center;justify-content:center;padding:20px}
20
+ .paywall-content{background:#fff;border-radius:16px;padding:48px 40px;max-width:500px;width:100%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.3);animation:slideUp .3s}
21
+ .paywall-icon{font-size:64px;margin-bottom:24px}
22
+ #paywall-title{font-size:28px;font-weight:700;color:#1a1a1a;margin:0 0 16px 0}
23
+ .paywall-content p{font-size:16px;color:#666;line-height:1.6;margin:0 0 24px 0}
24
+ .paywall-btn{display:block;width:100%;padding:16px 32px;border:none;border-radius:8px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s;margin-bottom:12px}
25
+ .paywall-btn.primary{background:#3b82f6;color:#fff}
26
+ .paywall-btn.primary:hover{background:#2563eb}
27
+ .paywall-btn.secondary{background:transparent;color:#666;border:2px solid #e5e7eb}
28
+ .footer{margin-top:24px;font-size:14px;color:#666}
29
+ .footer a{color:#3b82f6;text-decoration:none}
30
+ @keyframes fadeIn{from{opacity:0}to{opacity:1}}
31
+ @keyframes slideUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
32
+ `,document.head.appendChild(s)}document.body.appendChild(l);let c=l.querySelector("#paywall-subscribe");c&&(c.onclick=()=>r.location.href=e.subscribeUrl||this.config.subscribeUrl);let i=l.querySelector("#paywall-close");i&&(i.onclick=()=>l.remove())},showPaywall:function(e){this.blurContent(),this.createPaywallModal(e||{type:"hard"})},hidePaywall:function(){let e=document.getElementById("paywall-protect-modal");e&&e.remove()},reload:function(){let e=document.getElementById("paywall-protect-modal");e&&e.remove(),this.start()}},a=document.currentScript||document.querySelector("script[data-site-id]");a&&a.dataset.siteId&&n.init({siteId:a.dataset.siteId,apiKey:a.dataset.apiKey,apiUrl:a.dataset.apiUrl,subscribeUrl:a.dataset.subscribeUrl,loginUrl:a.dataset.loginUrl,mode:a.dataset.mode}),r.PaywallProtect=n,typeof d!="undefined"&&d.exports&&(d.exports=n)})(typeof window!="undefined"?window:{})});return y();})();
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "paywall-protect-widget",
3
+ "version": "1.0.0",
4
+ "description": "PaywallProtect widget for bot detection and content protection",
5
+ "main": "dist/paywall-widget.min.js",
6
+ "module": "dist/paywall-widget.esm.js",
7
+ "types": "types/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "types",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "npm run build:iife && npm run build:esm",
15
+ "build:iife": "esbuild src/index.js --bundle --minify --target=es2015 --format=iife --global-name=PaywallProtect --outfile=dist/paywall-widget.min.js",
16
+ "build:esm": "esbuild src/index.js --bundle --minify --target=es2015 --format=esm --outfile=dist/paywall-widget.esm.js",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": ["paywall", "bot-detection"],
20
+ "author": "PaywallProtect",
21
+ "license": "MIT",
22
+ "devDependencies": {
23
+ "esbuild": "^0.19.0"
24
+ }
25
+ }
@@ -0,0 +1,35 @@
1
+ export interface PaywallProtectConfig {
2
+ siteId: string;
3
+ apiKey: string;
4
+ apiUrl?: string;
5
+ subscribeUrl?: string;
6
+ loginUrl?: string;
7
+ mode?: 'auto' | 'always' | 'never';
8
+ }
9
+
10
+ export interface PaywallConfig {
11
+ type: 'hard' | 'metered' | 'bot-blocked';
12
+ title?: string;
13
+ message?: string;
14
+ articlesRead?: number;
15
+ freeLimit?: number;
16
+ subscribeUrl?: string;
17
+ loginUrl?: string;
18
+ }
19
+
20
+ export interface PaywallProtect {
21
+ version: string;
22
+ init(config: PaywallProtectConfig): void;
23
+ showPaywall(config?: PaywallConfig): void;
24
+ hidePaywall(): void;
25
+ reload(): void;
26
+ }
27
+
28
+ declare global {
29
+ interface Window {
30
+ PaywallProtect: PaywallProtect;
31
+ }
32
+ }
33
+
34
+ export const PaywallProtect: PaywallProtect;
35
+ export default PaywallProtect;