The Payment Intents API Fresh
Use the Payment Intents API to build integrations that handle complex payment flows with a status that changes over the PaymentIntent's lifecycle. It tracks a payment from creation through checkout and triggers additional authentication steps when required.
Why use Payment Intents?
- Automatic authentication handling (3D Secure, etc.)
- No double charges
- No idempotency key issues
- Support for Strong Customer Authentication (SCA) and similar regulatory requirements
- Works with Setup Intents and Payment Methods APIs for dynamic payment flows
Creating a PaymentIntent
bash
curl https://api.stripe.com/v1/payment_intents \
-u "sk_test_YOUR_KEY:" \
-d amount=1099 \
-d currency=usdjavascript
const paymentIntent = await stripe.paymentIntents.create({
amount: 1099,
currency: 'usd',
// Optionally specify payment methods
payment_method_types: ['card'],
});The API returns a PaymentIntent object with a client_secret — a unique key used on the client to confirm and complete the payment.
Best practices
- Create early — Create a PaymentIntent as soon as you know the amount (e.g., when the customer begins checkout). This helps track the purchase funnel.
- Update if amount changes — Call
stripe.paymentIntents.update()if the cart total changes before confirmation. - Reuse interrupted sessions — Store the PaymentIntent ID in your session/cart. If checkout is interrupted, retrieve the existing PaymentIntent instead of creating a new one.
- Use idempotency keys — Pass
Idempotency-Keyheaders to prevent creating duplicate PaymentIntents for the same cart.
Passing the client secret to the client
The client_secret must be passed from your server to the client. Never expose your secret API key.
Single-page application (fetch)
javascript
// Server endpoint
app.get('/secret', async (req, res) => {
const intent = await stripe.paymentIntents.create({
amount: req.query.amount,
currency: 'usd',
});
res.json({ client_secret: intent.client_secret });
});javascript
// Client
const { client_secret } = await fetch('/secret').then(r => r.json());Server-rendered pages
Render the client secret into your HTML template:
html
<script>
const clientSecret = "{{ client_secret }}";
</script>PaymentIntent lifecycle
| Status | Meaning |
|---|---|
requires_payment_method | No payment method attached yet |
requires_confirmation | Payment method attached, waiting for confirmation |
requires_action | Customer must take action (e.g., 3D Secure authentication) |
processing | Stripe is processing the payment |
requires_capture | Auth-only — awaiting capture |
succeeded | Payment complete |
canceled | PaymentIntent was canceled |
Confirming a PaymentIntent
On the client side, use Stripe.js to confirm:
javascript
const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: cardElement,
billing_details: { name: 'John Doe' },
},
});
if (error) {
// Show error to customer
} else if (paymentIntent.status === 'succeeded') {
// Payment succeeded
}Retrieving a PaymentIntent
bash
curl https://api.stripe.com/v1/payment_intents/pi_xxx \
-u "sk_test_YOUR_KEY:"Updating a PaymentIntent
bash
curl https://api.stripe.com/v1/payment_intents/pi_xxx \
-u "sk_test_YOUR_KEY:" \
-X POST \
-d amount=2000Canceling a PaymentIntent
You can cancel a PaymentIntent before it's confirmed or while it has requires_action or requires_payment_method status:
bash
curl https://api.stripe.com/v1/payment_intents/pi_xxx/cancel \
-u "sk_test_YOUR_KEY:" \
-X POSTRelated APIs
- SetupIntent — Collect and save a payment method without charging it. Use for subscriptions or future off-session payments.
- PaymentMethod — Stores payment details attached to a customer or PaymentIntent.
- Charge — Created when a PaymentIntent is confirmed. Represents the actual attempt to move money.