or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

address-elements.mdbank-elements.mdcard-elements.mdcheckout-components.mdcore-providers-hooks.mdembedded-checkout.mdindex.mdmessage-elements.mdpayment-elements.md

embedded-checkout.mddocs/

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

```