Embedded Checkout Fresh
Embed Stripe Checkout directly in your website so customers never leave your domain. The embedded checkout renders in an iframe using the Checkout Sessions API with ui_mode: 'embedded'.
How it works
- Your server creates a Checkout Session with
ui_mode: 'embedded'and returns theclient_secret - Your client initializes the embedded checkout with the
client_secret - Stripe renders the checkout UI in your page
- On completion, Stripe redirects to your
return_url
Server: create the session
javascript
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
app.post('/create-checkout-session', async (req, res) => {
const session = await stripe.checkout.sessions.create({
ui_mode: 'embedded',
line_items: [
{
price_data: {
currency: 'usd',
product_data: { name: 'Pro Subscription' },
unit_amount: 2000,
},
quantity: 1,
},
],
mode: 'payment',
return_url: `${req.headers.origin}/return?session_id={CHECKOUT_SESSION_ID}`,
});
res.json({ clientSecret: session.client_secret });
});Client: mount the checkout
html
<!-- Include Stripe.js -->
<script src="https://js.stripe.com/v3/"></script>
<div id="checkout"></div>javascript
const stripe = Stripe('pk_test_YOUR_PUBLISHABLE_KEY');
// Fetch the client secret from your server
const { clientSecret } = await fetch('/create-checkout-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
}).then(r => r.json());
// Initialize and mount the embedded checkout
const checkout = await stripe.initEmbeddedCheckout({ clientSecret });
checkout.mount('#checkout');React implementation
jsx
import { useState, useEffect } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js';
const stripePromise = loadStripe('pk_test_YOUR_KEY');
function CheckoutPage() {
const [clientSecret, setClientSecret] = useState('');
useEffect(() => {
fetch('/create-checkout-session', { method: 'POST' })
.then(r => r.json())
.then(data => setClientSecret(data.clientSecret));
}, []);
return (
<EmbeddedCheckoutProvider stripe={stripePromise} options={{ clientSecret }}>
<EmbeddedCheckout />
</EmbeddedCheckoutProvider>
);
}Handle the return
After checkout completes, retrieve the session to verify payment:
javascript
app.get('/return', async (req, res) => {
const session = await stripe.checkout.sessions.retrieve(req.query.session_id);
if (session.status === 'complete') {
// Payment succeeded — show success state
res.send('Payment successful!');
} else {
// Payment not complete
res.send('Payment incomplete');
}
});Customization
Use the Appearance API to match your brand (70+ configurable settings):
javascript
const checkout = await stripe.initEmbeddedCheckout({
clientSecret,
// Appearance is set at session creation time in the Dashboard
// or via the Appearance API when using Elements directly
});Configure appearance in Dashboard → Settings → Branding, or pass appearance settings when creating elements-based integrations.
Key differences from hosted checkout
| Feature | Hosted | Embedded |
|---|---|---|
| Customer domain | stripe.com | Your domain |
| Customization | 15 settings | 70+ settings |
| Order summary | Full | Limited |
| redirect on completion | Required | Not needed (return_url) |
| 3DS handling | Automatic | Automatic |
Testing
Use 4242 4242 4242 4242 with any future expiry. See test cards for decline and 3DS scenarios.