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.