In the world of Stripe, a PaymentIntent represents the intention to collect a payment from a customer. When using Stripe Elements, you'll typically create a PaymentIntent on your server, and then use its client_secret to securely collect payment details from your customer on the client-side using Elements.
The process of confirming a payment involves several steps: initializing the Stripe client, creating a PaymentIntent on the server, retrieving its client_secret, and finally using that client_secret with your Elements to confirm the payment.
Here's a breakdown of the typical workflow:
flowchart TD
A[Client: User initiates checkout] --> B(Client: Create PaymentIntent request)
B --> C(Server: Create PaymentIntent via Stripe API)
C --> D(Server: Return PaymentIntent client_secret to client)
D --> E(Client: Mount Stripe Elements)
E --> F(Client: Collect payment details via Elements)
F --> G(Client: Call stripe.confirmCardPayment with client_secret)
G --> H{Stripe API: Process payment}
H --> I(Client: Handle payment result - success or failure)
On your Next.js server (e.g., in an API route), you'll create the PaymentIntent. Ensure you're using the stripe library and have your secret key configured.
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
export default async function handler(req, res) {
if (req.method === 'POST') {
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: 1000, // amount in cents
currency: 'usd',
automatic_payment_methods: {
enabled: true,
},
});
res.status(200).json({ clientSecret: paymentIntent.client_secret });
} catch (error) {
res.status(500).json({ error: error.message });
}
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}On the client-side, you'll fetch this clientSecret and then use it with your Stripe Elements instance to confirm the payment. First, ensure you've initialized the Stripe object with your public key.
import { Elements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';
const stripePromise = loadStripe('YOUR_STRIPE_PUBLIC_KEY');
function CheckoutForm() {
const handleSubmit = async (event) => {
event.preventDefault();
const { clientSecret } = await fetch('/api/create-payment-intent').then(r => r.json());
const stripe = await stripePromise;
const result = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: Elements.getElement('card'),
billing_details: {
name: 'Jenny Rosen',
},
},
});
if (result.error) {
console.error(result.error.message);
} else {
if (result.paymentIntent.status === 'succeeded') {
console.log('Payment succeeded!');
}
}
};
return (
<form onSubmit={handleSubmit}>
<CardElement />
<button type="submit" disabled={!stripe}>Pay</button>
</form>
);
}
export default function CheckoutPage() {
return (
<Elements stripe={stripePromise}>
<CheckoutForm />
</Elements>
);
}The stripe.confirmCardPayment function is the key here. It takes the clientSecret obtained from your server and a payment_method object. The payment_method object contains details about how the customer is paying, typically by referencing the mounted Stripe Element (e.g., CardElement).
The result object returned by confirmCardPayment contains valuable information. You'll check result.error for any issues, and if successful, examine result.paymentIntent.status to confirm the payment's final state (e.g., 'succeeded', 'requires_capture').