billing-requirements
You are an expert Domain Driven Design (DDD) software architect that specialises and has a wealth of experience in designing job boards in the blue collar industry in south east asia, specifically Singapore and specially using ruby on rails. Think payrolls, shift managements, daily job fulfilment, posting job ads, boosting job listings. You know how to handle DDD elegantly only choosing relevant DDD concepts so that the system remains maintainable for users.
I would like to design a Billing system for my platform, which for a start will only handle billing for Ads, and then the following in order:
- Careers Job Posting
- Job Boost
- Gig
When answering, use the following format:
- Provide a high level ramp-up (grok) that defines how we should structure our Billing domain, as well as a exhausive list of requirements for the billing system
- For each topic/concept in the high level ramp-up, provide a detailed description for each topic
- Provide a high level schema design showing how the different database tables would be connected.
- have a section for each table name that provides the minimal set of attributes that will fulfil the requirements
Overview
Jod uses a pre-paid model where Org::Company will purchase "credits" to do use our platform.
Here are bounded-contexts (a.k.a domains) that require support from billing, some are planned are unimplemented at the time of writing.
- Gig: Finding additional manpower on a daily basis to fill up shifts. Credits are deducted when a job is fulfilled
- Careers: Spend credits to post a full-time job on the job board
- Ads: Placing a creative/picture on prime real estate on our website
- Workforce: Possibly a subscription model that allows Orgs to manage their shifts. This will expand into calculating payrolls with social security (e.g. CPF in SG, BPJS in ID) and income tax for various countries
We want to design a billing system under the domain name ::Billing.
Focus right now is to minimally support billing for Careers and Ads domains. However we want to consider other domains so that our billing system is flexible.
Feature Descriptions
Gig
Currently running in production, plan to migrate over to rails.
Feature that allows employers to post Gig:Job and their respective Gig::Shift to find adhoc manpower on a daily basis.
Org::UserProfilewould post a job for a singleOrg::Outletthat is part of theOrg::Company.- here we would reserve credits, so they cannot "double post"
- Talents would apply to these jobs
Org::UserProfilewill select an applicant- Applicant acknowledges and turns up for the shift
Org::UserProfile(a.k.a Hiring Manager) could generate a barcdoe to clock-in (and later clock-out) the applicant- Upon completion of the job, system would release the reserved credits and deduct credits from the Org's account based on the total wage of that shift.
Careers
Feature that allows employers to post full-time/part-time jobs on the platform.
- this is independent of Gig,
- since Gig solve a different problem, that is finding additional manpower and/or adhoc manpower mostly for retail.
- part-time here is similar to full-time, but they work less hours than full-time staff.
Vision is to allow companies already using Gig to find full-time staff if they want to stop depending on adhoc manpower.
Companies will purchase coins which they can use to post a full-time job.
- charging the credits need to be flexible to allow us to charge based on:
- each application the job received
- each job posting, regardless of the number of application
Ads
Feature that allows employers to upload a creative/picture which gets shown on our website.
- employers can choose different locations on our website for different prices and different duration
- the better the location (e.g. above the fold on home page) would cost most
- a user clicking on their ad would be directed to a job detail page or the company's profile page, within the same website
We should be able to charge for different locations.
Job Boost
Feature that allows employers to "boost" a Listing::Job.
Listing::Jobis a projection ofGig::JobandCareers::Jobto enable easier full-text search from a single table and to allow showing different jobs in a single list.Listing::Jobthat are boosted appear on the top of job lists pages (e.g. top of the list in the search page)- Charged based on the number of days they want the job to be boosted.
Finance requirements: Careers, Ads & Job Boost
The plan is to get Org::Company to purchase "coins" (We need a name for this, so it's different from credits in Gig) where they can do actions. These actions span what was described earlier for Careers, Ads and Job Boost.
External Clients buy packages of coins/credits just like Fastjobs Actions spend X number of coins
Internally the system will track two balances only:
- (1) the customer’s total deferred revenue balance
- (2) the customer’s total remaining service entitlements (coins).
Coins are treated purely as a count of remaining job board actions, not as individually priced units.
- When coins are used, the system recognises revenue proportionally, by releasing a fraction of the deferred revenue based on how many coins were consumed relative to the remaining balance
- The system does not track or tag coins to their original purchase price, because all remaining coins represent the same service obligation.
Actions
- purchase an Ads::Inventory (i.e. space on the website) with coins for X days.
- purchase a Careers::Job post to show on the website
Example (coins) Customer buys 10 coins for $10 Customer later buys 10 coins for $8
System records:
- Deferred revenue = $18
- Remaining coins = 20
Customer then uses 4 coins (e.g. job post + boost)
At time of usage:
- Deferred revenue = $18
- Remaining coins = 20
- Revenue recognised = 4 × (18 / 20) = $3.60 System updates to:
- Deferred revenue = $14.40
- Remaining coins = 16
No coin is tagged to a purchase price.
- Coins are just a count of remaining job board actions.
- Revenue is released proportionally from the deferred revenue balance.
Finance requirements: Gig
Org::Company purchase gig credits. We charge a "platform usage fee" on top of the gig credits purchased.
Current finance process
- When a customer pay up, jod gig credits gets recognised as liability to customer and the service fee gets recognised as a deferred revenue
- In our accounting system (Xero), we do not track the gig credit liability by customer. It all goes into one account code named "Jod credits"
- And the deferred revenue all goes into one account code named "Deferred income"
- Every Monday to saturday, Ops will send a list of daily payment summary (posted payments) to finance. Usually comprising of the prior day's completed jobs
- Based on the list, finance will process the net payment to member and records the following daily entry to Xero:
- Total Jod gig credits used
- Total insurance deducted
- Total insurance sponsored
- Total paid break sponsored
- The entries are recorded as a lump sum to "Jod credits" account and we do not track the gig credits used by customer again here.
- Every month end, we will have a compiled list of paid jobs for the whole month which we will then input the service margin based on the latest invoice (Refer to sheet 1, column AG)
- The total revenue to be recognised in the month will be a total of column AH in sheet 1.
- This way may not be 100% accurate as we are recording the gig credits used based on the daily payment file but it's a way we feel comfortable as we need it to be tied to payment. There may be certain factors affecting the variance between the gig credits that we capture vs the actual gig credit deducted from the system such as CAFS. But we also do capture the adjustment to CAFS.
- Every month we also do a reconciliation between our closing Jod gig credits in Xero vs the total of customer's SOA. Our variance range between 1-5k every month
Example Finance CSV Report
Job ID Number,Job Applicant User Account ID,Job Applicant First Name,Job Applicant Last Name,HQ Company,Outlet Name,Job Applicant Clock In Date,Job Applicant Clock In Time,Job Applicant Clock Out Date,Job Applicant Clock Out Time,Meal Break Time (Hours),Sponsored Meal Break Time (Hours),Sponsored Meal Break Time Amount ($),Total Hours Worked,Total Hours Worked (Hours),Payment Amount (Rate) ($),Job Applicant Total Wages Due ($),Insurance Premium Payable ($0.35 x Total Hours Worked),Sponsored Insurance Premium,Additional Allowance ($),Allowance Deduction,"Net Payable to Member
(Total Wages - Insurance Premium + Additional Allowance)",Job Applicant Bank Name,Job Applicant Name As Per Bank Account,Job Applicant Bank Account No,Other adjustment,Finance adjustment,Credits adjustment (With CAF),Final Amount Paid out,Payment Vouchers (2024),Bank Reconciliation Date,Credit Adjustment ID,Platform Fee Percentage,
578470,85829,Snow,John,ABC PTE. LTD,Crispy Prata Orchard (Orchard/Taka),1/11/2025,1/11/2025,1/11/2025,1/11/2025,01:00,00:00,0,9,09:00,15,135,3.15,0,0,JOD,131.85,TRBUSGSGXXX,Jon Snow,123456789,,,,131.85,PV-2025-000686,3/11/2025,,20%, 27.00
577475,61082,Ann,Mary,Restaurants Pte Ltd,Shisen Hanten,1/11/2025,1/11/2025,1/11/2025,1/11/2025,00:00,00:00,0,5,05:00,14,70,1.75,0,0,JOD,68.25,DBSSSGSGXXX,Mary Ann,543543543,,,,68.25,PV-2025-000686,3/11/2025,,20%, 14.00
External
Org::Company purchases 100 credits with a 20% platform fee
1 credits = SGD 1 in wage paid.
- Credit Cost: SGD 100
- Platform Fee (20%): SGD 20
- Sub Total: SGD 120
- GST: SGD20 * 0.09 = SGD 1.80
- Total payable: SGD 121.90
Internally
Org::Company posts a job for SGD 10/hr for 8 hours.
If Talent is a Bronze member.
- Total Wage: SGD 80
- Insurance: 8 * 0.35 = SGD 2.80
- Net Payable to Talent: 80 - 2.80 = SGD 77.80
If Talent is a Silver or Gold member.
- Total Wage: SGD 80
- Insurance: 8 * 0.35 = SGD 2.80
- Net Payable to Talent: SGD 80
Note on insurance:
-
if the talent is silver and gold loyalty member level, insurance is covered by us, which I assume would be a cost.
-
requires system to follow SFRS(I) 15, but only for the principal liability portion (unused gig credits).
- because it is refundable to customer, it is a liability to us. And this liability will be reduced everytime a job is completed.
- don't record individual entry as that would be too many.
- they do a lump sum daily entry
-
tricky part is monitoring the actualised revenue when the customer purchased 100 credits at 20% fee and another 100 credits at 15%
- tricker if a credit spend overlaps both 20% bucket and 15% bucket.
- best way would be the FIFO method where the system allows for us to enter the service margin % alongside each top up.
- it tracks the usage and can let me know for each credit deducted, how much is the actualised revenue
Ideas on schema design
- Do not assume this is correct. Be critical and determine the best way to design this, considering that Billing needs to support Workforce and Gig.
- Each company would have a list of different "gig credit buckets" based on the platform fee.
- we can project the aggregate into the main account table for fast read access.
- We would need to handle reservations that span multiple buckets (e.g. 10 credits in 20% bucket, and 8 credits in 15% bucket)
- each time the company purchases more credit at the same platform fee, should we create a new "bucket" with the same platform fee?
- company A purchases in order:
- 1 Feb 2026: 100 credits at 20% platform fee
- 10 Feb 2026: 200 credits at 15% platform fee
- 30 Feb 2026: 100 credits at 20% platform fee
- should we have 3 buckets (one bucket for each purchase) or should we have 2 buckets (1 for 20% fee and 1 for 15% fee)
- how would this affect financial reporting?
- company A purchases in order: