More Tutorials

Migrate Cancel Pages from Brightback.js

Chargebee.js
Growth

Chargebee Cancel Pages has been powered by the standalone Brightback.js script to trigger cancel pages. Now, this functionality is fully integrated into Chargebee.js, which also supports experiences such as Checkout, Portal, Personalized Offers, and Pricing Table.

Brightback.js is deprecated and, although fully functional, it is no longer maintained. This guide explains how to migrate an existing integration from Brightback.js to the Chargebee.js Cancel Page integration.

Reasons to migrate

Brightback.js is deprecated and, although fully functional, it is no longer maintained. To keep your integration up-to-date and reliable, migrate to the Chargebee.js Cancel Page integration. This migration provides the following benefits:

  • Simpler setup and maintenance: Configure Chargebee.js once for all features, including Checkout, Payment Components, Cancel Page, Pricing Table, and Personalized Offers.
    • Unified integration: Initialize with Chargebee.init() and reuse the chargebee object everywhere.
    • Single configuration: Set context (like site) once; no duplicate setup for individual features.
  • Faster rollouts: Launch new Chargebee-hosted experiences or cancel page experiments quickly.
  • More reliable: One script reduces errors, avoids conflicts, and delivers a smoother experience.
  • Future-ready foundation: All upcoming innovations are delivered through this unified setup.

Change summary

