Metered billing in Chargebee can be used if you need to bill the same customer varying amounts for every billing period based on usage. See the docs for more details.
Overview
How does it work?
If metered billing is enabled Chargebee will create an event invoice_created (see Event types) when one of these 2 conditions occur:
- Subscription Renewal.
- Plan changes (upgrade/downgrade or quantity changes) in the subscription.
You will receive generated invoices via webhooks in your webhook handler, and after receiving the invoice, you can use the ID of the invoice resource to add the usage charges to the invoice received.
Note: For the first invoice for a subscription, the invoice will not be moved to pending state but rather it will be collected immediately. For example, the invoice created during signup or during activation (incase of trial) will be collected immediately.
What are Webhooks?
Webhooks are a great way of being notified of changes that occur within your billing system. Once configured, Chargebee will notify your service whenever a event occurs. View the documentation for further details.
Prerequisites
To try out the tutorial yourself, you'll need the following:
- A Chargebee account. Signup for a free trial if you don't have one.
- A plan.
- A Non recurring addon named wallpapers
- Your Chargebee API key for your test site.
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.
/**
* 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"));
For the tutorial, we have configured the site credentials in config/environments/development.rb
ENV["CHARGEBEE_SITE"]="honeycomics-test"
ENV["CHARGEBEE_API_KEY"]="test_5LjFA6K6doB2EKRP7cufTd5TvT32a5BrT"
We setup the client library in config/initializers/chargebee.rb
ChargeBee.configure(:site => ENV["CHARGEBEE_SITE"], :api_key => ENV["CHARGEBEE_API_KEY"])
For the tutorial, we have configured the site credentials in Config.php
require_once(dirname(__FILE__) . "/lib/ChargeBee.php");
/*
* Sets the environment for calling the Chargebee API.
* You need to sign up at ChargeBee app to get this credential.
* It is better if you fetch configuration from the environment
* properties instead of hard coding it in code.
*/
ChargeBee_Environment::configure("honeycomics-test", "test_5LjFA6K6doB2EKRP7cufTd5TvT32a5BrT");
Enable metered billing
To enable metered billing, login to the admin console, go to Settings > Site Info, enable “Notify for Pending Invoices” and click on “Update Site Info”. Once this is done, you are all set.
Listening to events
Chargebee calls the configured webhook url with the event details sent as json in the request body. First we need to parse it to obtain the event.
/*
* Getting the json content from the request.
*/
BufferedReader reader = request.getReader();
/*
* Assigning the recieved content to ChargeBee Event object.
*/
Event event = new Event(reader);
# Getting the json content from the request.
body = request.body.string
# Assigning the recieved content to ChargeBee Event object.
event = ChargeBee::Event.deserialize(body)
/*
* Getting the json content from the request.
*/
if(!checkIfRequestIsFromChargeBee() ){
return;
}
$content = file_get_contents('php://input');
/*
* Assigning the recieved content to ChargeBee Event object.
*/
$event = ChargeBee_Event::deserialize($content);
Then we check if the event is of type invoice_created.
/*
* Checking the event type as Pending Invoice Created to add Charge for Meter Billing.
*/
EventType eventType = event.eventType();
if (EventType.PENDING_INVOICE_CREATED.equals(eventType)) {
String invoiceId = event.content().invoice().id();
Invoice invoice = Invoice.retrieve(invoiceId).request().invoice();
if( invoice.status().equals(Invoice.Status.PENDING) ) {
new MeterBilling().closePendingInvoice(invoice);
response.getWriter().write("Invoice has been closed successfully");
} else {
response.getWriter().write("Invoice is not in pending state");
}
}
# Checking the event type as Pending Invoice Created to add Charge for Meter Billing.
if event.event_type == "pending_invoice_created"
id = event.content.invoice.id
invoice_obj = ChargeBee::Invoice.retrieve(id).invoice
if invoice_obj.status == "pending"
MeterBilling.new.close_pending_invoice(invoice_obj)
render json: {:message => "Invoice has been closed successfully" }
else
render json: {:message => "Invoice is not in pending state" }
end
else
render json: {:status => "ok"}
end
/*
* Checking the event type as Pending Invoice Created to add Charge for Meter Billing.
*/
$eventType = $event->eventType;
if($eventType == "pending_invoice_created" ) {
$invoiceId = $event->content()->invoice()->id;
$invoiceObj = ChargeBee_Invoice::retrieve($invoiceId)->invoice();
if($invoiceObj->status == "pending" ){
$meterBilling = new MeterBilling();
$meterBilling->closePendingInvoice($invoiceObj);
echo "Invoice has been closed successfully";
} else {
echo "Invoice is not in pending state";
}
}
Adding usage charges
We get the details from the invoice received from webhook.
String invoiceId = invoiceObj.id();
String subscriptionId = invoiceObj.subscriptionId();
Timestamp invoiceDate = invoiceObj.date();
invoice_id = invoice_obj.id
subscription_id = invoice_obj.subscription_id
invoice_date = invoice_obj.date
$invoiceId = $invoiceObj->id;
$subscriptionId = $invoiceObj->subscriptionId;
$invoiceDate = $invoiceObj->date;
We then add a unmodeled charge to the invoice for the time period via add charge to pending invoice api
int chargeInCents = getUsageCharge(invoiceDate, subscriptionId);
/*
* Calling ChargeBee Add Charge Invoice API and add Charge to invoice
* based on the usage made by customer.
*/
Invoice.addCharge(invoiceId).amount(chargeInCents)
.description("monthly usage")
.request();
charge = get_usage_charge(invoice_date,subscription_id)
# Calling ChargeBee Add Charge Invoice API and add Charge to invoice
# based on the usage made by customer.
ChargeBee::Invoice.add_charge(invoice_id, {
:amount => charge,
:description => "monthly charge"
})
$chargeInCents = MeterBilling::getUsageCharge($invoiceDate, $subscriptionId);
$addChargeParam = array("amount" => $chargeInCents ,
"description" =>"monthly usage");
/*
* Calling ChargeBee Add Charge Invoice API and add Charge to invoice
* based on the usage made by customer.
*/
ChargeBee_Invoice::addCharge($invoiceId, $addChargeParam);
We also add a addon based charge using add addon to pending invoice api
Integer addonQuantity = getQuantityUsed(invoiceDate, subscriptionId);
/*
* Calling the ChargeBee Add Addon Charge Invoice API and add the no of
* addons used by customers to the invoice.
*/
Invoice.addAddonCharge(invoiceId).addonId("wallpapers")
.addonQuantity(addonQuantity)
.request();
addon_quantity = get_quantity_used(invoice_date, subscription_id)
# Calling the ChargeBee Add Addon Charge Invoice API and add the no of
# addons used by customers to the invoice.
ChargeBee::Invoice.add_addon_charge(invoice_id,
:addon_id => "wallpapers" ,
:addon_quantity => addon_quantity
)
$addonQuantity = MeterBilling::getQuantityUsed($invoiceDate, $subscriptionId);
$addAddonCharge = array("addonId"=>"wallpapers",
"addonQuantity" => $addonQuantity);
/*
* Calling the ChargeBee Add Addon Charge Invoice API and add the no of
* addons used by customers to the invoice.
*/
ChargeBee_Invoice::addAddonCharge($invoiceId,$addAddonCharge);
Once done we invoke the close pending invoice api.
/*
* Closing the invoice and Collecting the payment(if auto collection is on)
* by calling the ChargeBee Collect Invoice API.
*/
Invoice.close(invoiceId).request();
# Closing the invoice and Collecting the payment(if auto collection is on)
# by calling the ChargeBee Collect Invoice API.
ChargeBee::Invoice.close(invoice_id)
/*
* Closing the invoice and Collecting the payment(if auto collection is on)
* by calling the ChargeBee Collect Invoice API.
*/
ChargeBee_Invoice::close($invoiceId);
Note: Securing the webhook url
The webhook url needs to be secured either using Basic Authentication or via a secret parameter.
/**Check if the request is from chargebee.
* You can secure the webhook either using
* - Basic Authentication
* - Or check for specific value in a parameter.
*<br/>
* For demo purpose we are using the second option though
* basic auth is strongly preferred. Also store the key
* securely in the server rather than hard coding in code.
*/
private static boolean checkIfRequestIsFromChargeBee(HttpServletRequest req,
HttpServletResponse resp) throws IOException{
if(!"DEMO_KEY".equals(req.getParameter("webhook_key"))){
resp.sendError(HttpServletResponse.SC_FORBIDDEN,
"webhook_key is not correct");
return false;
}
return true;
}
# Check if the request is from chargebee.
# You can secure the webhook either using
# - Basic Authentication
# - Or check for specific value in a parameter.
# For demo purpose we are using the second option though
# basic auth is strongly preferred. Also store the key
# securely in the server rather than hard coding in code.
def check_if_request_is_from_chargebee(_params)
if _params['webhook_key'] != "DEMO_KEY"
render status: 403, json: {"error_msg" => "webhook_key is not correct"}
return false
end
return true
end
/* Check if the request is from chargebee.
* You can secure the webhook either using
* - Basic Authentication
* - Or check for specific value in a parameter.
* For demo purpose we are using the second option though
* basic auth is strongly preferred. Also store the key
* securely in the server rather than hard coding in code.
*/
function checkIfRequestIsFromChargeBee() {
if($_REQUEST['webhook_key'] != "DEMO_KEY" ) {
header("HTTP/1.0 403 Error");
return false;
}
return true;
}
Reference Links
We're always happy to help you with any questions you might have!
support@chargebee.com