More Tutorials

Create a portal for your customers

Self-serve portal
Product Catalog 1.0

A customer portal allows your customers to update their information such as their card and billing information. In addition they can view and download their invoices. You could build your own full fledged customer portal using Chargebee's extensive api.

Alternative options

Instead of building your own customer portal you could instead use the hosted customer portal provided by Chargebee.

Features in the sample Self Service Portal

The following features are supported.

  • Viewing the current subscription details such as subscribed items,billing address, shipping address
  • Editing billing and shipping addresses.
  • Adding/Updating the card using the hosted updated card page.
  • Listing the invoices and support for downloading the invoices.
  • Canceling the subscription.

Setup the Chargebee client library

You have to download and import the client library of our choice. Then, configure the client library with your test site and its api key.

For the tutorial, we have configured the site and the credentials in a separate properties file. When the webapp is initialized, the client library gets configured.

EnvInitializer.javaView full code
/**
  * The credentials are stored in a properties file under WEB-INF
  * The live site api keys should be stored securely. It should preferably
  * be stored only in the production machine(s) and not hard coded
  * in code or checked into a version control system by mistake.
  */
Properties credentials = read("WEB-INF/ChargeBeeCredentials.properties");
Environment.configure(credentials.getProperty("site"), credentials.getProperty("api_key"));

Authentication

The sample does not do "real" authentication. But rather just checks if the subscription id is present in your Chargebee site and subscription id is set in the session. The assumption is once you integrate the ssp into your app/website, the customer would login into your app/website you just need to integrate your authentication into sample ssp.

SelfServicePortal.javaView full code
/*
 * Verifying subscription id is present in ChargeBee.
 */
private boolean fetchSubscription(HttpServletRequest request) throws IOException, Exception {
    try {
        String username = request.getParameter("subscription_id");
        if(username == null || username.isEmpty()) {
            return false;
        }
        Result result = Subscription.retrieve(username).request();
        HttpSession session = request.getSession();
        session.setAttribute("subscription_id", 
                result.subscription().id());
        session.setAttribute("customer_id", 
                result.customer().id());
        return true;
    } catch (InvalidRequestException ex) {
        if ("resource_not_found".equals(ex.apiErrorCode)) {
            return false;
        }
        throw ex;
    }
}

Building the Profile page.

The profile page allows the customer to see his information like subcribed items, his billing and shipping addresses etc. We use the retrieve subscription api that returns the customer,subscription and card details.

ssp/subscription.jspView full code
<% 
  Result result = Subscription.retrieve(subscriptionId).request();
%>

The result is used to display the information.

ssp/acc_info_show.jspfView full code
<div className="row">
  <label className="col-sm-5 control-label">Name</label>
  <div className="col-sm-7 form-control-static">
    <%= Utils.esc(result.customer().firstName()) + " " + Utils.esc(result.customer().lastName())%> 
  </div>
</div>

Editing account,billing and shipping addresses

The following api are used to allow the customers to add/update their information.

We have created specific forms for each of the above information as shown below.

ssp/bill_info.jspView full code
<div className="form-group">
  <label for="billing_address[line1]" >Address Line 1</label>
  <small for="billing_address[line1]" className="pull-right text-danger">&nbsp;</small>
  <input type="text" name="billing_address[line1]" className="form-control"
              placeholder="Enter your address line 1" 
              value="<%= Utils.esc(billingAddress != null ? billingAddress.line1() : "")%>"
              maxlength="50" required data-msg-required="cannot be blank" />
</div>

When the customer submits the form, we call the appropriate api

SelfServicePortal.javaView full code
/*
* Update Billing info of customer in ChargeBee.
*/
private void updateBillingInfo(HttpServletRequest req, 
        HttpServletResponse resp) throws ServletException, IOException {
    resp.setHeader("Content-Type", "application/json;charset=utf-8");
    PrintWriter out = resp.getWriter();

    validateParameters(req);
    try {
      Customer.updateBillingInfo(getCustomerId(req))
      .billingAddressFirstName(req.getParameter("billing_address[first_name]"))
      .billingAddressLastName(req.getParameter("billing_address[last_name]"))
      .billingAddressLine1(req.getParameter("billing_address[line1]"))
      .billingAddressLine2(req.getParameter("billing_address[line2]"))
      .billingAddressCity(req.getParameter("billing_address[city]"))
      .billingAddressState(req.getParameter("billing_address[state]"))
      .billingAddressCountry(req.getParameter("billing_address[country]"))
      .billingAddressZip(req.getParameter("billing_address[zip]"))
      .request();

        out.write("{\"forward\" : \"/ssp/subscription.jsp\"}");
    } catch (InvalidRequestException e) {
        handleInvalidRequestErrors(e, resp, out, null);
    } catch (Exception e) {
        handleGeneralErrors(e, resp, out);
    } finally {
        out.flush();
    }
}

We have kept the form input names to follow the chargebee's curl parameter format (example billing_address[first_name] ). Whenever error is thrown by Chargebee API due to invalid data in a parameter, the error response contains the parameter that is invalid in the error_param attribute. The parameter name sent back follows the curl parameter format. We send the error response received from back to browser client where we are able to display the error at the appropriate position near the field.

