create-brainerce-store 1.3.2 → 1.3.3
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/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "create-brainerce-store",
|
|
34
|
-
version: "1.
|
|
34
|
+
version: "1.3.3",
|
|
35
35
|
description: "Scaffold a production-ready e-commerce storefront connected to Brainerce",
|
|
36
36
|
bin: {
|
|
37
37
|
"create-brainerce-store": "dist/index.js"
|
package/package.json
CHANGED
|
@@ -18,6 +18,8 @@ declare global {
|
|
|
18
18
|
onError?: (response: unknown) => void;
|
|
19
19
|
onTimeout?: (response: unknown) => void;
|
|
20
20
|
onWalletChange?: (state: string) => void;
|
|
21
|
+
onPaymentStart?: (response: unknown) => void;
|
|
22
|
+
onPaymentCancel?: (response: unknown) => void;
|
|
21
23
|
};
|
|
22
24
|
}) => void;
|
|
23
25
|
renderPaymentOptions: (authCode: string) => void;
|
|
@@ -33,14 +35,60 @@ interface PaymentStepProps {
|
|
|
33
35
|
className?: string;
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Load a script tag if not already present in the DOM.
|
|
40
|
+
* Resolves when the script loads (or immediately if already present).
|
|
41
|
+
*/
|
|
42
|
+
function loadScript(src: string, optional = false): Promise<void> {
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
if (document.querySelector(`script[src="${src}"]`)) {
|
|
45
|
+
resolve();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const script = document.createElement('script');
|
|
49
|
+
script.src = src;
|
|
50
|
+
script.async = true;
|
|
51
|
+
script.onload = () => resolve();
|
|
52
|
+
script.onerror = () => {
|
|
53
|
+
if (optional) {
|
|
54
|
+
resolve(); // Non-blocking for optional SDKs
|
|
55
|
+
} else {
|
|
56
|
+
resolve(); // Still resolve — caller handles missing global
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
document.head.appendChild(script);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Wait for window.growPayment to become available (set by the SDK script).
|
|
65
|
+
*/
|
|
66
|
+
function waitForGrowGlobal(timeoutMs = 5000): Promise<boolean> {
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
if (window.growPayment) {
|
|
69
|
+
resolve(true);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const start = Date.now();
|
|
73
|
+
const check = setInterval(() => {
|
|
74
|
+
if (window.growPayment) {
|
|
75
|
+
clearInterval(check);
|
|
76
|
+
resolve(true);
|
|
77
|
+
} else if (Date.now() - start > timeoutMs) {
|
|
78
|
+
clearInterval(check);
|
|
79
|
+
resolve(false);
|
|
80
|
+
}
|
|
81
|
+
}, 50);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
36
85
|
export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
|
|
37
86
|
const [paymentIntent, setPaymentIntent] = useState<PaymentIntent | null>(null);
|
|
38
87
|
const [loading, setLoading] = useState(true);
|
|
39
88
|
const [error, setError] = useState<string | null>(null);
|
|
40
89
|
const [growReady, setGrowReady] = useState(false);
|
|
41
|
-
const [
|
|
42
|
-
const
|
|
43
|
-
const growRenderCalled = useRef(false);
|
|
90
|
+
const [sdkScriptLoaded, setSdkScriptLoaded] = useState(false);
|
|
91
|
+
const renderAttempted = useRef(false);
|
|
44
92
|
|
|
45
93
|
const handleGrowSuccess = useCallback(
|
|
46
94
|
async (response: unknown) => {
|
|
@@ -71,90 +119,36 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
|
|
|
71
119
|
setError(msg);
|
|
72
120
|
}, []);
|
|
73
121
|
|
|
74
|
-
// Step 1: Load
|
|
75
|
-
// This matches the documented flow where init() is called on script.onload
|
|
122
|
+
// Step 1: Load SDK scripts (Apple Pay + Grow) — just load, don't init yet
|
|
76
123
|
useEffect(() => {
|
|
77
|
-
|
|
78
|
-
growInitCalled.current = true;
|
|
79
|
-
|
|
80
|
-
// Load Apple Pay SDK (required for Apple Pay in Grow wallet)
|
|
81
|
-
function loadApplePaySdk(): Promise<void> {
|
|
82
|
-
return new Promise((resolve) => {
|
|
83
|
-
if (document.querySelector(`script[src="${APPLE_PAY_SDK_URL}"]`)) {
|
|
84
|
-
resolve();
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const script = document.createElement('script');
|
|
88
|
-
script.src = APPLE_PAY_SDK_URL;
|
|
89
|
-
script.async = true;
|
|
90
|
-
script.onload = () => resolve();
|
|
91
|
-
script.onerror = () => {
|
|
92
|
-
console.warn('Apple Pay SDK failed to load — Apple Pay will be unavailable');
|
|
93
|
-
resolve(); // Non-blocking: other payment methods still work
|
|
94
|
-
};
|
|
95
|
-
document.head.appendChild(script);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
124
|
+
let cancelled = false;
|
|
98
125
|
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
126
|
+
async function loadSdkScripts() {
|
|
127
|
+
// Load Apple Pay SDK first (optional — needed for Apple Pay in Grow wallet)
|
|
128
|
+
await loadScript(APPLE_PAY_SDK_URL, true);
|
|
129
|
+
|
|
130
|
+
// Load Grow SDK script
|
|
131
|
+
await loadScript(GROW_SDK_URL);
|
|
132
|
+
|
|
133
|
+
// Wait for growPayment global to be set by the script
|
|
134
|
+
const available = await waitForGrowGlobal();
|
|
135
|
+
|
|
136
|
+
if (cancelled) return;
|
|
104
137
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const check = setInterval(() => {
|
|
108
|
-
if (window.growPayment) {
|
|
109
|
-
clearInterval(check);
|
|
110
|
-
initSdk('DEV');
|
|
111
|
-
}
|
|
112
|
-
}, 100);
|
|
138
|
+
if (!available) {
|
|
139
|
+
setError('Failed to load payment SDK');
|
|
113
140
|
return;
|
|
114
141
|
}
|
|
115
142
|
|
|
116
|
-
|
|
117
|
-
script.type = 'text/javascript';
|
|
118
|
-
script.async = true;
|
|
119
|
-
script.src = GROW_SDK_URL;
|
|
120
|
-
script.onload = () => {
|
|
121
|
-
// Wait for growPayment global to be set
|
|
122
|
-
const check = setInterval(() => {
|
|
123
|
-
if (window.growPayment) {
|
|
124
|
-
clearInterval(check);
|
|
125
|
-
initSdk('DEV');
|
|
126
|
-
}
|
|
127
|
-
}, 50);
|
|
128
|
-
// Timeout after 5s
|
|
129
|
-
setTimeout(() => clearInterval(check), 5000);
|
|
130
|
-
};
|
|
131
|
-
script.onerror = () => {
|
|
132
|
-
setError('Failed to load payment SDK');
|
|
133
|
-
};
|
|
134
|
-
document.head.appendChild(script);
|
|
143
|
+
setSdkScriptLoaded(true);
|
|
135
144
|
}
|
|
136
145
|
|
|
137
|
-
|
|
138
|
-
if (!window.growPayment) return;
|
|
139
|
-
console.info('Grow SDK: calling init with environment:', env);
|
|
140
|
-
window.growPayment.init({
|
|
141
|
-
environment: env,
|
|
142
|
-
version: 1,
|
|
143
|
-
events: {
|
|
144
|
-
onSuccess: handleGrowSuccess,
|
|
145
|
-
onFailure: handleGrowFailure,
|
|
146
|
-
onError: handleGrowError,
|
|
147
|
-
onWalletChange: (state: string) => {
|
|
148
|
-
console.info('Grow wallet state:', state);
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
setGrowSdkInitialized(true);
|
|
153
|
-
}
|
|
146
|
+
loadSdkScripts();
|
|
154
147
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
148
|
+
return () => {
|
|
149
|
+
cancelled = true;
|
|
150
|
+
};
|
|
151
|
+
}, []);
|
|
158
152
|
|
|
159
153
|
// Step 2: Create payment intent (API call)
|
|
160
154
|
useEffect(() => {
|
|
@@ -189,30 +183,47 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
|
|
|
189
183
|
createIntent();
|
|
190
184
|
}, [checkoutId]);
|
|
191
185
|
|
|
192
|
-
// Step 3: When BOTH SDK is
|
|
186
|
+
// Step 3: When BOTH SDK script is loaded AND payment intent is ready:
|
|
187
|
+
// - init() with the CORRECT environment from the payment intent
|
|
188
|
+
// - then renderPaymentOptions() with the authCode
|
|
193
189
|
useEffect(() => {
|
|
194
|
-
if (!
|
|
190
|
+
if (!sdkScriptLoaded) return;
|
|
195
191
|
if (!paymentIntent || paymentIntent.provider !== 'grow') return;
|
|
196
|
-
if (growRenderCalled.current) return;
|
|
197
192
|
if (!window.growPayment) return;
|
|
193
|
+
if (renderAttempted.current) return;
|
|
198
194
|
|
|
199
|
-
|
|
195
|
+
renderAttempted.current = true;
|
|
200
196
|
|
|
201
|
-
// Parse authCode from clientSecret (format: "ENV|authCode")
|
|
197
|
+
// Parse environment and authCode from clientSecret (format: "ENV|authCode")
|
|
202
198
|
const pipeIndex = paymentIntent.clientSecret.indexOf('|');
|
|
199
|
+
const env = pipeIndex !== -1 ? paymentIntent.clientSecret.substring(0, pipeIndex) : 'DEV';
|
|
203
200
|
const authCode =
|
|
204
201
|
pipeIndex !== -1
|
|
205
202
|
? paymentIntent.clientSecret.substring(pipeIndex + 1)
|
|
206
203
|
: paymentIntent.clientSecret;
|
|
207
204
|
|
|
208
|
-
|
|
205
|
+
// Init SDK with the correct environment (must match the authCode's origin)
|
|
206
|
+
console.info('Grow SDK: init with environment:', env);
|
|
207
|
+
window.growPayment.init({
|
|
208
|
+
environment: env,
|
|
209
|
+
version: 1,
|
|
210
|
+
events: {
|
|
211
|
+
onSuccess: handleGrowSuccess,
|
|
212
|
+
onFailure: handleGrowFailure,
|
|
213
|
+
onError: handleGrowError,
|
|
214
|
+
onWalletChange: (state: string) => {
|
|
215
|
+
console.info('Grow wallet state:', state);
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
});
|
|
209
219
|
|
|
210
|
-
//
|
|
220
|
+
// Render payment options — small delay to allow init() to complete internal setup
|
|
221
|
+
console.info('Grow SDK: calling renderPaymentOptions');
|
|
211
222
|
setTimeout(() => {
|
|
212
223
|
window.growPayment?.renderPaymentOptions(authCode);
|
|
213
224
|
setGrowReady(true);
|
|
214
|
-
},
|
|
215
|
-
}, [
|
|
225
|
+
}, 300);
|
|
226
|
+
}, [sdkScriptLoaded, paymentIntent, handleGrowSuccess, handleGrowFailure, handleGrowError]);
|
|
216
227
|
|
|
217
228
|
if (loading) {
|
|
218
229
|
return (
|