> ## Documentation Index
> Fetch the complete documentation index at: https://storekit.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Delivery Fee Strategies

> Optimise delivery fees and service charges to maximise checkout conversion while still covering costs. Compare flat, tiered, distance, and free options.

export const DeliveryFeeCalculator = () => {
  const [deliveryCost, setDeliveryCost] = useState(5.00);
  const [serviceCharge, setServiceCharge] = useState(0.99);
  const [deliveryFee, setDeliveryFee] = useState(2.99);
  const [monthlyOrders, setMonthlyOrders] = useState(500);
  const [selectedStrategy, setSelectedStrategy] = useState(null);
  const strategies = [{
    id: 'subsidy',
    name: 'Service Charge Subsidy',
    description: 'Small service charge on all orders, lower delivery fee',
    serviceCharge: 0.99,
    deliveryFee: deliveryCost - 1.50,
    icon: '💰'
  }, {
    id: 'balanced',
    name: 'Balanced Approach',
    description: 'Even split between service charge and delivery fee',
    serviceCharge: deliveryCost * 0.25,
    deliveryFee: deliveryCost * 0.65,
    icon: '⚖️'
  }, {
    id: 'full-recovery',
    name: 'Full Cost Recovery',
    description: 'Pass full delivery cost to customer',
    serviceCharge: 0,
    deliveryFee: deliveryCost,
    icon: '📊'
  }, {
    id: 'aggressive',
    name: 'Conversion Focused',
    description: 'Minimise visible fees, maximise conversions',
    serviceCharge: 1.49,
    deliveryFee: Math.max(0, deliveryCost - 2.50),
    icon: '🚀'
  }];
  const calculateMetrics = (sc, df) => {
    const customerTotal = sc + df;
    const yourRevenue = customerTotal;
    const subsidy = Math.max(0, deliveryCost - yourRevenue);
    const profit = yourRevenue - deliveryCost;
    const monthlySubsidy = subsidy * monthlyOrders;
    const monthlyRevenue = yourRevenue * monthlyOrders;
    return {
      customerTotal: customerTotal.toFixed(2),
      subsidy: subsidy.toFixed(2),
      profit: profit.toFixed(2),
      monthlySubsidy: monthlySubsidy.toFixed(0),
      monthlyRevenue: monthlyRevenue.toFixed(0),
      coveragePercent: (yourRevenue / deliveryCost * 100).toFixed(0)
    };
  };
  const customMetrics = calculateMetrics(serviceCharge, deliveryFee);
  return <div className="not-prose space-y-6">
      {}
      <div className="p-4 rounded-xl border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800/50">
        <h3 className="text-lg font-semibold text-zinc-900 dark:text-white mb-4">Your Delivery Costs</h3>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
          <div>
            <label className="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">
              Average delivery cost (£)
            </label>
            <input type="number" step="0.50" min="0" value={deliveryCost} onChange={e => setDeliveryCost(parseFloat(e.target.value) || 0)} className="w-full px-3 py-2 rounded-lg border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-700 text-zinc-900 dark:text-white" />
          </div>
          <div>
            <label className="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">
              Monthly delivery orders
            </label>
            <input type="number" step="50" min="0" value={monthlyOrders} onChange={e => setMonthlyOrders(parseInt(e.target.value) || 0)} className="w-full px-3 py-2 rounded-lg border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-700 text-zinc-900 dark:text-white" />
          </div>
        </div>
      </div>

      {}
      <div>
        <h3 className="text-lg font-semibold text-zinc-900 dark:text-white mb-4">Compare Strategies</h3>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
          {strategies.map(strategy => {
    const metrics = calculateMetrics(strategy.serviceCharge, strategy.deliveryFee);
    const isSelected = selectedStrategy === strategy.id;
    return <div key={strategy.id} onClick={() => {
      setSelectedStrategy(strategy.id);
      setServiceCharge(strategy.serviceCharge);
      setDeliveryFee(strategy.deliveryFee);
    }} className={`p-4 rounded-xl border-2 cursor-pointer transition-all ${isSelected ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20' : 'border-zinc-200 dark:border-zinc-700 hover:border-zinc-300 dark:hover:border-zinc-600'}`}>
                <div className="flex items-start gap-3">
                  <span className="text-2xl">{strategy.icon}</span>
                  <div className="flex-1">
                    <h4 className="font-semibold text-zinc-900 dark:text-white">{strategy.name}</h4>
                    <p className="text-sm text-zinc-600 dark:text-zinc-400 mt-1">{strategy.description}</p>
                    
                    <div className="mt-3 grid grid-cols-2 gap-2 text-sm">
                      <div className="bg-zinc-100 dark:bg-zinc-700/50 rounded px-2 py-1">
                        <span className="text-zinc-500 dark:text-zinc-400">Service: </span>
                        <span className="font-medium text-zinc-900 dark:text-white">£{strategy.serviceCharge.toFixed(2)}</span>
                      </div>
                      <div className="bg-zinc-100 dark:bg-zinc-700/50 rounded px-2 py-1">
                        <span className="text-zinc-500 dark:text-zinc-400">Delivery: </span>
                        <span className="font-medium text-zinc-900 dark:text-white">£{strategy.deliveryFee.toFixed(2)}</span>
                      </div>
                    </div>
                    
                    <div className="mt-2 flex items-center justify-between text-sm">
                      <span className="text-zinc-600 dark:text-zinc-400">Customer pays:</span>
                      <span className="font-bold text-lg text-zinc-900 dark:text-white">£{metrics.customerTotal}</span>
                    </div>
                    
                    <div className="mt-1 flex items-center justify-between text-sm">
                      <span className="text-zinc-600 dark:text-zinc-400">Cost coverage:</span>
                      <span className={`font-medium ${parseFloat(metrics.coveragePercent) >= 100 ? 'text-green-600 dark:text-green-400' : parseFloat(metrics.coveragePercent) >= 80 ? 'text-yellow-600 dark:text-yellow-400' : 'text-red-600 dark:text-red-400'}`}>
                        {metrics.coveragePercent}%
                      </span>
                    </div>
                  </div>
                </div>
              </div>;
  })}
        </div>
      </div>

      {}
      <div className="p-4 rounded-xl border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-800">
        <h3 className="text-lg font-semibold text-zinc-900 dark:text-white mb-4">Custom Calculator</h3>
        
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
          <div>
            <label className="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">
              Service charge (£)
            </label>
            <input type="number" step="0.10" min="0" value={serviceCharge} onChange={e => {
    setServiceCharge(parseFloat(e.target.value) || 0);
    setSelectedStrategy(null);
  }} className="w-full px-3 py-2 rounded-lg border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-700 text-zinc-900 dark:text-white" />
          </div>
          <div>
            <label className="block text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1">
              Delivery fee (£)
            </label>
            <input type="number" step="0.10" min="0" value={deliveryFee} onChange={e => {
    setDeliveryFee(parseFloat(e.target.value) || 0);
    setSelectedStrategy(null);
  }} className="w-full px-3 py-2 rounded-lg border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-700 text-zinc-900 dark:text-white" />
          </div>
        </div>

        {}
        <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
          <div className="bg-zinc-50 dark:bg-zinc-700/50 rounded-lg p-3 text-center">
            <div className="text-xs text-zinc-500 dark:text-zinc-400 uppercase tracking-wide">Customer Pays</div>
            <div className="text-2xl font-bold text-zinc-900 dark:text-white mt-1">£{customMetrics.customerTotal}</div>
          </div>
          <div className="bg-zinc-50 dark:bg-zinc-700/50 rounded-lg p-3 text-center">
            <div className="text-xs text-zinc-500 dark:text-zinc-400 uppercase tracking-wide">Your Subsidy</div>
            <div className={`text-2xl font-bold mt-1 ${parseFloat(customMetrics.subsidy) > 0 ? 'text-red-600 dark:text-red-400' : 'text-green-600 dark:text-green-400'}`}>
              £{customMetrics.subsidy}
            </div>
          </div>
          <div className="bg-zinc-50 dark:bg-zinc-700/50 rounded-lg p-3 text-center">
            <div className="text-xs text-zinc-500 dark:text-zinc-400 uppercase tracking-wide">Cost Coverage</div>
            <div className={`text-2xl font-bold mt-1 ${parseFloat(customMetrics.coveragePercent) >= 100 ? 'text-green-600 dark:text-green-400' : parseFloat(customMetrics.coveragePercent) >= 80 ? 'text-yellow-600 dark:text-yellow-400' : 'text-red-600 dark:text-red-400'}`}>
              {customMetrics.coveragePercent}%
            </div>
          </div>
          <div className="bg-zinc-50 dark:bg-zinc-700/50 rounded-lg p-3 text-center">
            <div className="text-xs text-zinc-500 dark:text-zinc-400 uppercase tracking-wide">Monthly Subsidy</div>
            <div className={`text-2xl font-bold mt-1 ${parseFloat(customMetrics.monthlySubsidy) > 0 ? 'text-red-600 dark:text-red-400' : 'text-green-600 dark:text-green-400'}`}>
              £{customMetrics.monthlySubsidy}
            </div>
          </div>
        </div>
      </div>

      {}
      <div className="p-4 rounded-xl bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800">
        <div className="flex items-start gap-3">
          <span className="text-xl">💡</span>
          <div>
            <h4 className="font-semibold text-blue-900 dark:text-blue-100">Recommendation</h4>
            <p className="text-sm text-blue-800 dark:text-blue-200 mt-1">
              {parseFloat(customMetrics.coveragePercent) >= 90 ? "Great balance! You're covering most of your costs while keeping fees reasonable." : parseFloat(customMetrics.coveragePercent) >= 70 ? "Good for conversions, but you're subsidising each delivery. Ensure higher order volume makes up for it." : "Heavy subsidy - consider increasing fees or focus on pickup orders to improve margins."}
            </p>
          </div>
        </div>
      </div>
    </div>;
};