error: function(jqXHR, textStatus, errorThrown) {
    try {
        var resp = JSON.parse(jqXHR.responseText);
        if ('error_param' in resp) {
            var errorMap = {};
            var errParam = resp.error_param;
            var errMsg = resp.error_msg;
            errorMap[errParam] = errMsg;
            formElement.validate().showErrors(errorMap);
        } else {
            var errMsg = resp.error_msg;
            $(".alert-danger").show().text(errMsg);
        }
    } catch (err) {
        $(".alert-danger").show().text("Error while processing your request");
    }
}

Adding/Updating customers card information via hosted page

We use the Chargebee's hosted page api to let the customers update their card information.

Listing invoices

The list invoices for a subscription api is used to fetch the invoices for the subscription.

ssp/invoice_list.jspView full code
<%
  ListResult result = Invoice.invoicesForSubscription(subscriptionId).limit(20).request();
%>

We iterate through the list, skipping the pending invoices (used when you have metered billing).

ssp/invoice_list.jspView full code
<%
  int i = 0;
  for (ListResult.Entry entry : result) {
    Invoice invoice = entry.invoice();
    if (invoice.status().equals(Invoice.Status.PENDING)) {
      continue;
    }
    i++;
  }
%>

and display the important details of each invoice.

ssp/invoice_list.jspView full code
<td>
  <%= Utils.getHumanReadableDate(invoice.date())%>
</td>
<td> 
  <%=invoice.id()%>
</td>
<td >
  $ <%= String.format("%d.%02d", invoice.total()/ 100, invoice.total() % 100)%>
</td>

Downloading invoices as PDF

We allow users to download the invoices using download invoice as pdf api. The api returns a secure temporary url that we can forward it to the browser to trigger automatic pdf download.

SelfServicePortal.javaView full code
/*
* Returns pdf download url for the requested invoice
*/
private void invoiceAsPdf(HttpServletRequest request, 
        HttpServletResponse response) throws ServletException, IOException, Exception {
    //response.setHeader("Content-Type", "application/json;charset=utf-8");
    String invoiceId = request.getParameter("invoice_id");
    Invoice invoice = Invoice.retrieve(invoiceId).request().invoice();
    if( !getSubscriptionId(request).equals(invoice.subscriptionId()) ) {
        response.sendError(HttpServletResponse.SC_BAD_REQUEST);
        return;
    }
    Result result = Invoice.pdf(invoiceId).request();
    response.sendRedirect(result.download().downloadUrl());
}

Canceling subscription.

The customers also have the option of canceling the subscription. They can choose to cancel immediately or can cancel at the end of term. We use the cancel subscription api to cancel the subscription.

SelfServicePortal.javaView full code
/*
* Cancels the Subscription.
*/
private void subscriptionCancel(HttpServletRequest request, 
        HttpServletResponse response) throws ServletException, IOException, Exception {

    String cancelStatus = request.getParameter("cancel_status");
    Subscription.CancelRequest subscriptionCancelParam = Subscription
            .cancel(getSubscriptionId(request));

    if ("cancel_on_next_renewal".equals(cancelStatus)) {
        subscriptionCancelParam.endOfTerm(Boolean.TRUE);
    }
    subscriptionCancelParam.request();
    response.sendRedirect("/ssp/subscription.jsp");
}

Re-activating subscription

Incase the subscription has been canceled we allow users to re-activate the subscription using the reactivate a subscription api.

SelfServicePortal.javaView full code
/*
* Reactivate the subscription from cancel/non-renewing state to active state.
*/
private void subscriptionReactivate(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
    response.setHeader("Content-Type", "application/json;charset=utf-8");
    PrintWriter out = response.getWriter();

    try{
        Subscription.reactivate(getSubscriptionId(request))
                    .request();
        out.write("{ \"forward\" : \"/ssp/subscription.jsp\" }");
    } catch (InvalidRequestException e ) {
        handleInvalidErrors(e, response, out);
    } catch ( Exception e) {
        handleGeneralErrors(e, response, out);
    } finally {
        out.flush();
    }

}

Validation and Error Handling

Here's how we validate user inputs and handle API call errors in this demo:

  • Client Side Validation: Chargebee uses jQuery form validation plugin to check whether the user’s field inputs(email, zip code and phone number) are valid or not.
  • Server Side Validation: As this is a demo application we have skipped the server side validation of all input parameters. But we recommend you to perform the validation at your end.
  • Payment Errors: If a payment fails due to card verification or processing errors, Chargebee returns an error response(as shown below) which is thrown as a payment exception by the client library. We handle the exceptions in the demo application with appropriate error messages.
    {
      "api_error_code": "payment_method_verification_failed",
      "error_code": "add_card_error",
      "error_msg": "Problem while adding the card. Error message : (3009) Do not honour.",
      "http_status_code": 400,
      "message": "Problem while adding the card. Error message : (3009) Do not honour.",
      "type": "payment"
    }
    
  • General API Errors: Chargebee might return error responses due to various reasons such as invalid configuration, bad request etc. To identify specific reasons for all error responses you can check the API documentation. Also take a look at the error handler file to check how these errors can be handled.
Was this tutorial helpful ?
Need more help?

We're always happy to help you with any questions you might have!

support@chargebee.com