0
# Embedded Checkout
1
2
Complete embedded checkout solution for streamlined payment experiences. Provides a fully-hosted checkout flow that can be embedded directly into your application.
3
4
## Capabilities
5
6
### EmbeddedCheckoutProvider
7
8
Context provider that manages the embedded checkout instance and configuration throughout your React application.
9
10
```typescript { .api }
11
/**
12
* React context provider for Embedded Checkout
13
* @param props - Embedded checkout provider configuration props
14
* @returns JSX element wrapping child components with embedded checkout context
15
*/
16
function EmbeddedCheckoutProvider(props: EmbeddedCheckoutProviderProps): JSX.Element;
17
18
interface EmbeddedCheckoutProviderProps {
19
/** Stripe instance or Promise resolving to Stripe instance */
20
stripe: PromiseLike<Stripe | null> | Stripe | null;
21
/** Embedded checkout configuration options */
22
options: EmbeddedCheckoutOptions;
23
/** Child components that will have access to embedded checkout context */
24
children: ReactNode;
25
}
26
27
interface EmbeddedCheckoutOptions {
28
/** Client secret for the checkout session */
29
clientSecret?: string | null;
30
/** Function to fetch client secret dynamically */
31
fetchClientSecret?: (() => Promise<string>) | null;
32
/** Callback fired when checkout is completed successfully */
33
onComplete?: () => void;
34
/** Callback fired when checkout encounters an error */
35
onError?: (error: {message: string}) => void;
36
/** Callback fired when shipping details change */
37
onShippingDetailsChange?: (
38
event: StripeEmbeddedCheckoutShippingDetailsChangeEvent
39
) => Promise<ResultAction>;
40
/** Callback fired when line items change */
41
onLineItemsChange?: (
42
event: StripeEmbeddedCheckoutLineItemsChangeEvent
43
) => Promise<ResultAction>;
44
}
45
```
46
47
### EmbeddedCheckout
48
49
The embedded checkout component that renders the complete checkout experience within your application.
50
51
```typescript { .api }
52
/**
53
* Embedded checkout component that mounts the checkout experience
54
* @param props - Embedded checkout component props
55
* @returns JSX element for embedded checkout interface
56
*/
57
function EmbeddedCheckout(props: EmbeddedCheckoutProps): JSX.Element;
58
59
interface EmbeddedCheckoutProps {
60
/** HTML id attribute for the checkout container */
61
id?: string;
62
/** CSS class name for the checkout container */
63
className?: string;
64
}
65
```
66
67
**Basic Usage Examples:**
68
69
```typescript
70
import React, { useState, useCallback } from 'react';
71
import { loadStripe } from '@stripe/stripe-js';
72
import {
73
EmbeddedCheckoutProvider,
74
EmbeddedCheckout
75
} from '@stripe/react-stripe-js';
76
77
const stripePromise = loadStripe('pk_test_...');
78
79
const CheckoutForm = () => {
80
const [clientSecret, setClientSecret] = useState('');
81
82
// Fetch client secret from your backend
83
const fetchClientSecret = useCallback(async () => {
84
const response = await fetch('/api/create-checkout-session', {
85
method: 'POST',
86
headers: {
87
'Content-Type': 'application/json',
88
},
89
body: JSON.stringify({
90
// Your checkout session parameters
91
line_items: [
92
{
93
price: 'price_1234',
94
quantity: 1,
95
},
96
],
97
}),
98
});
99
100
const { client_secret } = await response.json();
101
return client_secret;
102
}, []);
103
104
const handleComplete = () => {
105
console.log('Checkout completed successfully!');
106
// Redirect to success page or show confirmation
107
window.location.href = '/checkout/success';
108
};
109
110
const handleError = (error) => {
111
console.error('Checkout error:', error.message);
112
// Handle checkout errors
113
};
114
115
return (
116
<div className="checkout-container">
117
<EmbeddedCheckoutProvider
118
stripe={stripePromise}
119
options={{
120
fetchClientSecret,
121
onComplete: handleComplete,
122
onError: handleError
123
}}
124
>
125
<EmbeddedCheckout className="embedded-checkout" />
126
</EmbeddedCheckoutProvider>
127
</div>
128
);
129
};
130
131
export default CheckoutForm;
132
```
133
134
**Advanced Usage with Static Client Secret:**
135
136
```typescript
137
import React from 'react';
138
import { loadStripe } from '@stripe/stripe-js';
139
import {
140
EmbeddedCheckoutProvider,
141
EmbeddedCheckout
142
} from '@stripe/react-stripe-js';
143
144
const stripePromise = loadStripe('pk_test_...');
145
146
const StaticCheckoutForm = ({ checkoutSessionClientSecret }) => {
147
const handleComplete = () => {
148
// Handle successful checkout
149
console.log('Payment completed!');
150
151
// You might want to:
152
// - Show a success message
153
// - Redirect to a confirmation page
154
// - Update your app state
155
// - Send analytics events
156
};
157
158
const handleError = (error) => {
159
// Handle checkout errors
160
console.error('Checkout failed:', error.message);
161
162
// You might want to:
163
// - Show an error message to the user
164
// - Log the error for debugging
165
// - Provide alternative payment options
166
// - Contact support information
167
};
168
169
return (
170
<div className="checkout-page">
171
<div className="checkout-header">
172
<h1>Complete Your Purchase</h1>
173
<p>Secure checkout powered by Stripe</p>
174
</div>
175
176
<EmbeddedCheckoutProvider
177
stripe={stripePromise}
178
options={{
179
clientSecret: checkoutSessionClientSecret,
180
onComplete: handleComplete,
181
onError: handleError
182
}}
183
>
184
<EmbeddedCheckout
185
id="checkout-form"
186
className="my-embedded-checkout"
187
/>
188
</EmbeddedCheckoutProvider>
189
190
<div className="checkout-footer">
191
<p>Questions? Contact support@example.com</p>
192
</div>
193
</div>
194
);
195
};
196
```
197
198
**Server-Side Rendering (SSR) Example:**
199
200
```typescript
201
import React, { useState, useEffect } from 'react';
202
import { loadStripe } from '@stripe/stripe-js';
203
import {
204
EmbeddedCheckoutProvider,
205
EmbeddedCheckout
206
} from '@stripe/react-stripe-js';
207
208
// SSR-safe component
209
const SSRCheckoutForm = ({ initialClientSecret = null }) => {
210
const [stripePromise] = useState(() =>
211
typeof window !== 'undefined' ? loadStripe('pk_test_...') : null
212
);
213
const [clientSecret, setClientSecret] = useState(initialClientSecret);
214
215
useEffect(() => {
216
if (!clientSecret && typeof window !== 'undefined') {
217
// Fetch client secret on client-side mount
218
fetchCheckoutSession();
219
}
220
}, [clientSecret]);
221
222
const fetchCheckoutSession = async () => {
223
try {
224
const response = await fetch('/api/create-checkout-session', {
225
method: 'POST',
226
});
227
const { client_secret } = await response.json();
228
setClientSecret(client_secret);
229
} catch (error) {
230
console.error('Failed to create checkout session:', error);
231
}
232
};
233
234
const fetchClientSecret = async () => {
235
return clientSecret || fetchCheckoutSession();
236
};
237
238
// Don't render checkout on server
239
if (typeof window === 'undefined') {
240
return (
241
<div className="checkout-loading">
242
<div>Loading checkout...</div>
243
</div>
244
);
245
}
246
247
// Don't render without Stripe or client secret
248
if (!stripePromise || !clientSecret) {
249
return (
250
<div className="checkout-loading">
251
<div>Preparing checkout...</div>
252
</div>
253
);
254
}
255
256
return (
257
<EmbeddedCheckoutProvider
258
stripe={stripePromise}
259
options={{
260
clientSecret,
261
onComplete: () => {
262
window.location.href = '/success';
263
},
264
onError: (error) => {
265
console.error('Checkout error:', error);
266
}
267
}}
268
>
269
<EmbeddedCheckout />
270
</EmbeddedCheckoutProvider>
271
);
272
};
273
```
274
275
**Custom Styling and Integration:**
276
277
```typescript
278
import React, { useState } from 'react';
279
import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js';
280
281
const CustomizedCheckout = ({ stripePromise, clientSecret }) => {
282
const [isLoading, setIsLoading] = useState(true);
283
const [checkoutError, setCheckoutError] = useState(null);
284
285
const handleComplete = () => {
286
setIsLoading(false);
287
// Custom success handling
288
console.log('Checkout completed!');
289
290
// Example: Show success animation, then redirect
291
setTimeout(() => {
292
window.location.href = '/order-confirmation';
293
}, 2000);
294
};
295
296
const handleError = (error) => {
297
setIsLoading(false);
298
setCheckoutError(error.message);
299
300
// Custom error handling
301
console.error('Checkout failed:', error);
302
};
303
304
return (
305
<div className="custom-checkout-container">
306
<style jsx>{`
307
.custom-checkout-container {
308
max-width: 600px;
309
margin: 0 auto;
310
padding: 20px;
311
background: #ffffff;
312
border-radius: 12px;
313
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
314
}
315
316
.checkout-header {
317
text-align: center;
318
margin-bottom: 30px;
319
}
320
321
.checkout-title {
322
font-size: 24px;
323
font-weight: bold;
324
color: #333;
325
margin-bottom: 8px;
326
}
327
328
.checkout-subtitle {
329
color: #666;
330
font-size: 16px;
331
}
332
333
.embedded-checkout {
334
min-height: 400px;
335
border-radius: 8px;
336
overflow: hidden;
337
}
338
339
.loading-indicator {
340
display: flex;
341
justify-content: center;
342
align-items: center;
343
height: 200px;
344
color: #666;
345
}
346
347
.error-message {
348
background: #fee;
349
border: 1px solid #fcc;
350
color: #c00;
351
padding: 12px;
352
border-radius: 6px;
353
margin-bottom: 20px;
354
}
355
`}</style>
356
357
<div className="checkout-header">
358
<h1 className="checkout-title">Secure Checkout</h1>
359
<p className="checkout-subtitle">
360
Complete your purchase securely with Stripe
361
</p>
362
</div>
363
364
{checkoutError && (
365
<div className="error-message">
366
<strong>Checkout Error:</strong> {checkoutError}
367
<button
368
onClick={() => window.location.reload()}
369
style={{ marginLeft: '10px' }}
370
>
371
Try Again
372
</button>
373
</div>
374
)}
375
376
<EmbeddedCheckoutProvider
377
stripe={stripePromise}
378
options={{
379
clientSecret,
380
onComplete: handleComplete,
381
onError: handleError
382
}}
383
>
384
<EmbeddedCheckout className="embedded-checkout" />
385
</EmbeddedCheckoutProvider>
386
387
{isLoading && (
388
<div className="loading-indicator">
389
Loading secure checkout...
390
</div>
391
)}
392
</div>
393
);
394
};
395
```
396
397
**Integration with React Router:**
398
399
```typescript
400
import React, { useEffect, useState } from 'react';
401
import { useParams, useNavigate } from 'react-router-dom';
402
import { loadStripe } from '@stripe/stripe-js';
403
import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js';
404
405
const stripePromise = loadStripe('pk_test_...');
406
407
const CheckoutPage = () => {
408
const { sessionId } = useParams();
409
const navigate = useNavigate();
410
const [clientSecret, setClientSecret] = useState(null);
411
const [error, setError] = useState(null);
412
413
useEffect(() => {
414
if (sessionId) {
415
// Retrieve existing checkout session
416
fetchExistingSession(sessionId);
417
} else {
418
// Create new checkout session
419
createNewSession();
420
}
421
}, [sessionId]);
422
423
const fetchExistingSession = async (sessionId) => {
424
try {
425
const response = await fetch(`/api/checkout-sessions/${sessionId}`);
426
const session = await response.json();
427
setClientSecret(session.client_secret);
428
} catch (err) {
429
setError('Failed to load checkout session');
430
}
431
};
432
433
const createNewSession = async () => {
434
try {
435
const response = await fetch('/api/create-checkout-session', {
436
method: 'POST',
437
headers: { 'Content-Type': 'application/json' },
438
body: JSON.stringify({
439
// session parameters
440
}),
441
});
442
const { client_secret } = await response.json();
443
setClientSecret(client_secret);
444
} catch (err) {
445
setError('Failed to create checkout session');
446
}
447
};
448
449
const handleComplete = () => {
450
// Navigate to success page
451
navigate('/checkout/success');
452
};
453
454
const handleError = (error) => {
455
console.error('Checkout error:', error);
456
navigate('/checkout/error', { state: { error: error.message } });
457
};
458
459
if (error) {
460
return <div className="error">Error: {error}</div>;
461
}
462
463
if (!clientSecret) {
464
return <div className="loading">Loading checkout...</div>;
465
}
466
467
return (
468
<div className="checkout-page">
469
<EmbeddedCheckoutProvider
470
stripe={stripePromise}
471
options={{
472
clientSecret,
473
onComplete: handleComplete,
474
onError: handleError
475
}}
476
>
477
<EmbeddedCheckout />
478
</EmbeddedCheckoutProvider>
479
</div>
480
);
481
};
482
483
export default CheckoutPage;
484
```
485
486
## Context Hook Usage
487
488
```typescript { .api }
489
/**
490
* Hook to access embedded checkout context
491
* @returns Embedded checkout context value
492
*/
493
function useEmbeddedCheckoutContext(): EmbeddedCheckoutContextValue;
494
495
interface EmbeddedCheckoutContextValue {
496
/** Embedded checkout instance */
497
embeddedCheckout: EmbeddedCheckoutPublicInterface | null;
498
}
499
500
interface EmbeddedCheckoutPublicInterface {
501
/** Mount the checkout to a DOM element */
502
mount(location: string | HTMLElement): void;
503
/** Unmount the checkout from its current location */
504
unmount(): void;
505
/** Destroy the checkout instance */
506
destroy(): void;
507
}
508
```
509
510
**Custom Hook Usage Example:**
511
512
```typescript
513
import React, { useEffect, useRef } from 'react';
514
import { useEmbeddedCheckoutContext } from '@stripe/react-stripe-js';
515
516
const CustomCheckoutComponent = () => {
517
const { embeddedCheckout } = useEmbeddedCheckoutContext();
518
const checkoutRef = useRef(null);
519
520
useEffect(() => {
521
if (embeddedCheckout && checkoutRef.current) {
522
// Manually mount the checkout
523
embeddedCheckout.mount(checkoutRef.current);
524
525
return () => {
526
// Cleanup on unmount
527
if (embeddedCheckout) {
528
try {
529
embeddedCheckout.unmount();
530
} catch (error) {
531
// Handle unmount errors silently
532
}
533
}
534
};
535
}
536
}, [embeddedCheckout]);
537
538
return (
539
<div>
540
<h2>Custom Checkout Implementation</h2>
541
<div ref={checkoutRef} className="custom-checkout-mount" />
542
</div>
543
);
544
};
545
```
546
547
## Error Handling
548
549
The embedded checkout automatically handles most errors, but you can provide custom error handling:
550
551
```typescript
552
const CheckoutWithErrorHandling = () => {
553
const [errors, setErrors] = useState([]);
554
555
const handleError = (error) => {
556
setErrors(prev => [...prev, {
557
id: Date.now(),
558
message: error.message,
559
timestamp: new Date().toISOString()
560
}]);
561
562
// Log to your error tracking service
563
console.error('Embedded checkout error:', error);
564
};
565
566
const clearErrors = () => setErrors([]);
567
568
return (
569
<div>
570
{errors.length > 0 && (
571
<div className="error-panel">
572
<h3>Checkout Errors</h3>
573
{errors.map(error => (
574
<div key={error.id} className="error-item">
575
{error.message}
576
<small>{error.timestamp}</small>
577
</div>
578
))}
579
<button onClick={clearErrors}>Clear Errors</button>
580
</div>
581
)}
582
583
<EmbeddedCheckoutProvider
584
stripe={stripePromise}
585
options={{
586
clientSecret,
587
onError: handleError
588
}}
589
>
590
<EmbeddedCheckout />
591
</EmbeddedCheckoutProvider>
592
</div>
593
);
594
};
595
```