This project is to demonstrate a simple billing engine. The billing engine is a RESTful service that handles loaning product.
- Java 21
- a bit of virtual threads
- Spring Boot 3.3.4
- MVC
- Maven
- Junit 5
- H2 DB
- MySQL (Optional)
- Docker
- Enable Docker on your machine
- Run the following command to build the project:
./bin/setup
- Run the following command to start the project:
Default port is 8080 if not provided
./bin/run $port
- Enable Docker on your machine
- Run the following command to build the project:
./bin/setup-mysql
- Run the following command to start the project:
./bin/run 8080 mysql
- Basically this system has 2 basic entities,
Customer
andProduct
.Customer
is the one who takes the loan, whileProduct
is the loan product.- The loan
Product
has a fixed amount, interest rate, and period (in week). - There are multiple loan
Product
. - One customer can take multiple loan
Product
.
- The loan product is taken by the customer through
ProductTransaction
.- The
ProductTransaction
is the transaction that connectsCustomer
andProduct
. - The
ProductTransaction
has a status that indicates the transaction status.
- The
- The transaction is billed through
Billing
.- The
Billing
is the billing information that connectsProductTransaction
andBillingCycle
.
- The
- The
BillingCycle
is the billing period.
erDiagram
CUSTOMER |o--o{ PRODUCT_TRANSACTION : "takes loan"
CUSTOMER {
string name
string email
}
PRODUCT |o--o{ PRODUCT_TRANSACTION : taken
PRODUCT {
string name
numeric amount
float interest_rate
int period
}
PRODUCT_TRANSACTION |o--o{ BILLING : has
PRODUCT_TRANSACTION {
bigint customer_id
bigint product_id
string status
timestamp taken_at
}
BILLING {
bigint product_transaction_id
bigint billing_cycle_id
numeric amount
timestamp paid_at
}
BILLING_CYCLE |o--o{ BILLING : "on period"
BILLING_CYCLE {
string name
timestamp timestamp
}
sequenceDiagram
actor user
autonumber
user ->>+ api: check outstanding
api ->>+ billing: get outstanding
billing ->>+ billing db: fetch unpaid billings
billing db ->>- billing: return unpaid list
billing ->> billing: calculate total amount
billing ->>- api: return aggregated outstanding
api ->>- user: return oustanding response
sequenceDiagram
actor user
autonumber
user ->>+ api: check non-performing
api ->>+ transaction: get npl
transaction ->>+ transaction db: fetch active transactions
transaction db ->>- transaction: return active transaction list
loop every transaction
transaction ->>+ billing db: get billings
billing db ->>- transaction: return billing list
end
transaction ->> transaction: aggregate data
transaction ->>- api: return npl data
api ->>- user: return npl response
sequenceDiagram
actor user
autonumber
user ->>+ api: make payment
api ->>+ billing: process payment
billing ->>+ billing db: fetch oustanding billings
billing db ->>- billing: return outstanding billing list
alt amount not equel
billing ->> api: return false
else
billing ->> billing: change status to paid
billing ->> billing db: save paid data
par check if finished
billing ->>+ billing db: check if transaction finished
billing db ->>- billing: return finish status
billing ->> transaction: set status as finished
transaction ->> transaction db: commit status
end
billing ->>- api: return true
end
api ->>- user: return reponse
sequenceDiagram
actor ext
Note right of ext: external could be user or automation
autonumber
rect rgba(0, 0, 255, .1)
ext ->>+ api: create new billing cycle
api ->>+ billing cycle: send instruction
billing cycle ->> billing cycle db: commit new billing cycle
billing cycle ->>- api: done
api ->>- ext: done
end
rect rgba(0, 255, 255, .1)
ext ->>+ api: create new bills
api ->>+ billing: send instruction
billing ->>+ billing cycle: get latest cycle
billing cycle ->>- billing: return latest cycle
billing ->>+ transaction: find unbilled transactions
transaction ->>- billing: return unbilled list
loop every transaction
billing ->> billing db: create new bill
end
billing ->>- api: done
end
curl --location 'http://localhost:8080/api/v1/billing/customer' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Person A",
"email": "a@email.com"
}'
curl --location 'http://localhost:8080/api/v1/billing/product' \
--header 'Content-Type: application/json' \
--data '{
"name": "Loan A",
"amount": 5000000,
"interestRate": 10,
"period": 50
}'
curl --location 'http://localhost:8080/api/v1/billing/transaction' \
--header 'Content-Type: application/json' \
--data '{
"customerId": 1,
"productId": 1
}'
curl --location --request POST 'http://localhost:8080/api/v1/billing/billing-cycle'
curl --location --request POST 'http://localhost:8080/api/v1/billing'
curl --location 'http://localhost:8080/api/v1/billing/outstanding?trxId=1'
curl --location 'http://localhost:8080/api/v1/billing/payment' \
--header 'Content-Type: application/json' \
--data '{
"productTransactionId": 1,
"amount": 110000.00
}'
curl --location 'http://localhost:8080/api/v1/billing/transaction/npl?customerId=1'
- Complete API contract:
- Swagger UI: http://localhost:8080/swagger-ui.html
© 2024 Andreas Arifin