Skip to content
Create account
or
Sign in
The Stripe Docs logo
/
Ask AI
Create account
Sign in
Get started
Payments
Revenue
Platforms and marketplaces
Money management
Developer resources
Overview
About Stripe payments
Upgrade your integration
Payments analytics
Online payments
OverviewFind your use caseManaged Payments
Use Payment Links
Build a checkout page
    Overview
    Quickstarts
    Customise look and feel
    Collect additional information
    Collect taxes
    Dynamically update checkout
      Dynamically customise shipping options
      Dynamically update line items
      Dynamically update trial durations
      Dynamically update discounts
    Manage your product catalogue
    Subscriptions
    Manage payment methods
    Let customers pay in their local currency
    Add discounts, upsells, and optional items
    Set up future payments
    Save payment details during payment
    Manually approve payments on your server
    After the payment
    Elements with Checkout Sessions API beta changelog
    Migrate from legacy Checkout
    Migrate Checkout to use Prices
Build an advanced integration
Build an in-app integration
Payment Methods
Add payment methods
Manage payment methods
Faster checkout with Link
Payment interfaces
Payment Links
Checkout
Web Elements
In-app Elements
Payment scenarios
Handle multiple currencies
Custom payment flows
Flexible acquiring
Orchestration
In-person payments
Terminal
Beyond payments
Incorporate your company
Crypto
Financial Connections
Climate
Understand fraud
Radar fraud protection
Manage disputes
Verify identities
HomePaymentsBuild a checkout pageDynamically update checkout

Dynamically customise shipping options

Update shipping options based on a customer's shipping address.

Learn how to dynamically update shipping options based on the address that your customer enters in Checkout.

Use cases

  • Validate an address: Confirm whether you can ship a product to a customer’s address using your own custom validation rules. You can also create a custom UI for customers to confirm their preferred address.
  • Show relevant shipping options: Display only available shipping methods, based on the customer’s address. For example, show overnight shipping only for deliveries in your country.
  • Dynamically calculate shipping rates: Calculate and display shipping fees based on a customer’s delivery address.
  • Update shipping rates based on order total: Offer shipping rates based on the shipping address or order total, such as free shipping for orders over 100 USD. For checkouts allowing quantity changes or cross-sells, see Dynamically updating line items.

Limitations

  • Only supported in payment mode. Shipping rates aren’t available in subscription mode.

Configure Checkout Session update permissions
Server-side

Set the shipping_address_collection.allowed_countries to the list of countries you want to offer shipping to.

Pass in permissions.update_shipping_details=server_only when you create the Checkout Session to enable update shipping address and shipping options from your server. Pass this option will also disable the client method updateShippingAddress, ensuring that all updates pass through your server.

Command Line
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node
Go
.NET
No results
curl https://api.stripe.com/v1/checkout/sessions \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"
\ -d ui_mode=custom \ -d "permissions[update_shipping_details]"=server_only \ -d "shipping_address_collection[allowed_countries][0]"=US \ -d "line_items[0][price]"=
{{PRICE_ID}}
\ -d "line_items[0][quantity]"=1 \ -d mode=payment \ --data-urlencode return_url="https://example.com/return"

Customise shipping options
Server-side

From your server, create a new endpoint to calculate the shipping options based on the customer’s shipping address.

  1. Retrieve the Checkout Session using the checkoutSessionId from the request body.
  2. Validate the customer’s shipping details from the request body.
  3. Calculate the shipping options based on the customer’s shipping address and the Checkout Session’s line items.
  4. Update the Checkout Session with the customer’s shipping_details and the shipping_options.
Ruby
Python
PHP
Node
.NET
Go
Java
No results
require 'sinatra' require 'json' require 'stripe' set :port, 4242 # Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/apikeys Stripe.api_key =
"sk_test_BQokikJOvBiI2HlWgH4olfQ2"
# Return a boolean indicating whether the shipping details are valid def validate_shipping_details(shipping_details) # TODO: Remove error and implement... raise NotImplementedError.new(<<~MSG) Validate the shipping details the customer has entered. MSG end # Return an array of the updated shipping options or the original options if no update is needed def calculate_shipping_options(shipping_details, session) # TODO: Remove error and implement... raise NotImplementedError.new(<<~MSG) Calculate shipping options based on the customer's shipping details and the Checkout Session's line items. MSG end post '/calculate-shipping-options' do content_type :json request.body.rewind request_data = JSON.parse(request.body.read) checkout_session_id = request_data['checkout_session_id'] shipping_details = request_data['shipping_details'] # 1. Retrieve the Checkout Session session = Stripe::Checkout::Session.retrieve(checkout_session_id) # 2. Validate the shipping details if !validate_shipping_details(shipping_details) return { type: 'error', message: "We can't ship to your address. Please choose a different address." }.to_json end # 3. Calculate the shipping options shipping_options = calculate_shipping_options(shipping_details, session) # 4. Update the Checkout Session with the customer's shipping details and shipping options if shipping_options Stripe::Checkout::Session.update(checkout_session_id, { collected_information: { shipping_details: shipping_details }, shipping_options: shipping_options }) return { type: 'object', value: { succeeded: true } }.to_json else return { type: 'error', message: "We can't find shipping options. Please try again." }.to_json end end

