Lesson 5 / 25
Payment Element and PaymentIntents
An embedded form confirmed in the browser.
Client secret and client-side confirmation
The Payment Element is a UI component from Stripe.js that renders inside your own page and shows the payment methods available for the customer. The server creates a PaymentIntent with the amount and currency and returns its client secret to the browser. The browser initialises Elements with that client secret, mounts the Payment Element and calls stripe.confirmPayment, which sends the details directly to Stripe, handles any 3D Secure challenge and redirects to your return_url. Never compute the amount in the browser: the server decides the price.
Server and browser halves
Node.js on the server, Stripe.js in the browser.
// server: create the PaymentIntent
app.post('/create-payment-intent', async (req, res) => {
const amount = await priceForCart(req.user.cartId); // server-side total
const intent = await stripe.paymentIntents.create({
amount,
currency: 'usd',
automatic_payment_methods: { enabled: true },
metadata: { cartId: req.user.cartId },
});
res.json({ clientSecret: intent.client_secret });
});
// browser: mount the Payment Element and confirm
const stripe = Stripe('pk_test_...');
const { clientSecret } = await (await fetch('/create-payment-intent', { method: 'POST' })).json();
const elements = stripe.elements({ clientSecret });
elements.create('payment').mount('#payment-element');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const { error } = await stripe.confirmPayment({
elements,
confirmParams: { return_url: 'https://example.com/order/complete' },
});
if (error) showMessage(error.message); // otherwise the browser is redirected
});
Treat the client secret carefully
The client secret lets the browser confirm that one PaymentIntent. Send it only to the customer who owns the payment, over HTTPS, and do not log or store it.
Quick check: What does the server send to the browser so it can confirm a PaymentIntent?
- The secret API key
- The PaymentIntent's client_secret
- The raw card number
- The webhook signing secret
Answer
The PaymentIntent's client_secret — The client secret is scoped to a single PaymentIntent.