Tiered Pricing
Updated June 07, 2016
Tiered Pricing is a means of providing products or services at different price points. In this type of pricing model, the product rates are bundled into separate pricing tiers. Once a tier is filled up, the product will move to the next tier and a different price will be charged.

If you're looking to set up Volume Pricing, please refer to our guide here.
Use Case 1: Subscription created with 10 units
When a Subscription is created with a quantity of 10 units, all units will be charged $15, as the number of units would fall in the Tier 1 range.

Use Case 2: Subscriptions created with 50 units
When a Subscription is created with a quantity of 50 units, the price would be calculated across all four tiers.


Here's a quick guide on how to set up Tiered Pricing using API and JSON payload.
Step 1
Create a $0 Base Plan (e.g. Plan name Cloud Server) with Flat Fee pricing in your Chargebee site. The only purpose of this Plan would be to define the billing frequency of the product (monthly, quarterly, etc.).
Step 2
Create a Recurring Addon (quantity based) for every tier as shown below. Ensure that the Addon frequency matches the frequency of the Plan.
Step 3
Add the JSON Meta data to the $0 Base Plan. Chargebee has the Meta data feature that can be used to store additional data at different levels (Plans, Subscriptions, Customers etc). Add JSON Meta data to the $0 Base Plan, to establish a relationship between "the unit range and the Addons.
                                
{"tiered_pricing": [
   {
       "addon_id": "cloud_server_01_20",
       "start_range": 1,
       "end_range": 20 
   },
   {
       "addon_id": "cloud_server_21_25",
       "start_range": 21,
       "end_range": 25
   },
   {
       "addon_id": "cloud_server_26_30",
       "start_range": 26,
       "end_range": 30
   },
   {
       "addon_id": "cloud_server_31",
       "start_range": 31
   }
]}
                                
                            function tieredPricingUpdatePlanMetaData($planId) {
  $tieredPricing = array( "tiered_pricing" => array(
     array("addon_id" => $planId . "_01_20", 
           "start_range" => 1, 
           "end_range" => 20),
     array("addon_id" => $planId . "_21_25", 
           "start_range" => 21, 
           "end_range" => 25),
     array("addon_id" => $planId . "_26_30", 
           "start_range" => 26, "end_range" => 30),
     array("addon_id" => $planId . "_31", 
            "start_range" => 31)
     )
  );
  $planResult = ChargeBee_Plan::update($planId, 
           array("metaData" => json_encode($tieredPricing)));
}
def tiered_pricing_update_plan_meta_data(plan_id)
 tiered_pricing = {:tiered_pricing => [
    {  :addon_id => "#{plan_id}_01_20",
       :start_range => 1,
       :end_range => 20
    },
    {  :addon_id => "#{plan_id}_21_25",
       :start_range => 21,
       :end_range => 25
    },
    {  :addon_id => "#{plan_id}_26_30",
       :start_range => 26,
       :end_range => 30
    },
    {  :addon_id => "#{plan_id}_31",
       :start_range => 31
    }
  ]}
  result = ChargeBee::Plan.update(plan_id, {
    :meta_data => tiered_pricing.to_json
  })
end
def tiered_pricing_update_plan_meta_data(plan_id):
    tiered_pricing = {
       "tiered_pricing" : [
            { "addon_id" :  "cloud_server_01_20",
              "start_range" : 1,
              "end_range" : 20
            },
            { "addon_id" :  "cloud_server_21_25",
              "start_range" : 21,
              "end_range" : 25
            },
            { "addon_id" : "cloud_server_26_30",
              "start_range" : 26,
              "end_range" : 30
            },
            { "addon_id" : "cloud_server_31",
              "start_range" : 31
            }
    ]}
    plan_result = chargebee.Plan.update(plan_id, 
         {"meta_data": json.dumps(tiered_pricing)})
function tieredPricingUpdatePlanMetaData(planId) {
var tiered_pricing = { "tiered_pricing": [
    { "addon_id" : planId + "_01_20",
      "start_range": 1,
      "end_range": 20
    },
    { "addon_id" : planId + "_21_25",
      "start_range": 21,
      "end_range": 25
    },
    { "addon_id" : planId + "_26_30",
      "start_range": 26,
      "end_range": 30
    },
    { "addon_id" : planId + "_31",
      "start_range": 31,
    }]
};
chargebee.plan.update(planId, {  meta_data: tiered_pricing })
    .request(function(error, result) {
      if(error){
        console.log(error);
      }else{
        console.log(result);
      }
});
}
public void tieredPricingUpdatePlanMetaData(String planId) 
       throws JSONException, IOException {
    JSONObject metaData = new JSONObject();
    JSONArray arr = new JSONArray();
    arr.put(addonObj(planId + "_01_20", 1, 20));
    arr.put(addonObj(planId + "_21_25", 21, 25));
    arr.put(addonObj(planId + "_26_30", 26, 30));
    arr.put(addonObj(planId + "_31", 31, 0));
    metaData.put("tiered_pricing", arr);
    
    Result planResult = Plan.update(planId)
            .metaData(metaData).request();
}
    