Update client SDK
Client-side

Initialise stripe.js with the custom_checkout_server_updates_1 beta header.

checkout.js
const stripe = Stripe(
'pk_test_TYooMQauvdEDq54NiTphI7jx'
, { betas: ['custom_checkout_server_updates_1'], });

Invoke server updates
Client-side

Create an asynchronous function that makes a request to your server to update the shipping options and wrap it in runServerUpdate. If the request is successful, the Session object updates with the new shipping options.

The following code example shows how to update the shipping options with the AddressElement.

index.html
<div id="shipping-form"> <!-- Shipping Address Element will be mounted here --> <div id="shipping-address-element"></div> <button id="save-button">Save</button> <div id="error-message"></div> </div> <div id="shipping-display" style="display: none"> <div id="address-display"></div> <button id="edit-button">Edit</button> </div>
checkout.js
// mount the Shipping Address Element const shippingAddressElement = checkout.createShippingAddressElement(); shippingAddressElement.mount('#shipping-address-element'); const toggleViews = (isEditing) => { document.getElementById('shipping-form').style.display = isEditing ? 'block' : 'none'; document.getElementById('shipping-display').style.display = isEditing ? 'none' : 'block'; } const displayAddress = (address) => { const displayDiv = document.getElementById('address-display'); displayDiv.innerHTML = ` <div>${address.name}</div> <div>${address.address.line1}</div> <div>${address.address.city}, ${address.address.state} ${address.address.postal_code}</div> `; } const updateShippingOptions = async (shippingDetails) => { const response = await fetch("/calculate-shipping-options", { method: "POST", headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ checkout_session_id: 'session_id', shipping_details: shippingDetails }) }); const result = await response.json(); if (result.type === 'error') { document.getElementById('error-message').textContent = result.message; toggleViews(true); } else { document.getElementById('error-message').textContent = ''; toggleViews(false); displayAddress(checkout.session().shippingAddress); } return result; } const handleSave = async () => { const addressElement = await checkout.getShippingAddressElement(); if (!addressElement) { return; } const result = await addressElement.getValue(); if (!result.complete) { return; } try { await checkout.runServerUpdate(() => updateShippingOptions(result.value)); } catch (error) { document.getElementById('error-message').textContent = error?.message; toggleViews(true); } } // Event Listeners document.getElementById('save-button').addEventListener('click', handleSave); document.getElementById('edit-button').addEventListener('click', () => toggleViews(true));

Test the integration

Follow these steps to test your integration, and ensure your custom shipping options work correctly.

  1. Set up a sandbox environment that mirrors your production setup. Use your Stripe sandbox API keys for this environment.

  2. Simulate various shipping addresses to verify that your calculateShippingOptions function handles different scenarios correctly.

  3. Verify server-side logic by using logging or debugging tools to confirm that your server:

    • Retrieves the Checkout Session.
    • Validates shipping details.
    • Calculates shipping options.
    • Updates the Checkout Session with new shipping details and options. Make sure the update response contains the new shipping details and options.
  4. Verify client-side logic by completing the checkout process multiple times in your browser. Pay attention to how the UI updates after entering shipping details. Make sure that:

    • The runServerUpdate function is called when expected.
    • Shipping options update correctly based on the provided address.
    • Error messages display properly when shipping is unavailable.
  5. Enter invalid shipping addresses or simulate server errors to test error handling, both server-side and client-side.

Was this page helpful?
YesNo
  • Need help? Contact Support.
  • Join our early access programme.
  • Check out our changelog.
  • Questions? Contact Sales.
  • LLM? Read llms.txt.
  • Powered by Markdoc