How you structure delivery fees and service charges significantly impacts both checkout conversion rates and your profit margins. This guide explores different strategies to find the right balance for your business.

## Understanding Your Fees

storekit provides two fee types you can configure:

| Fee Type           | Scope                                   | Where to Set                 |
| ------------------ | --------------------------------------- | ---------------------------- |
| **Service Charge** | Fixed amount on all orders              | Store Settings               |
| **Delivery Fee**   | Per delivery zone, variable by distance | Fulfillment > Delivery Zones |

Both appear as separate line items at checkout, so customers see exactly what they're paying.

## The Psychology of Delivery Fees

High delivery fees are a leading cause of cart abandonment. Research shows:

* Customers often expect "free" or subsidised delivery from online ordering
* A single large fee feels more painful than multiple smaller charges
* Customers accept small service charges more readily than high delivery fees

## Strategy 1: Service Charge Subsidy (Recommended)

**Best for:** Stores using third-party delivery (Uber Direct, Stuart, etc.) or own drivers

Add a small fixed service charge to all orders, then reduce the delivery fee customers see.

### How It Works

1. Calculate your average delivery cost (e.g., £4.50 per order)
2. Add a service charge of £0.99 to all orders
3. Set your delivery fee to £2.99 instead of £4.50

**Customer sees:**