public JSONObject addonObj(String addonId, int startRange, 
        int endRange) throws JSONException {
    return new JSONObject().put("addon_id", addonId)
            .put("start_range", startRange)
            .put("end_range", endRange);
}
public void tieredPricingUpdatePlanMetaData(String planId){
    JObject metaData = new JObject();
    JArray arr = new JArray();
    arr.Add(addonObj(planId + "_01_20", 1, 20));
    arr.Add(addonObj(planId + "_21_25", 21, 25));
    arr.Add(addonObj(planId + "_26_30", 26, 30));
    arr.Add(addonObj(planId + "_31", 31, 0));
    metaData.Add(new JProperty("tiered_pricing", arr));
    EntityResult planResult = Plan.Update(planId)
        .MetaData(metaData).Request();
}
public JObject addonObj(String addonId, int startRange, 
            int endRange) {
    return new JObject (new JProperty("addon_id",addonId),
        new JProperty("start_range",startRange),
        new JProperty("end_range",endRange));
}
Step 4
Create a Subscription with the Tiered Plan and Addons
Based on the Plan's metadata, pick the appropriate Addon(s) and create the Subscription with the required range. The Plan's metadata can be queried before Subscription creation to receive the Addon and Tier details.
How to change quantity for Subscriptions with Tiered Pricing
When a Subscription's quantity has to be changed, you can create the new set of Addons using the Update Subscription API.
Let's consider the following scenario:
A Subscription is created with a quantity of 25 units. The price for this, hence, will be calculated using 2 tiers.
The Customer now wants the Subscription's quantity to be changed from 
25 to 5 units.
In this case, call the Update a Subscription API with replace_addon_list parameter set to true and pass the Addon quantities as per the change in Subscription.

function tieredPricingUpdateSubscription($planId,
       $subscribeQty, $subscriptionId) {
  $planResult = ChargeBee_Plan::retrieve($planId);
  $metaData = $planResult->plan()->metaData;
  $tieredPricing = $metaData["tiered_pricing"];
  $addons = array();
  for($i=0; $i < count($tieredPricing); $i++) {
    $addonObj = $tieredPricing[$i];
    $addonId = $addonObj["addon_id"];
    $startRange = $addonObj["start_range"];
    $endRange = isset($addonObj["end_range"]) ?
                       $addonObj["end_range"] : 0;
    if($subscribeQty < $startRange) {
     break;
    }
    $addonQty = 0;
    if($subscribeQty <= $endRange || $endRange == 0 ) {
         $addonQty = $subscribeQty - $startRange;
    } else {
         $addonQty =  $endRange - $startRange;
    }
    $addonQty = $addonQty + 1;
    $addons[] = array("id" => $addonId,
                      "quantity"=> $addonQty);
  }
  $result = ChargeBee_Subscription::update($subscriptionId,
          array("replaceAddonList"=> "true",
                "addons" => $addons));
}
def tiered_pricing_update_subscription(plan_id, 
          subscription_id, subscribe_qty)
  plan_result = ChargeBee::Plan.retrieve(plan_id)
  meta_data = plan_result.plan.meta_data
  tiered_pricing = meta_data[:tiered_pricing]
  addons = Array.new
  tiered_pricing.each do |addon_obj|
    addon_id = addon_obj[:addon_id]
    start_range = addon_obj[:start_range]
    end_range = addon_obj[:end_range]
    if subscribe_qty < start_range
      break
    end
    addon_qty = 0
    if end_range == nil || subscribe_qty <= end_range
       addon_qty = subscribe_qty - start_range
    elsif
       addon_qty = end_range - start_range
    end
    addon_qty = addon_qty + 1
    addons.push({:id => addon_id, :quantity => addon_qty })
 end
 result = ChargeBee::Subscription.update(subscription_id,
              {:replace_addon_list => "true", 
               :addons => addons})                        
