react-gamified-captcha 1.0.0 → 1.0.1
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/CaptchaWidget.js +23 -45
- package/README.md +2 -11
- package/package.json +1 -1
package/CaptchaWidget.js
CHANGED
|
@@ -1,65 +1,43 @@
|
|
|
1
|
-
import React, { useEffect
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
export const GamifiedCaptcha = ({
|
|
4
4
|
siteKey = "ch_pub_demo_testkey_12345",
|
|
5
|
-
onHumanVerified,
|
|
6
|
-
|
|
5
|
+
onHumanVerified,
|
|
6
|
+
gameUrl = "https://conversion.business/sunny-day-maze/",
|
|
7
7
|
className = "conversion-business-widget",
|
|
8
8
|
style = {}
|
|
9
9
|
}) => {
|
|
10
|
-
const [hasError, setHasError] = useState(false);
|
|
11
10
|
const isInvalidKey = !siteKey || siteKey === "ch_pub_demo_testkey_12345";
|
|
12
11
|
|
|
13
12
|
useEffect(() => {
|
|
14
|
-
//
|
|
15
|
-
if (typeof window === 'undefined'
|
|
13
|
+
// SSR Safety Check
|
|
14
|
+
if (typeof window === 'undefined') {
|
|
16
15
|
return;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
// 2. Funnel Logic (Console Warning for missing/demo keys)
|
|
20
18
|
if (isInvalidKey) {
|
|
21
19
|
console.error("Conversion.Business Error: Invalid Site Key. Please register at https://conversion.business to obtain a valid API key.");
|
|
22
|
-
setHasError(true);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const scriptId = 'conversion-business-sdk';
|
|
27
|
-
let script = document.getElementById(scriptId);
|
|
28
|
-
let isNewScript = false;
|
|
29
|
-
|
|
30
|
-
// 3. Singleton Script Loading
|
|
31
|
-
if (!script) {
|
|
32
|
-
script = document.createElement('script');
|
|
33
|
-
script.id = scriptId;
|
|
34
|
-
script.src = 'https://conversion-business-widgets.web.app/sdk.js';
|
|
35
|
-
script.async = true;
|
|
36
|
-
isNewScript = true;
|
|
37
|
-
|
|
38
|
-
// 4. CSP/Network Error Handling (Fail-Open)
|
|
39
|
-
script.onerror = (e) => {
|
|
40
|
-
console.error("Conversion.Business Error: Failed to load SDK. Check your Content Security Policy or AdBlocker.", e);
|
|
41
|
-
if (onError) onError(e);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
document.body.appendChild(script);
|
|
45
20
|
}
|
|
46
21
|
|
|
47
|
-
const verificationHandler = (
|
|
48
|
-
|
|
49
|
-
|
|
22
|
+
const verificationHandler = (event) => {
|
|
23
|
+
// Ensure we only process events from the conversion.business iframe
|
|
24
|
+
if (event.data && event.data.type === 'oops_captcha_solved') {
|
|
25
|
+
if (onHumanVerified && event.data.payload) {
|
|
26
|
+
onHumanVerified(event.data.payload);
|
|
27
|
+
}
|
|
50
28
|
}
|
|
51
29
|
};
|
|
52
30
|
|
|
53
|
-
|
|
31
|
+
window.addEventListener('message', verificationHandler);
|
|
54
32
|
|
|
55
33
|
return () => {
|
|
56
|
-
|
|
57
|
-
|
|
34
|
+
// Memory leak cleanup when component unmounts
|
|
35
|
+
window.removeEventListener('message', verificationHandler);
|
|
58
36
|
};
|
|
59
|
-
}, [siteKey, onHumanVerified,
|
|
37
|
+
}, [siteKey, onHumanVerified, isInvalidKey]);
|
|
60
38
|
|
|
61
|
-
//
|
|
62
|
-
if (
|
|
39
|
+
// Visual Fallback for missing/demo keys (The Funnel Trap)
|
|
40
|
+
if (isInvalidKey) {
|
|
63
41
|
return (
|
|
64
42
|
<div style={{ color: '#d32f2f', border: '1px solid #d32f2f', padding: '12px', borderRadius: '4px', backgroundColor: '#fff', fontFamily: 'sans-serif', ...style }} className={className}>
|
|
65
43
|
<strong>Widget Error:</strong> Valid API Key Required. <a href="https://conversion.business" target="_blank" rel="noopener noreferrer" style={{color: '#d32f2f', textDecoration: 'underline'}}>Get your free key here</a>.
|
|
@@ -67,13 +45,13 @@ export const GamifiedCaptcha = ({
|
|
|
67
45
|
);
|
|
68
46
|
}
|
|
69
47
|
|
|
70
|
-
//
|
|
48
|
+
// Exact iframe architecture matching portal.html
|
|
71
49
|
return (
|
|
72
|
-
<
|
|
73
|
-
className={className}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
50
|
+
<iframe
|
|
51
|
+
className={className}
|
|
52
|
+
src={`${gameUrl}?mode=captcha&clientId=${siteKey}`}
|
|
53
|
+
style={{ width: "100%", height: "400px", border: "none", borderRadius: "12px", ...style }}
|
|
54
|
+
title="Conversion.Business Validation"
|
|
77
55
|
/>
|
|
78
56
|
);
|
|
79
57
|
};
|
package/README.md
CHANGED
|
@@ -19,13 +19,8 @@ import { GamifiedCaptcha } from 'react-gamified-captcha';
|
|
|
19
19
|
|
|
20
20
|
function App() {
|
|
21
21
|
const handleVerify = (token) => {
|
|
22
|
-
console.log("Human verified! Token:", token);
|
|
23
|
-
// Send this
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const handleError = (error) => {
|
|
27
|
-
console.warn("Captcha failed to load or encountered an error. Failing open.", error);
|
|
28
|
-
// Best practice: Fail-open by allowing the user to submit the form anyway
|
|
22
|
+
console.log("Human verified! Token payload:", token);
|
|
23
|
+
// Send this payload to your backend for cryptographic HMAC verification
|
|
29
24
|
};
|
|
30
25
|
|
|
31
26
|
return (
|
|
@@ -35,7 +30,6 @@ function App() {
|
|
|
35
30
|
<GamifiedCaptcha
|
|
36
31
|
siteKey="YOUR_CONVERSION_BUSINESS_API_KEY"
|
|
37
32
|
onHumanVerified={handleVerify}
|
|
38
|
-
onError={handleError}
|
|
39
33
|
/>
|
|
40
34
|
|
|
41
35
|
<button type="submit">Submit</button>
|
|
@@ -46,6 +40,3 @@ function App() {
|
|
|
46
40
|
|
|
47
41
|
## Next.js (SSR) Support
|
|
48
42
|
This component is fully SSR-safe and can be used directly in Next.js or Remix applications without requiring dynamic imports.
|
|
49
|
-
|
|
50
|
-
## Graceful Degradation (`onError`)
|
|
51
|
-
If a user has an aggressive ad-blocker or strict Content Security Policy that prevents our script from loading, the `onError` callback will fire. We strongly recommend implementing a **fail-open** strategy in this scenario. If `onError` triggers, simply let the user bypass the captcha so you don't lose the signup.
|