* Service charge: £0.99
* Delivery: £2.99
* **Total fees: £3.98**

**You receive:** £3.98 toward your £4.50 cost (you subsidise £0.52 per order)

<Tip>
  A service charge of 99p feels insignificant to customers but adds up. On 1,000 orders/month, that's £990 toward delivery costs.
</Tip>

### Configuration

1. Go to **Settings > Store Settings**
2. Set **Service Charge** to your desired amount (e.g., 99)
3. Optionally set a **Service Charge Label** (e.g., "Order fee" or "Packaging")
4. Go to **Fulfillment > Delivery Zones**
5. Reduce each zone's delivery fee by the service charge amount

## Strategy 2: Tiered Delivery Fees

**Best for:** Stores with wide delivery radius where costs vary significantly

Set different delivery fees per zone based on actual cost, while using a service charge to keep the closest zone very affordable.

### Example Configuration

| Zone   | Distance  | Actual Cost | Service Charge | Delivery Fee | Customer Total |
| ------ | --------- | ----------- | -------------- | ------------ | -------------- |
| Zone 1 | 0-2 miles | £3.50       | £0.99          | £1.99        | £2.98          |
| Zone 2 | 2-4 miles | £5.00       | £0.99          | £3.49        | £4.48          |
| Zone 3 | 4-6 miles | £7.00       | £0.99          | £4.99        | £5.98          |