end
def tiered_pricing_update_subscription(plan_id, 
      subscription_id, subscribe_qty):
   plan_result = chargebee.Plan.retrieve(plan_id)
   meta_data = plan_result.plan.meta_data
   tiered_pricing = meta_data["tiered_pricing"]
   addons = []
   for addon_obj in tiered_pricing:
       addon_id = addon_obj.get("addon_id")
       start_range = addon_obj.get("start_range")
       end_range = addon_obj.get("end_range", None)
       addon_qty = 0
       if subscribe_qty < start_range:
          break
       if end_range == None or subscribe_qty <= end_range:
           addon_qty = subscribe_qty - start_range
       else:
           addon_qty = end_range - start_range
       addon_qty = addon_qty + 1
       addons.append({"id": addon_id, "quantity" : addon_qty})
   result = chargebee.Subscription.update(subscription_id,
             {"replace_addon_list" : "true", 
              "addons" : addons})    
function tieredPricingUpdateSubscription(planId,
      subscribeQty, subscriptionId ) {
 chargebee.plan.retrieve(planId)
     .request(function(planError, planResult) {
   if(planError) {
      console.log(planError);
   } else {
      var metaData = planResult.plan.meta_data;
      var tieredPricing = metaData.tiered_pricing;
      var addons = [];
      for(var i=0; i < tieredPricing.length; i++) {
        var addonObj = tieredPricing[i];
        var addonId = addonObj.addon_id
        var startRange = addonObj.start_range
        var endRange = addonObj.end_range
        var addonQty = 0;
        if(subscribeQty < startRange) {
           break;
        }
        if( typeof(endRange) === 'undefined' || 
                         subscribeQty <= endRange) {
           addonQty = subscribeQty - startRange;
        } else {
           addonQty = endRange - startRange;
        }
        addonQty = addonQty + 1;
        addons.push({ "id" : addonId, "quantity" : addonQty})
      }
      chargebee.subscription.update(subscriptionId,
        { "replace_addon_list" : "true",
          "addons" : addons })
         .request(function(error, result){
              if(error) {
                console.log(error);
              } else {
                console.log(result);
              }
      })
   }
 })
}
public void tieredPricingUpdateSubscription(String planId,
        int subscribeQty, String subscriptionId) 
        throws Exception {
    Result planResult = Plan.retrieve(planId).request();
    JSONArray tieredPricing = planResult.plan().metaData()
            .getJSONArray("tiered_pricing");
    Subscription.UpdateRequest updateRequest = Subscription
            .update(subscriptionId);
    int addonIndex = 0; 
    for(int i=0;i < tieredPricing.length(); i++) {
        JSONObject obj = (JSONObject) tieredPricing.get(i);
        String addonId = obj.getString("addon_id");
        int startRange =  obj.getInt("start_range");
        int endRange = obj.getInt("end_range");
        if(subscribeQty < startRange) { 
            break;
        }
        int addonQty = 0;
        if(subscribeQty <= endRange || endRange == 0) {
            addonQty = subscribeQty - startRange;
        } else {
            addonQty = endRange - startRange; 
        }
        addonQty = addonQty + 1; 
        updateRequest.addonId(addonIndex, addonId)
                .addonQuantity(addonIndex, addonQty);
        addonIndex++;
    }
    Result result = updateRequest.replaceAddonList(true)
            .request();
}
public void tieredPricingUpdateSubscription(String planId, 
    int subscribeQty,String subscriptionId) {
    EntityResult planResult = Plan.Retrieve(planId)
                                .Request();
    JArray tieredPricing = planResult.Plan
        .MetaData ["tiered_pricing"].Value<JArray>();
    Subscription.UpdateRequest updateRequest = Subscription
        .Update(subscriptionId);
    int addonIndex = 0; 
    for(int i=0;i < tieredPricing.Count; i++) {
        JObject obj = (JObject)tieredPricing [i];
        String addonId = (string)obj ["addon_id"];
        int startRange =  (int)obj ["start_range"];
        int endRange = (int)obj ["end_range"];
        if(subscribeQty < startRange) { 
            break;
        }
        int addonQty = 0;
        if(subscribeQty <= endRange || endRange == 0) {
            addonQty = subscribeQty - startRange;
        } else {
            addonQty = endRange - startRange; 
        }
        addonQty = addonQty + 1; 
        updateRequest.AddonId(addonIndex, addonId)
            .AddonQuantity(addonIndex, addonQty);
        addonIndex++;
    }
    EntityResult result = updateRequest
        .ReplaceAddonList(true).Request();
}
