Stripe | Financial Infrastructure to Grow Your Revenue

Stripe | Financial Infrastructure to Grow Your Revenue

4466 articles

Dynamically update payment amounts


Dynamically update payment amounts

Learn how to modify total amounts when customers change their selections during checkout.

Update the amount of a Checkout Session or Payment Intent when customers change what they’re buying or how much they pay. Recalculate the totals on your server, and then update the amount of the PaymentIntent.

Common use cases

  • Add or remove add-ons (such as gift wrap or a warranty).
  • Select a different shipping method or delivery speed.
  • Add additional services or charges.
  • Apply or remove a discount code or pre-tax store credit.

Security best practices

  • Recalculate amounts on your server. Don’t trust client-provided prices or totals.
  • Authorize the update based on your business rules (for example, enforce max quantities).
  • Only update Sessions that are active and not completed or expired.

Constraints and behavior

  • You can update the amount while the Payment Intent or Checkout Session is awaiting payment (for example, requires _ payment _ method or requires _ confirmation ).
  • After confirmation, you generally can’t increase the amount.

Update the client SDK Client-side

When using Elements with the Checkout Sessions API, wrap client calls to your server in runServerUpdate so the checkout state and totals refresh.

Wrap runServerUpdate calls in try / catch blocks to handle errors from your server and from runServerUpdate itself (for example, timeouts). response.type === 'error' covers failures in Stripe’s internal retrieval of the updated session. Errors returned by your own server (such as 4xx or 5xx responses) aren’t reflected in response because the Fetch API resolves for any completed HTTP response regardless of status code. Check response.ok inside your update function and throw on failure so that the catch block is reached.

checkout.js

import {loadStripe} from '@stripe/stripe-js';

const stripe = await loadStripe('pk_test_TYooMQauvdEDq54NiTphI7jx');

const checkout = stripe.initCheckoutElementsSdk({
 clientSecret,
 elementsOptions: {/* ... */},
});

// Example: Add additional service using price_data
const loadActionsResult = await checkout.loadActions();
if (loadActionsResult.type === 'success') {
 const actions = loadActionsResult.actions;
 const session = actions.getSession();
 document
 .getElementById('add-service')
 .addEventListener('click', async () => {
 const updateOnServer = async () => {
 const response = await fetch('/update-custom-amount', {
 method: 'POST',
 headers: {'Content-Type': 'application/json'},
 body: JSON.stringify({
 checkout_session_id: session.id,
 product_id: 'gift_wrap', // Server looks up actual price
 }),
 });
 if (!response.ok) {
 const body = await response.json();
 throw new Error(body.message);
 }
 };

 try {
 const response = await actions.runServerUpdate(updateOnServer);
 if (response.type === 'error') {
 // Handle Stripe API errors (for example, session retrieval failure)
 }
 } catch (error) {
 // Handle promise rejection from your server (4xx/5xx errors) or
 // from runServerUpdate itself (for example, timeouts).
 // error.message contains the message thrown from your update function.
 }
 });
}

Create server endpoints Server-side

Calculate amounts and validate inputs on your server. Then, you can update line_items with price_data to add ad-hoc charges.

Note

Updating the line_items or price_data recalculates the Session total and taxes.

Select a language

Node

Python

No results

import express from 'express';
import Stripe from 'stripe';

const app = express();
app.use(express.json());

// Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
const stripe = new Stripe('sk_test_BQokikJOvBiI2HlWgH4olfQ2');


// Product catalog with prices - store this securely server-side
const PRODUCTS = {
 gift_wrap: { name: 'Gift Wrap', price: 500 }, // $5.00
 express_shipping: { name: 'Express Shipping', price: 1500 }, // $15.00
 warranty: { name: 'Extended Warranty', price: 2000 }, // $20.00
};

app.post('/update-custom-amount', async (req, res) => {
 try {
 const {checkout_session_id, product_id} = req.body;

 const session = await stripe.checkout.sessions.retrieve(checkout_session_id);
 if (session.status === 'complete' || session.expires_at * 1000 < Date.now()) {
 return res.status(400).json({error: 'Session is no longer updatable.'});
 }

 // Look up product price server-side
 const product = PRODUCTS[product_id];
 if (!product) {
 return res.status(400).json({error: 'Invalid product ID'});
 }

 // Add the additional product via price_data
 const updated = await stripe.checkout.sessions.update(checkout_session_id, {
 line_items: [
 {
 price_data: {
 currency: 'usd',
 product_data: {name: product.name},
 unit_amount: product.price,
 },
 quantity: 1,
 },
 ],
 });

 return res.json({id: updated.id, amount_total: updated.amount_total});
 } catch (err) {
 return res.status(400).json({error: err.message});
 }
});

app.listen(4242, () => console.log('Server running on port 4242'));
Last verified 2026-09-24

Is this helpful?