This approach:

* Keeps local delivery very competitive
* Passes higher costs to customers ordering from further away
* Maintains consistent service charge revenue

## Strategy 3: Free Delivery Threshold

**Best for:** Stores wanting to increase average order value

Combine a minimum order value with reduced or free delivery.

### Example Setup

* Service charge: £1.49 (all orders)
* Delivery fee: £2.99 (orders under £25)
* Free delivery: Orders over £25

Configure this using:

1. Set your service charge in Store Settings
2. Create a [discount code](/guides/discounts/discount-codes) with:
   * Type: Free delivery
   * Minimum order: £25
   * Auto-apply: Enabled

<Note>
  Even with "free delivery", you keep the service charge revenue to offset costs.
</Note>

## Strategy 4: Full Cost Recovery

**Best for:** Premium restaurants where customers expect to pay for quality service

Pass the full delivery cost to customers, but frame it transparently.

### Configuration

* Service charge: £0 (or small amount for packaging)
* Delivery fee: Full cost per zone

This works when:

* Your food commands premium pricing
* Customers value quality over price
* You want to maintain healthy margins

<Warning>
  This approach typically has lower conversion rates but higher profit per order. Test carefully.
</Warning>

## Strategy 5: Pickup Incentive

**Best for:** Stores with high foot traffic or wanting to reduce delivery volume

Make pickup significantly cheaper than delivery to encourage collection.

### Example

| Method   | Fees                         |
| -------- | ---------------------------- |
| Pickup   | £0                           |
| Delivery | £3.99 + £0.99 service charge |

This works well combined with:

* Pre-orders for pickup
* Loyalty rewards for pickup orders
* Prominent "Ready in X minutes" messaging

## Calculating Your Break-Even

To find the right fee structure, calculate your actual costs:

### Third-Party Delivery Costs

| Provider    | Typical Cost      | Notes              |
| ----------- | ----------------- | ------------------ |
| Uber Direct | £4-7 per delivery | Varies by distance |
| Stuart      | £4-6 per delivery | Zone-based         |
| Own drivers | £2-5 per delivery | Fuel + wage        |

### Your Break-Even Formula

```
Break-even = Delivery Cost - Service Charge - Delivery Fee
```

**Example:**

* Delivery cost: £5.00
* Service charge: £0.99
* Delivery fee: £2.99
* **Break-even: £1.02** (your subsidy per order)

<Tip>
  A small subsidy per order is often worthwhile if it increases conversion rates and total order volume.
</Tip>

## A/B Testing Your Fees

Before committing to a strategy, test different approaches:

1. **Week 1-2:** Current fees (baseline conversion rate)
2. **Week 3-4:** Lower delivery fee + service charge
3. **Week 5-6:** Different service charge amount

Track:

* Checkout conversion rate
* Average order value
* Total revenue
* Profit per order

## Common Mistakes to Avoid

| Mistake                  | Why It Hurts                   | Better Approach                                |
| ------------------------ | ------------------------------ | ---------------------------------------------- |
| Very high delivery fee   | Cart abandonment               | Split into service charge + lower delivery fee |
| £0 service charge        | Leaves money on the table      | Even £0.49 adds up                             |
| Same fee for all zones   | Losing money on far deliveries | Tiered delivery zones                          |
| Rounding to whole pounds | Feels more expensive           | Use £X.99 pricing                              |

## Recommended Starting Point

For most stores, we recommend starting with:

| Setting                  | Value       |
| ------------------------ | ----------- |
| Service charge           | £0.99       |
| Service charge label     | "Order fee" |
| Zone 1 delivery (0-2 mi) | £1.99-2.49  |
| Zone 2 delivery (2-4 mi) | £2.99-3.49  |
| Minimum order            | £10-15      |

This balances conversion with cost recovery while keeping flexibility to adjust based on your results.

## Compare Strategies

Use this calculator to compare different fee structures and see how they impact your costs and what customers pay.

<DeliveryFeeCalculator />

## Next Steps

* [Set up delivery zones](/guides/fulfillment/delivery/delivery-zones) with your chosen fees
* [Create a free delivery promotion](/guides/discounts/promotions) for high-value orders
* [Configure minimum order values](/guides/fulfillment/delivery/delivery-zones) per zone
