JDK 7 or later installed
Maven 3.5.2 or later installed
Git 2.15.1 or later installed
Some of the Maven plugins used may not be available to m2e, however, and the projects will not import completely. To work around this issue, the Eclipse
Preferences > Maven > Lifecycle Mappings configuration can be updated to include mappings like the following:
<?xml version="1.0" encoding="UTF-8"?> <lifecycleMappingMetadata> <pluginExecutions> <pluginExecution> <pluginExecutionFilter> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <versionRange>1.4</versionRange> <goals> <goal>run</goal> </goals> </pluginExecutionFilter> <action> <execute> <runOnConfiguration>true</runOnConfiguration> <runOnIncremental>true</runOnIncremental> </execute> </action> </pluginExecution> <pluginExecution> <pluginExecutionFilter> <groupId>org.codehaus.mojo</groupId> <artifactId>templating-maven-plugin</artifactId> <versionRange>1.0-alpha-3</versionRange> <goals> <goal>filter-sources</goal> </goals> </pluginExecutionFilter> <action> <execute> <runOnConfiguration>true</runOnConfiguration> <runOnIncremental>true</runOnIncremental> </execute> </action> </pluginExecution> <pluginExecution> <pluginExecutionFilter> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <versionRange>[1.0,)</versionRange> <goals> <goal>add-source</goal> <goal>add-test-source</goal> <goal>add-resource</goal> <goal>add-test-resource</goal> <goal>maven-version</goal> <goal>parse-version</goal> </goals> </pluginExecutionFilter> <action> <execute> <runOnConfiguration>true</runOnConfiguration> <runOnIncremental>true</runOnIncremental> </execute> </action> </pluginExecution> </pluginExecutions> </lifecycleMappingMetadata>
Kill Bill is a standard Maven project. Simply run the following command from the project root directory:
mvn clean install -DskipTests
On the first build, Maven will download all the dependencies from the internet and cache them in the local repository (
~/.m2/repository), which can take a considerable amount of time. Subsequent builds will be faster.
Once built, you can start Kill Bill by running:
By default, Kill Bill will expect a MySQL database. To switch to PostgreSQL:
Create a killbill user and database (verify connectivity with
psql -h 127.0.0.1 -U killbill -p 5432):
create USER killbill; \password killbill; CREATE DATABASE killbill WITH OWNER killbill;
Create the schema:
./bin/db-helper -a create --driver postgres -u killbill -p killbill
org.killbill.dao.url=jdbc:postgresql://127.0.0.1:5432/killbill org.killbill.dao.user=killbill org.killbill.dao.password=killbill org.killbill.billing.osgi.dao.url=jdbc:postgresql://127.0.0.1:5432/killbill org.killbill.billing.osgi.dao.user=killbill org.killbill.billing.osgi.dao.password=killbill
org.kill-bill.billing:killbill-oss-parent) defines all of the dependencies required by Kill Bill. If a
-SNAPSHOT version is specified (e.g.
0.141.74-SNAPSHOT), this means some dependencies haven’t been released yet and you need to build them locally, before being able to build Kill Bill.
Typically, the following repositories need to be built in order:
Navigating the Kill Bill codebase
It can be quite daunting to start poking around the Kill Bill codebase, as the code is scattered over various repositories. Here is a primer on how to get started.
https://github.com/killbill/killbill-api: Java APIs, used when embedding Kill Bill (advanced users), by the REST API layer (internally) as well as by plugins, which need to interact with the core system.
https://github.com/killbill/killbill-plugin-api: Java Plugin APIs, which need to be implemented by plugin developers.
https://github.com/killbill/killbill-commons: similar in concept to Apache Commons, repository of re-useable components. You could use these in your projects even outside of the Kill Bill environment.
https://github.com/killbill/killbill-platform: underlying Kill Bill platform, contains code unrelated to billing and payments, such as setting up the lifecycle, the OSGI environment, the webapp container bridge, logging, etc.
https://github.com/killbill/killbill: the Kill Bill library, containing the core of the system. It is divided into submodules, each one of them being independent (own APIs, own set of tables, etc.) and interacting with other modules either via internal Java APIs or Bus events.
The following blog posts can help provide more context on the Kill Bill architecture:
https://github.com/killbill/killbill-integration-tests: while each repository contains its own test suites, from unit tests to functional tests (with and without a persistent layer), as well as end-to-end tests (see the
profiles/killbilltests in the main killbill repository for example), the killbill-integration-tests repository adds another set of tests which focus on long running scenarii against a running Kill Bill server. This also gives you a base framework to develop your own tests, to verify the integration of Kill Bill in your environment and to make sure it follows your business rules.
https://github.com/killbill/killbill-stripe-plugin, https://github.com/killbill/killbill-avatax-plugin, etc. provide integrations with third-party providers.
https://github.com/killbill/killbill-analytics-plugin, etc. provide additional functionality (e.g. notification plugins).
Date, Time and Timezone
A few general concepts around time in Kill Bill:
Kill Bill’s granularity is the day, and as a result the system will not invoice for portions of a day.
Each account in Kill Bill has a default timezone and that timezone is used throughout the system to be able to convert a DateTime into a Date into the account Timezone.
Kill Bill will internally use UTC for all its Datetime manipulation, but any Date information is interpreted as a Date in the account timezone.
Mysql Date, DateTime, Timestamp
We are only relying on date and datetime which are not sensitive to the MySQL timezone setting:
Datetime: Storing a Datetime value into MySQL relies on
datetimewhich is independent of the mysql time_zone. It is stored as a UTC value, and the selected value is always the same, regardless of the MySQL timezone.
LocalDate: Storing a LocalDate value into MySQL relies on
datewhich is also independent of the MySQL time_zone.
From an operation point of view, there are different places where timezone may be set:
Server on which Kill Bill runs
It is required to have Kill Bill runs in UTC for correct serialization of DateTime/LocalDate. Actually, in Java, there is no UTC timezone setting but instead
GMT. In a first approximation, we will consider those identical, even though they are not and could lead to some rare issues.
When Kill Bill starts, it will override the default timezone if this one was specified as a system property with something different than GMT. The code will log a WARN message and proceed to do so, to avoid issues later down the road.
Kill Bill APIs that accept dates as an argument will allow for the following:
A fully qualified Datetime (a point in time)
If there is a need to convert from a Datetime to a Date, the conversion will occur by specifying the account timezone, so the resulting Date is as seen by the customer. This would for instance be the case when triggering a future invoice by specifying a target Datetime.
If there is a need to convert from a Date to Datetime, this is obviously more subtle as we can’t infer the additional precision to compute the time. The Date is always interpreted to be a Date as seen by the customer, that is in the account timezone.
The system will use the reference time on the account in such a way that converting back from that fully qualified Datetime using the account timezone would give us back the original Date provided.
Multiple changes in a day
So what happens if a user is making several subscription changes within the same day?
In the default mode, Kill Bill will react to changes immediately and trigger a new invoice for each change, which in turn might result in a charge.
Let’s consider the following case, where there exists 3 monthly plans (Bronze, Silver and Gold), ordered by ascending price:
Initially, the customer is invoiced for the Bronze, from january 1st to feb 1st. By default a payment would also be made.
On January 1st again, the customer changes its mind and moves to Silver. A new invoice is generated that will credit the full month — including the day of january 1st — and the new plan is now invoiced from january 1st to february 1st and the credit generated is immediately used, so in the end the customer is really only invoiced for the difference of the price between the 2 plans; Additionally, a new payment is made for that amount.
If now the customer changes its plan on Jan 2nd, the portion from January 1st to January 2nd will be invoiced for the Silver plan and the portion from January 2nd to February 1st will be invoiced for the Gold plan.
From an entitlement point of view, the system will reflect the current plan and therefore two different calls to retrieve the plan information on January 1st may lead to different results since there was a change of plan during that day.