# Make line item quantities adjustable

Learn how to allow your customers to adjust the quantity of items during checkout.

The line items for each [Checkout Session](https://docs.stripe.com/api/checkout/sessions.md) keep track of what your customer is purchasing. You can configure the Checkout Session so customers can adjust line item quantities during checkout.

> #### Payment Intents API
> 
> If you use the Payment Intents API, you must manually track line item updates and modify the payment amount, or by creating a new PaymentIntent with adjusted amounts.

## Enable adjustable quantities [Server-side]

> Other line item updates, such as adding new line items, aren’t supported for this integration.

Set [adjustable_quantity](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-line_items-adjustable_quantity) on your [line_items](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-line_items) when creating a Checkout Session to allow your customers to update the quantity of an item during checkout.

```curl
curl https://api.stripe.com/v1/checkout/sessions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "line_items[0][price_data][currency]=usd" \
  -d "line_items[0][price_data][product_data][name]=T-shirt" \
  -d "line_items[0][price_data][unit_amount]=2000" \
  -d "line_items[0][quantity]=1" \
  -d "line_items[0][adjustable_quantity][enabled]=true" \
  -d "line_items[0][adjustable_quantity][maximum]=100" \
  -d "line_items[0][adjustable_quantity][minimum]=0" \
  -d mode=payment \
  -d ui_mode=elements \
  -d return_url={{RETURN_URL}}
```

You can customize the default settings for the minimum and maximum quantities allowed by setting [adjustable_quantity.minimum](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-line_items-adjustable_quantity-minimum) and [adjustable_quantity.maximum](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-line_items-adjustable_quantity-maximum). By default, an item’s minimum adjustable quantity is 0 and the maximum adjustable quantity is 99. You can specify a value of up to 999999 for `adjustable_quantity.maximum`.

Checkout prevents the customer from removing an item if it’s the only item remaining.

## Update line item quantities [Client-side]

Use [updateLineItemQuantity](https://docs.stripe.com/js/custom_checkout/update_line_item_quantity) to change a line item’s quantity in response to customer interaction, such as a button to increment the quantity. Pass the [line item ID](https://docs.stripe.com/js/custom_checkout/session_object#custom_checkout_session_object-lineItems-id) and the new quantity:

#### HTML + JS

```html
<button class="increment-quantity-button" data-line-item="{{line item ID}}">+</button>
```

```js
const checkout = stripe.initCheckoutElementsSdk({clientSecret});
const button = document.querySelector('.increment-quantity-button');
const lineItem = button.getAttribute("data-line-item");
checkout.loadActions().then((loadActionsResult) => {
  if (loadActionsResult.type === 'success') {
    const {actions} = loadActionsResult;
    const session = loadActionsResult.getSession();
    const quantity = session.lineItems.find((li) => li.id === lineItem).quantity;
    button.addEventListener('click', () => {actions.updateLineItemQuantity({
        lineItem,
        quantity: quantity + 1,
      })
    });
  } else {
    const {error} = loadActionsResult;
  }
});
```

#### React

```jsx
import React from 'react';
import {useCheckout} from '@stripe/react-stripe-js/checkout';

const IncrementLineItemButton = (props) => {
  const checkoutState = useCheckout();
  if (checkoutState.type === 'loading') {
    return (
      <div>Loading...</div>
    );
  } else if (checkoutState.type === 'error') {
    return (
      <div>Error: {checkoutState.error.message}</div>
    );
  }
  const {updateLineItemQuantity} = checkoutState.checkout;

  const handleClick = () => {updateLineItemQuantity({
      lineItem: props.lineItem,
      quantity: props.quantity + 1,
    });
  };
  return <button onClick={handleClick}>+</button>;
};

export default IncrementLineItemButton;
```

## Handle completed transactions [Server-side]

After the payment completes, you can make a request for the finalized [line items](https://docs.stripe.com/api/checkout/sessions/line_items.md) and their quantities. If your customer removes a line item, it’s also removed from the line items response. See the [Fulfillment guide](https://docs.stripe.com/checkout/fulfillment.md) to learn how to create an event handler to handle completed Checkout Sessions.

> To test your event handler, [install the Stripe CLI](https://docs.stripe.com/stripe-cli.md) and use `stripe listen --forward-to localhost:4242/webhook` to [forward events to your local server](https://docs.stripe.com/webhooks.md#test-webhook).

#### Ruby

```ruby
# Set your secret key. Remember to switch to your live secret key in production!
# See your keys here: https://dashboard.stripe.com/apikeys
require 'stripe'
# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
client = Stripe::StripeClient.new('<<YOUR_SECRET_KEY>>')

require 'sinatra'

# You can find your endpoint's secret in your webhook settings
endpoint_secret = 'whsec_...'

post '/webhook' do
  event = nil

  # Verify webhook signature and extract the event
  # See https://stripe.com/docs/webhooks#verify-events for more information.
  begin
    sig_header = request.env['HTTP_STRIPE_SIGNATURE']
    payload = request.body.read
    event = Stripe::Webhook.construct_event(payload, sig_header, endpoint_secret)
  rescue JSON::ParserError => e
    # Invalid payload
    return status 400
  rescue Stripe::SignatureVerificationError => e
    # Invalid signature
    return status 400
  end

  if event['type'] == 'checkout.session.completed'
    checkout_session = event['data']['object']

    line_items = client.v1.checkout.sessions.line_items.list(checkout_session['id'], {limit: 100})

    # Fulfill the purchase...
    begin
      fulfill_order(checkout_session, line_items)
    rescue NotImplementedError => e
      return status 400
    end
  end

  status 200
end

def fulfill_order(checkout_session, line_items)
  # TODO: Remove error and implement...
  raise NotImplementedError.new(<<~MSG)
    Given the Checkout Session "#{checkout_session.id}" load your internal order from the database here.
    Then you can reconcile your order's quantities with the final line item quantity purchased. You can use `checkout_session.metadata` and `price.metadata` to store and later reference your internal order and item ids.
  MSG
end
```
