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 +76 -0
- package/dist/paywall-widget.esm.js +32 -0
- package/dist/paywall-widget.min.js +32 -0
- package/package.json +25 -0
- package/types/index.d.ts +35 -0
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
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -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;
|