Payments and billing

Sluurp includes billing: what you charge for, who owes what, invoices, and payments, online, by bank transfer or in cash. Anyone who bills people can use it, from a school's canteen to a club's membership.

The pieces

IssuerWho bills: a school’s committee, the club that runs after-school care. Each has its own bank account, invoice numbers and online payments
ItemA thing in an issuer’s catalogue, with a price and a unit: each, day, month or year
AccountWho a charge is for, such as a pupil. Who pays for an account comes from the app’s own data (custodies by default), so billing keeps no second list of families
ChargeOne thing billed to one account: posted by hand, for a whole class at once if need be, or made from a subscription
SubscriptionCharged each period: hot meals on Mondays and Thursdays, a monthly fee, a year of chess club
InvoiceAn account’s open charges, with a number, a due date, and a structured reference that a bank transfer is matched by
PaymentSettles invoices: a transfer, cash at the desk, or online
WalletMoney paid ahead and spent as it goes. A reserved meal is taken from it at once, and put back if cancelled in time

Every amount is a whole number of cents, never a float.

Billing a period

app.ts
const billing = sluurp.billing;

await billing.charge({ account: pupil.id, item: trip.id });   // one charge
await billing.run({ dry: true });                             // what a run would invoice
await billing.run({});                                        // count subscriptions, invoice open charges

remind(id) mails a reminder for an unpaid invoice, and cancel(id) cancels one.

Being paid

  • Online, with Stripe or Mollie, whichever the issuer uses; both offer cards and Bancontact. billing.pay({ invoice, back }) answers a url to send the person to, and they come back to back with ?paid=<id>. The provider’s webhook records the payment.
  • By transfer: paste the bank statement’s lines into billing.match(lines), and each is matched to its invoice by its reference. dry shows the matches first.
  • In cash: billing.record(payment) at the desk.
  • Topping up a wallet: billing.pay({ account, issuer, amount_cents }), between €1 and €1,000.

Who may do what

Managing (the catalogue, charges, runs, recording payments) is a rule, manage, in the billing settings: without one, only superusers may. Everyone else sees the accounts they pay for, and their own. billing.certificate(account, year) gives what was paid in a year that may be deducted from tax, per issuer: in Belgium, childcare for a child under fourteen.

The admin UI’s Payments screen sets up the Stripe and Mollie accounts.