Billing
Billing lets users and organizations buy add-ons that raise their quota limits, from an Add-ons tab in their settings. Arbinel sells them as monthly subscriptions through Creem, which acts as merchant of record and handles payment, tax and invoices. Forgejo has no billing feature, so everything on this page is specific to Arbinel.
Billing builds on quotas. Read Quotas first, and CI quota or Egress quota if you sell those.
How add-ons map to quota limits
Section titled “How add-ons map to quota limits”Each product names one or more quota subjects and an amount per unit. While an add-on counts, Arbinel adds the amount times the number of units to every rule of the owner that names one of those subjects.
- An add-on only raises rules that exist, and it matches subjects exactly.
It never raises the
[quota.default]or[quota.ci.default]fallbacks. - The Add-ons tab only offers a product to an owner who has a rule, not already unlimited, that names one of its subjects.
- A rule naming several of the product’s subjects is raised once.
- Several add-ons on one rule add up, and they add to any quota grants.
Naming a parent subject and its children in one product sells a shared pool.
With SUBJECTS = size:assets:all, size:assets:packages:all, size:assets:attachments:all, the parent’s rule limits the total, and every per-type rule is raised along with it.
An add-on counts while Creem reports its subscription as active, trialing, past_due, unpaid or scheduled_cancel.
past_due and unpaid mean Creem is still retrying a payment, and scheduled_cancel runs until the paid period ends.
Any other status, including a status Creem adds later, does not count.
Set up Creem
Section titled “Set up Creem”- Create a store in Creem. Start in test mode, which Arbinel uses by default.
- Create one recurring monthly product per add-on you want to sell. The product’s price is the price of one unit. Arbinel reads prices from Creem, so a price change there needs no configuration change.
- Create an API key, and keep it for
[billing.creem] API_KEY. - Register a webhook endpoint for
<ROOT_URL>/-/billing/creem/webhook, withcheckout.completedand thesubscription.*events. Keep its signing secret for[billing.creem] WEBHOOK_SECRET. - Make sure your quota groups have rules that name the subjects your products raise.
- Configure Arbinel as below and restart it.
- Buy each add-on once in test mode, then set
TEST_MODE = falsewith your live API key and webhook secret.
Configuration
Section titled “Configuration”[billing] and [billing.creem]
Section titled “[billing] and [billing.creem]”| Key | Default | Meaning |
|---|---|---|
[billing] ENABLED |
false |
Sells add-ons. Needs [quota] ENABLED. While it is off, nothing is sold and no limit differs from what the quota groups say. |
TEST_MODE |
true |
Sends every request to Creem’s test API, test-api.creem.io. |
API_KEY |
none | Creem API key. Required while billing is on. |
WEBHOOK_SECRET |
none | Signing secret of the webhook endpoint. Required while billing is on. |
[billing.product.<key>]
Section titled “[billing.product.<key>]”One section per product.
The key after billing.product. is what Arbinel stores with each add-on, so you can re-price a product or move it to another Creem product without a data migration.
Use lower-case letters, digits, - and _.
| Key | Default | Meaning |
|---|---|---|
CREEM_PRODUCT_ID |
none | The Creem product. Required, and unique across sections. |
NAME |
the key | Name shown on the Add-ons tab. |
TYPE |
fixed |
fixed for a product sold in units, metered for metered bandwidth. |
SUBJECTS |
none | Comma-separated quota subjects. All time: subjects or all size subjects, not both. Required. |
AMOUNT |
none | What one unit adds: a duration such as 1000m for time: subjects, a size such as 10GiB otherwise. Required. |
MAX_UNITS |
20 |
The most units one owner can buy of the product. |
time: limits reset every month, so a CI add-on adds its amount per month.
A storage add-on raises a limit that does not reset.
Startup checks
Section titled “Startup checks”Arbinel refuses to start when:
- billing is on and
[quota] ENABLEDis false, orAPI_KEY,WEBHOOK_SECRETor every product section is missing, - a product section is invalid, even while billing is off, so you notice a broken section as soon as you add it,
- a subject is unknown, or counted in a different unit than
AMOUNT, - a product raises
time:subjects while[quota.ci] ENABLEDis false.
It only logs a warning when no quota rule names a product’s subject, because the Add-ons tab then offers the product to nobody.
Removing a product section stops its add-ons from raising anything, and the Add-ons tab shows them as No longer sold. Creem keeps billing them until they are cancelled, so cancel them in Creem first.
Metered bandwidth
Section titled “Metered bandwidth”One product may have TYPE = metered.
It is billed by the download traffic above the owner’s free egress limit, not by units.
It needs the egress quota to be fed by your edge proxy.
| Key | Default | Meaning |
|---|---|---|
SUBJECTS |
egress:all |
The only subject a metered product can raise. |
EVENT_NAME |
none | Event name of the Creem meter. Required. |
PRICE_PER_GIB |
none | Price per GiB in cents, excluding VAT. Required. |
DEFAULT_CAP |
1000 |
Monthly cap in cents, excluding VAT, for an owner who has not set one. |
MAX_CAP |
100000 |
The highest monthly cap an owner can set, in cents. |
MIN_CHARGE |
500 |
Cents of usage to collect before reporting it, so Creem’s fee per charge stays small. |
AMOUNT and MAX_UNITS do not apply and are refused.
How bandwidth is billed
Section titled “How bandwidth is billed”The owner sets a monthly cap in money.
Arbinel raises their egress:all rules by the traffic that cap buys at PRICE_PER_GIB.
With the defaults, a cap of 1,000 cents at 5 cents per GiB buys 200 GiB.
Every hour, the billing_report_usage cron task calculates each owner’s billable traffic for the month.
This is the traffic they downloaded above their egress limit without add-ons, up to the amount their cap buys.
Traffic past the cap, served while the edge proxy’s deny list catches up, is never billed.
Arbinel waits until the usage is worth MIN_CHARGE, or the subscription ends, and then sends it to Creem as events named EVENT_NAME.
For the first six hours of a month, it also reports the previous month, for traffic the aggregators file late.
Each owner, whether a user or an organization, gets its own Creem customer for bandwidth, under a billing e-mail entered at checkout. The billing e-mail must be one of the buyer’s verified addresses, and for an organization it must not be the buyer’s primary address.
Set up the metered product in Creem
Section titled “Set up the metered product in Creem”The product needs exactly one usage price, which Arbinel checks before each report:
- settlement mode postpaid,
- no free allowance, because the egress quota rule is the free allowance,
- a unit price per MiB equal to
PRICE_PER_GIBdivided by 1024, - a meter whose event name is
EVENT_NAME, which sums the event propertymib.
If any of these do not match, the cron task fails with an error and reports nothing until you fix it.
Arbinel computes the owner’s limit from PRICE_PER_GIB, so the price in Creem must match it.
Webhook and cron tasks
Section titled “Webhook and cron tasks”Creem signs each webhook body.
Arbinel checks the signature and refuses deliveries older than 24 hours.
It treats a delivery only as a signal, and records the subscription as Creem’s API reports it.
As a result, duplicate or out-of-order deliveries cause no problems.
If Creem’s API cannot be reached, Arbinel answers 503 so that Creem retries.
| Task | Schedule | What it does |
|---|---|---|
billing_reconcile |
@midnight |
Synchronizes add-ons with Creem in case webhooks were lost. Records subscriptions that carry Arbinel’s metadata but have no row yet, corrects drifted rows, and stops counting add-ons Creem no longer knows. |
billing_report_usage |
@every 1h |
Reports metered bandwidth to Creem. Does nothing without a metered product. |
If Creem does not know most of the add-ons Arbinel counts, billing_reconcile changes nothing and fails.
This usually means that the API key belongs to another store or to the other mode.
In that case, cancelling every add-on would be wrong.
Administration
Section titled “Administration”The admin page of a user account lists its add-ons, with product, quantity, status, period end and Creem subscription.
An owner with add-ons that Creem still bills cannot be deleted. That also applies to a user who bought add-ons for an organization. Cancel the add-ons first. Deletion works once they have ended, which is at the end of the paid period. Bandwidth needs up to two more days after it ends, to bill the last downloads.
Usage and add-ons describes billing from the owner’s side.