# Stripe PaymentIntent in a React SPA

In a Vite or Create React App SPA there is no server component: your backend (any framework) exposes an endpoint that creates the PaymentIntent and returns the client secret. The SPA renders Elements and confirms the card.

## Backend endpoint (any framework)

Create the intent with the secret key and return only the client secret. Example:

```
const intent = await stripe.paymentIntents.create({
  amount: 2000,
  currency: 'usd',
  automatic_payment_methods: { enabled: true },
});
// respond with JSON carrying the client secret
```

## Frontend: Elements plus confirmCardPayment

```
import { loadStripe } from '@stripe/stripe-js';
import { Elements, CardElement, useStripe, useElements } from '@stripe/react-stripe-js';

const stripePromise = loadStripe('YOUR_PUBLISHABLE_KEY_VALUE');

function CheckoutForm({ clientSecret }) {
  const stripe = useStripe();
  const elements = useElements();

  const pay = async (e) => {
    e.preventDefault();
    const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
      payment_method: { card: elements.getElement(CardElement) },
    });
    if (error) {
      console.log(error.message);
    } else if (paymentIntent.status === 'succeeded') {
      console.log('paid');
    }
  };

  return (
    [form onSubmit={pay}]
      [CardElement /]
      [button disabled={!stripe}]Pay[/button]
    [/form]
  );
}

export default function App({ clientSecret }) {
  return (
    [Elements stripe={stripePromise}]
      [CheckoutForm clientSecret={clientSecret} /]
    [/Elements]
  );
}
```

## Notes

- Fetch the client secret from your backend when the checkout page loads, then render Elements.
- `confirmCardPayment` handles 3D Secure automatically; check `paymentIntent.status` after it resolves.
- The publishable key is public by design. The secret key never appears in frontend code.