To migrate from the Brightback.js library to the Chargebee.js Cancel Page integration, make the following key changes:

  • Script source: Replace the Brightback.js script with the Chargebee.js script (https://js.chargebee.com/v2/chargebee.js).
  • Container element: Change the container element id from bb-cancel to cb-cancel.
  • Initialization: Initialize Chargebee.js and create a cancelPage object before using cancel page features.
  • API usage:
  • API parameters: Update the data structure to match the new schema.

The following code samples illustrate these changes. See the inline comments for more information.

Simple integration (attach handler)

Use this approach to migrate from Brightback.handleData() to the new cancelPage.attachCancelHandler() API with minimal code changes. This method automatically attaches a click handler to your cancel button and initializes the cancel page flow when clicked.

Before (Brightback.js)
<head>
  <!-- OLD: Brightback script URL -->
  <script src="https://app.retention.chargebee.com/js/current/brightback.js"></script>
</head>

<body>
  <!-- OLD: Uses bb-cancel id for the cancel button. -->
  <a id="bb-cancel" href="https://app.yourcompany.com/cancel" class="btn btn-danger">
    Cancel Subscription
  </a>
  
  <script>
    // OLD: Check for Brightback object and call handleData()
    if (window.Brightback) {
      window.Brightback.handleData({
        app_id: 'YOUR_RETENTION_APP_ID',   // OLD: Uses app_id
        subscription_id: 'CHARGEBEE_BILLING_SUBSCRIPTION_ID',
        first_name: 'Jane',                // OLD: snake_case naming
        last_name: 'Doe',                  // OLD: snake_case naming
        email: 'jane.doe@example.com',
        save_return_url: 'https://app.yourcompany.com/save?id=jane_doe',        // OLD: snake_case
        cancel_confirmation_url: 'https://app.yourcompany.com/cancel_confirm?id=jane_doe', // OLD: snake_case
        account: {
          billing_id: 'CHARGEBEE_BILLING_CUSTOMER_ID',  // OLD: billing_id
          created_at: '2024-06-26',                     // OLD: created_at
          internal_id: 'jane_doe',                      // OLD: internal_id
          plan: 'enterprise',
          value: 1000.00                                // OLD: value
        },
        custom: {
          emailCount: 4208
        }
      });
    }
  </script>
</body>
After (Chargebee.js)
<head>
  <!-- NEW: Chargebee.js script URL -->
  <script src="https://js.chargebee.com/v2/chargebee.js"></script>
</head>

<body> 
  <!-- NEW: Uses cb-cancel id for the cancel button. -->
  <a id="cb-cancel" href="https://app.yourcompany.com/cancel" class="btn btn-danger">
    Cancel Subscription
  </a>

  <script>
    // NEW: Initialize Chargebee with site configuration
    const chargebee = window.Chargebee.init({
      site: "YOUR-CHARGEBEE-SUBDOMAIN", 
    });

    (async () => {
      // NEW: Create cancelPage instance using chargebee object
      const cancelPage = await chargebee.cancelPage();
      
      // NEW: cancelPage.attachCancelHandler() method instead of Brightback.handleData()
      cancelPage.attachCancelHandler({
        // NEW: subscription object
        subscription: {
          id: "CHARGEBEE_BILLING_SUBSCRIPTION_ID",
          plan: "enterprise"
        },
        firstName: "Jane",              // NEW: camelCase naming
        lastName: "Doe",                // NEW: camelCase naming
        email: "jane.doe@example.com",
        externalUserId: "jane_doe",     // NEW: externalUserId instead of internal_id
        saveReturnUrl: "https://app.yourcompany.com/save?id=jane_doe",        // NEW: camelCase
        cancelConfirmationUrl: "https://app.yourcompany.com/cancel_confirm?id=jane_doe", // NEW: camelCase naming
        account: {
          customerId: "CHARGEBEE_BILLING_CUSTOMER_ID",  // NEW: customerId instead of billing_id
          firstPurchaseDate: "2024-06-26",              // NEW: firstPurchaseDate instead of created_at
          contractValue: 1000.00                        // NEW: contractValue instead of value
        },
        custom: {
          emailCount: 4208
        }
      });
    })();
  </script>
</body>

Advanced integration (prefetch and redirect)

Use this approach to migrate from Brightback.handleDataPromise() to the new cancelPage.getPage() API. This method lets you prefetch a personalized cancel page URL and programmatically redirect the user, enabling you to run custom logic before redirection or trigger cancellation from multiple places in your application.

Before (Brightback.js)
<head>
  <!-- OLD: Brightback script URL -->
  <script src="https://app.retention.chargebee.com/js/current/brightback.js"></script>
</head>

<body>
  <!-- OLD: Uses bb-cancel id for the cancel button. -->
  <a id="bb-cancel" href="https://app.yourcompany.com/cancel" class="btn btn-danger">
    Cancel Subscription
  </a>
  
  <script>
    function bindLink(id, url) {
      document.getElementById(id).addEventListener('click', () => {
        window.location.assign(url);
      });
    }
    // OLD: Call Brightback.handleDataPromise()
    const p = window.Brightback.handleDataPromise({
        app_id: 'YOUR_RETENTION_APP_ID',   // OLD: Uses app_id
        subscription_id: 'CHARGEBEE_BILLING_SUBSCRIPTION_ID',
        first_name: 'Jane',                // OLD: snake_case naming
        last_name: 'Doe',                  // OLD: snake_case naming
        email: 'jane.doe@example.com',
        save_return_url: 'https://app.yourcompany.com/save?id=jane_doe',        // OLD: snake_case
        cancel_confirmation_url: 'https://app.yourcompany.com/cancel_confirm?id=jane_doe', // OLD: snake_case
        account: {
            billing_id: 'CHARGEBEE_BILLING_CUSTOMER_ID',  // OLD: billing_id
            created_at: '2024-06-26',                     // OLD: created_at
            internal_id: 'jane_doe',                      // OLD: internal_id
            plan: 'enterprise',
            value: 1000.00                                // OLD: value
        },
        custom: {
            emailCount: 4208
        }
    });

    p.then((success) => {
      if (success.valid) {
        bindLink('bb-cancel', success.url);
      } else {
        bindLink('bb-cancel', 'https://app.yourcompany.com/cancel?id=jane_doe');
      }
    });
  </script>
</body>
After (Chargebee.js)
<head>
  <!-- NEW: Chargebee.js script URL -->
  <script src="https://js.chargebee.com/v2/chargebee.js"></script>
</head>

<body>
  <!-- NEW: Uses cb-cancel id for the cancel button. -->
  <a id="cb-cancel" href="https://app.yourcompany.com/cancel" class="btn btn-danger">
    Cancel Subscription
  </a>

  <script>
    // NEW: Initialize Chargebee with site configuration
    const chargebee = window.Chargebee.init({
      site: "YOUR-CHARGEBEE-SUBDOMAIN",
    });

    function bindLink(id, url) {
      document.getElementById(id).addEventListener('click', () => {
        window.location.assign(url);
      });
    }

    (async () => {
      // NEW: Create cancelPage instance using chargebee object
      const cancelPage = await chargebee.cancelPage();
      
      const options = {
         // NEW: subscription object
        subscription: {
          id: "CHARGEBEE_BILLING_SUBSCRIPTION_ID",
          plan: "enterprise"
        },
        firstName: "Jane",              // NEW: camelCase naming
        lastName: "Doe",                // NEW: camelCase naming
        email: "jane.doe@example.com",
        externalUserId: "jane_doe",     // NEW: externalUserId instead of internal_id
        saveReturnUrl: "https://app.yourcompany.com/save?id=jane_doe",        // NEW: camelCase
        cancelConfirmationUrl: "https://app.yourcompany.com/cancel_confirm?id=jane_doe", // NEW: camelCase naming
        account: {
          customerId: "CHARGEBEE_BILLING_CUSTOMER_ID",  // NEW: customerId instead of billing_id
          firstPurchaseDate: "2024-06-26",              // NEW: firstPurchaseDate instead of created_at
          contractValue: 1000.00                        // NEW: contractValue instead of value
        },
        custom: {
          emailCount: 4208
        }
      };
      
      // NEW: cancelPage.getPage() method instead of Brightback.handleDataPromise()
      cancelPage.getPage(options).then((result) => {
        if (result.valid) {
          bindLink('cb-cancel', result.url);
        } else {
          bindLink('cb-cancel', 'https://app.yourcompany.com/cancel?id=jane_doe');
        }
      });
    })();
  </script>
</body>

Detailed migration steps

1. Load chargebee.js instead of brightback.js

Before
<script src="https://app.retention.chargebee.com/js/current/brightback.js"></script>
After
<script src="https://js.chargebee.com/v2/chargebee.js"></script>

2. Update the cancel button element

Change the id attribute of your cancel button from bb-cancel to cb-cancel.

Before
<a id="bb-cancel" href="https://app.yourcompany.com/cancel" class="btn btn-danger">
  Cancel Subscription
</a>
After
<a id="cb-cancel" href="https://app.yourcompany.com/cancel" class="btn btn-danger">
  Cancel Subscription
</a>

3. Initialize Chargebee.js

Once the page loads, initialize Chargebee.js with your Chargebee site subdomain to enable the use of the SDK.

Before

Not available in brightback.js.

After
const chargebee = window.Chargebee.init({
   site: "YOUR-CHARGEBEE-SUBDOMAIN",
});

4. Create the Cancel Page object

Use the cancelPage() function to instantiate Cancel Page.

Before

Not available in brightback.js.

After
const cancelPage = await chargebee.cancelPage();

5. Replace Retention integration methods

Option A: Simple integration (attach handler)

Use this approach to migrate from Brightback.handleData() to the new cancelPage.attachCancelHandler() API with minimal code changes. This method automatically attaches a click handler to your cancel button and initializes the cancel page flow when clicked.

Before
// OLD: Check for Brightback object and call handleData()
if (window.Brightback) {
    window.Brightback.handleData({
    app_id: 'YOUR_RETENTION_APP_ID',   // OLD: Uses app_id
    subscription_id: 'CHARGEBEE_BILLING_SUBSCRIPTION_ID',
    first_name: 'Jane',                // OLD: snake_case naming
    last_name: 'Doe',                  // OLD: snake_case naming
    email: 'jane.doe@example.com',
    save_return_url: 'https://app.yourcompany.com/save?id=jane_doe',        // OLD: snake_case
    cancel_confirmation_url: 'https://app.yourcompany.com/cancel_confirm?id=jane_doe', // OLD: snake_case
    account: {
        billing_id: 'CHARGEBEE_BILLING_CUSTOMER_ID',  // OLD: billing_id
        created_at: '2024-06-26',                     // OLD: created_at
        internal_id: 'jane_doe',                      // OLD: internal_id
        plan: 'enterprise',
        value: 1000.00                                // OLD: value
    },
    custom: {
        emailCount: 4208
    }
    });
}
After
cancelPage.attachCancelHandler({
    // NEW: subscription object
    subscription: {
        id: "CHARGEBEE_BILLING_SUBSCRIPTION_ID",
        plan: "enterprise"
    },
    firstName: "Jane",              // NEW: camelCase naming
    lastName: "Doe",                // NEW: camelCase naming
    email: "jane.doe@example.com",
    externalUserId: "jane_doe",     // NEW: externalUserId instead of internal_id
    saveReturnUrl: "https://app.yourcompany.com/save?id=jane_doe",        // NEW: camelCase
    cancelConfirmationUrl: "https://app.yourcompany.com/cancel_confirm?id=jane_doe", // NEW: camelCase naming
    account: {
        customerId: "CHARGEBEE_BILLING_CUSTOMER_ID",  // NEW: customerId instead of billing_id
        firstPurchaseDate: "2024-06-26",              // NEW: firstPurchaseDate instead of created_at
        contractValue: 1000.00                        // NEW: contractValue instead of value
    },
    custom: {
        emailCount: 4208
    }
});

Option B: Advanced integration (prefetch and redirect)

Use this approach to migrate from Brightback.handleDataPromise() to the new cancelPage.getPage() API. This method lets you prefetch a personalized cancel page URL and programmatically redirect the user, enabling you to run custom logic before redirection or trigger cancellation from multiple places in your application.

Before
function bindLink(id, url) {
    document.getElementById(id).addEventListener('click', () => {
    window.location.assign(url);
    });
}
// OLD: Call Brightback.handleDataPromise()
const p = window.Brightback.handleDataPromise({
    app_id: 'YOUR_RETENTION_APP_ID',   // OLD: Uses app_id
    subscription_id: 'CHARGEBEE_BILLING_SUBSCRIPTION_ID',
    first_name: 'Jane',                // OLD: snake_case naming
    last_name: 'Doe',                  // OLD: snake_case naming
    email: 'jane.doe@example.com',
    save_return_url: 'https://app.yourcompany.com/save?id=jane_doe',        // OLD: snake_case
    cancel_confirmation_url: 'https://app.yourcompany.com/cancel_confirm?id=jane_doe', // OLD: snake_case
    account: {
        billing_id: 'CHARGEBEE_BILLING_CUSTOMER_ID',  // OLD: billing_id
        created_at: '2024-06-26',                     // OLD: created_at
        internal_id: 'jane_doe',                      // OLD: internal_id
        plan: 'enterprise',
        value: 1000.00                                // OLD: value
    },
    custom: {
        emailCount: 4208
    }
});

p.then((success) => {
    if (success.valid) {
    bindLink('bb-cancel', success.url);
    } else {
    bindLink('bb-cancel', 'https://app.yourcompany.com/cancel?id=jane_doe');
    }
});
After
// NEW: Initialize Chargebee with site configuration
const chargebee = window.Chargebee.init({
    site: "YOUR-CHARGEBEE-SUBDOMAIN",
});

function bindLink(id, url) {
    document.getElementById(id).addEventListener('click', () => {
    window.location.assign(url);
    });
}

(async () => {
    // NEW: Create cancelPage instance using chargebee object
    const cancelPage = await chargebee.cancelPage();
    
    const options = {
        // NEW: subscription object
    subscription: {
        id: "CHARGEBEE_BILLING_SUBSCRIPTION_ID",
        plan: "enterprise"
    },
    firstName: "Jane",              // NEW: camelCase naming
    lastName: "Doe",                // NEW: camelCase naming
    email: "jane.doe@example.com",
    externalUserId: "jane_doe",     // NEW: externalUserId instead of internal_id
    saveReturnUrl: "https://app.yourcompany.com/save?id=jane_doe",        // NEW: camelCase
    cancelConfirmationUrl: "https://app.yourcompany.com/cancel_confirm?id=jane_doe", // NEW: camelCase naming
    account: {
        customerId: "CHARGEBEE_BILLING_CUSTOMER_ID",  // NEW: customerId instead of billing_id
        firstPurchaseDate: "2024-06-26",              // NEW: firstPurchaseDate instead of created_at
        contractValue: 1000.00                        // NEW: contractValue instead of value
    },
    custom: {
        emailCount: 4208
    }
    };
    
    // NEW: cancelPage.getPage() method instead of Brightback.handleDataPromise()
    cancelPage.getPage(options).then((result) => {
    if (result.valid) {
        bindLink('cb-cancel', result.url);
    } else {
        bindLink('cb-cancel', 'https://app.yourcompany.com/cancel?id=jane_doe');
    }
    });
})();

Input parameter schema changes

The input parameters for the methods have been changed as follows:

Brightback.jsChargebee.jsNotes
app_idNot needed.Site is configured globally in Chargebee.init().
first_namefirstNameChanged to camel case.
last_namelastNameChanged to camel case.
full_namefullNameChanged to camel case.
save_return_urlsaveReturnUrlChanged to camel case.
cancel_confirmation_urlcancelConfirmationUrlChanged to camel case.
account.company_nameaccount.companyNameChanged to camel case.
account.company_domainaccount.companyDomainChanged to camel case.
account.billing_idaccount.customerIdRenamed.
account.valueaccount.contractValueRenamed.
account.created_ataccount.firstPurchaseDateRenamed.
account.internal_idexternalUserIdRenamed and moved to top level.
account.plansubscription.planMoved to subscription object.
account.plan_termsubscription.planTermMoved to subscription object.
subscription_idsubscription.idRenamed and moved to subscription object.

Best practices

Local development

By default and for security reasons, Chargebee does not respond to cancel page API requests from localhost. For local development to work, temporarily map a domain to your localhost to verify the XHR request or use a tunnel service like ngrok or localtunnel. Watch the network tab in your DOM inspector to diagnose any additional issues.

Render within iframes

You can embed the Chargebee cancel page within your application using an iframe. To do so securely, you must configure a vanity domain that matches the domain of your website. For example, if your app runs at https://app.yourcompany.com, the iframe must load from a domain like https://cancel.yourcompany.com. Domains like yourcompany.chargebee.com cannot be embedded in an iframe due to cross-origin restrictions.

Additional resources

Was this tutorial helpful ?
Need more help?

We're always happy to help you with any questions you might have! Click here to reach out to us.