Introducing the Chargebee CLI

Retrieve subscription change timeline

Use the fetchChangeSubscriptionOptions function to control when a subscription change made in the Self-Serve Portal takes effect: either immediately or at the end of the current term. Chargebee invokes this function when a customer changes their subscription and uses the value you return to schedule the change.

This function is one of the two functions supported by Portal Custom Code.

Usage and invocation

Write your business logic to classify each subscription change, and return end_of_term to tell Chargebee when to apply it:

  • Set end_of_term to false to apply the change immediately.
  • Set end_of_term to true to apply the change at the end of the term.

Example

Apply upgrades immediately so the customer gets the added value right away, and defer downgrades to the end of the term so the customer keeps what they paid for until the term ends.

Requirements

  • Write the function in JavaScript.
  • Make sure the function runs in a Node.js v6.0 environment.
  • Use the resources that Chargebee passes to the function to write your business logic. These resources use the same format as the output of the Node.js client library.
  • To retrieve additional resources, use Chargebee's Node.js client library, provided you've already configured it with your API key and secret key.
  • Return a JavaScript object that matches the JSON schema on this page.

Parameters

ParameterTypeDescription
dataObjectThe resources that Chargebee passes to your custom code.
data.subscriptionObjectThe subscription object.
data.subscription_changesObjectThe updated plan and addons selected by the customer for the subscription.
data.subscription_changes.plan_idString, requiredItem price ID of the plan.
data.subscription_changes.plan_quantityIntegerQuantity of the plan. When multi-decimal support is enabled for your site, this field is null; read plan_quantity_in_decimal instead.
data.subscription_changes.plan_quantity_in_decimalStringQuantity of the plan as a decimal string. Provided when multi-decimal support is enabled for your site. Use this field instead of plan_quantity on multi-decimal sites, and wrap it in Number() before comparing.
data.subscription_changes.addonsArray<Object>List of addons.
data.subscription_changes.addons[].idString, requiredItem price ID of the addon.
data.subscription_changes.addons[].quantityIntegerQuantity of the addon. When multi-decimal support is enabled for your site, this field is null; read quantity_in_decimal instead.
data.subscription_changes.addons[].quantity_in_decimalStringQuantity of the addon as a decimal string. Provided when multi-decimal support is enabled for your site. Wrap it in Number() before comparing.
data.customerObjectThe customer object.
data.item_pricesArray&lt;Object&gt;The plan and addon item prices currently part of the subscription.
callbackFunctionFunction to send the output of the custom code to Chargebee.
callback.errorObjectError details to be sent to Chargebee when the custom code has not been successfully executed.
callback.successObjectOutput of the custom code to be sent to Chargebee when the custom code has been successfully executed.
loggerFunctionHelps log debug information for your custom code. To emit a log entry, call logger.debug('key', 'value') in your custom code. The logs appear in Chargebee Billing on the Custom Codes page at https://YOUR_SUBDOMAIN.chargebee.com/custom_codes, under the Execution section, after you execute the custom code.

Example

This function treats a change as a downgrade when the customer reduces the quantity of the current plan or moves to a lower-priced plan, and defers those downgrades to the end of the term. All other changes apply immediately.

var chargebee = require("chargebee")

exports.fetchChangeSubscriptionOptions = function ({ subscription, subscription_changes, item_prices }, callback, logger) {

    try {
        const changeSubPlanItemPrice = item_prices.find(({ item_price }) => item_price.id === subscription_changes.plan_id);
        const subPlanItemPrice = subscription.subscription_items.find(subItem => subItem.item_type === 'plan')
        const subPlanId = subPlanItemPrice.item_price_id;

        let downgrade = false;
        // On multi-decimal sites, the quantity is in the *_in_decimal fields; fall back to the integer fields otherwise.
        let changedPlanQuantity = Number(subscription_changes.plan_quantity_in_decimal || subscription_changes.plan_quantity);
        let currentPlanQuantity = Number(subPlanItemPrice.quantity_in_decimal || subPlanItemPrice.quantity);
        let changedPlanUnitPrice = changeSubPlanItemPrice.item_price.price;

        // Same plan, quantity reduced.
        if (subPlanId == subscription_changes.plan_id && changedPlanQuantity < currentPlanQuantity) {
            downgrade = true;
        }
        // Different plan at a lower price.
        if (subPlanId != subscription_changes.plan_id && subPlanItemPrice.unit_price > changedPlanUnitPrice) {
            downgrade = true;
        }

        callback(null, { end_of_term: downgrade })
    } catch (err) {
        callback(err);
    }

}
Sample output
{
  "end_of_term": true
}

Apply upgrades immediately and downgrades at the end of the term

The basic example inspects the plan only. This version also treats a change as a downgrade when the customer reduces an addon quantity or removes an addon. Any of these changes is deferred to the end of the term; otherwise, the change applies immediately. Use this approach when your subscriptions include quantity-based addons.

Custom code
let chargebee = require("chargebee");

exports.fetchChangeSubscriptionOptions = function ({ subscription, subscription_changes, item_prices }, callback, logger) {

    try {

        // On multi-decimal sites, quantities are in the *_in_decimal fields; fall back to the integer fields otherwise.
        const qty = (item) => Number(item.quantity_in_decimal || item.quantity);

        // Current plan item on the subscription.
        const subPlanItemPrice = subscription.subscription_items.find(subItem => subItem.item_type === 'plan');
        // Addons currently on the subscription.
        const addonsBefore = subscription.subscription_items.filter(item => item.item_type === 'addon');
        // Addons selected in the change.
        const addonsAfter = subscription_changes.addons || [];

        let downgrade = false;

        // Same plan, quantity reduced.
        if (Number(subscription_changes.plan_quantity_in_decimal || subscription_changes.plan_quantity) < qty(subPlanItemPrice)) {
            downgrade = true;
        }

        // Any addon removed or reduced in quantity.
        for (const before of addonsBefore) {
            const after = addonsAfter.find(a => a.id === before.item_price_id);

            if (!after || qty(after) < qty(before)) {
                downgrade = true;
                break;
            }
        }

        callback(null, { end_of_term: downgrade });
    } catch (err) {
        callback(err);
    }

}
Sample output
{
  "end_of_term": true
}

Expected JSON schema for the output

The return value must match the following JSON schema.

JSON schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-04/schema#",
  "definitions": {},
  "properties": {
    "end_of_term": {
      "type": "boolean",
      "default": false,
      "examples": [
        true
      ]
    }
  },
  "additionalProperties": false
}