When I started Algebra, I wanted to sell just one thing: an hour of lessons. The site was supposed to show the price and let people pick a time in the calendar, and the rest was to happen the way it always had, between me and the student. Over time it turned out that behind every lesson there is a whole small system: a price list, packages, discounts, instalments, payments, access to materials, and someone who manages all of it. This post is about that less visible part of the platform.
A price list in an ordinary file
Algebra's first price list was not in the database but in a settings file. In it I stored lesson lengths and their prices, packages of several lessons with a discount, per-person prices for group lessons and the number of instalments. The site read the file when it started up and built the offer from it.
At first that was convenient, because I had the whole price list in one place, as readable as a shopping list. Over time, though, it started to chafe, because every price change meant editing the file and releasing a new version of the site, and one misplaced comma could leave the site unable to read any of its settings. In the spring the price list moved into the database along with the rest of the settings, and since then changing a price no longer needs a new version of the site.
The same idea, content and prices that the owner changes without a programmer, kept coming back in later projects. On the Duostrings site the whole content was already editable, and in the end it grew into Panel Biznesu, where a business owner edits their website directly in the preview.
The admin panel
The admin panel, where I see the bookings and reply to messages, has been part of Algebra since day one. Its address is not public, and it is not in the site's code but in the server settings. Over time more tabs were added: a calendar with blocks and holidays, a list of students, payments, and in the summer a separate finance section.
Along the way I also managed to build too much. In February I created an elaborate system of roles and permissions, with a table of who can do what, an event log and role assignment in a separate window, like in a large corporate application. In July I cut out almost all of it, and what remained were two simple roles: administrator and student. With one person running the platform, a system like that was overkill, and I had to keep it in mind with every change.
Premium, which I did not think about at first
At first Algebra was only meant to schedule lessons, and the tasks and games were an extra for my students. But once the collection of tasks grew to several thousand items with step-by-step solutions, it became something people could use without having lessons with me.
So in July a Premium subscription arrived, at PLN 29.99 a month. The free version keeps a daily task limit, and Premium removes it. In the admin panel I can grant Premium by hand, with a start and end date, which is useful for students with a lesson package or for testing. The first weeks of Premium were mostly about plugging holes, by the way: I fixed a bug in revoking access, and a free limit that could be got around.

The tutor account
At the end of July a third role was added. The whiteboard I teach on caught the interest of other tutors, so Algebra got a teacher panel in which everyone has their own students and their own whiteboards, and nothing mixes with mine. It was the first moment the platform stopped being mine alone, so every feature had to be rethought in terms of who sees what.
HotPay, Stripe and payments that never arrived
The first payment provider on which I learned how it all works was Stripe, connected back in the first version of Algebra. After a few days I switched to HotPay and stayed with it. An online payment has two halves, and the second one is easy to forget. The first is the customer going to the provider's page, paying there and coming back to Algebra. The second is a separate notification that the provider sends straight to Algebra's server, and only that notification tells the application that the money has really come in.
At the end of July it turned out that only the first half worked for me. The database held a dozen or so payments that had been started and not a single one that had been completed. I built myself a tool that creates a PLN 1 test payment and immediately ran into the first surprise: the provider added PLN 6.99 to the basket, more than the payment itself. It was an optional buyer protection programme from another company, ticked on the payment page. It is not a shop fee and not a single zloty of it reaches me, but the customer sees a higher amount than in the offer, so a warning appeared before the payment button saying that this field can be left empty.
The second surprise was a message saying the secret key that links the shop to the payment provider was missing. While looking for the cause, I found a real bug, because the key on the server differed from the one in my settings, and I fixed it. The message kept coming back, though, until I checked and found that it simply appears when the payment link is too old. The conclusion was simple: the link has to be created right before the payment, not an hour earlier.
Finally I paid PLN 5 with BLIK. The provider showed that the payment had succeeded, while my application still saw it as pending. Sentry, a tool that collects errors from the server and which I had just switched on, recorded no notification from the provider at all. The culprit turned out to be the notification address set in the HotPay panel: it pointed to Algebra's website instead of the server that stores the data, so the notification landed in the part shown in the browser, which had no idea what to do with it. After I corrected the address, the notifications started arriving, and I closed the one payment that had got stuck along the way by hand.
What I took away from it
Three conclusions came out of this story. A test with real money, even PLN 5, tells you more than a week of reading code. The notification address is just as important as the code itself, even though it is set in a completely different place, in the payment provider's panel. And finally, before fixing a bug, it is worth checking that it really is the one causing the trouble, because in this story a real but innocent bug nearly hid the actual cause.
The whole series
This is the fourth part of a series about how Algebra i Spółka came to be:
- The first Algebra: Flask, templates and the PHP I quickly gave up on
- Bookings that disappeared with every deployment
- Algebra from scratch: from Flask templates to React and TypeScript
- A price list in a file, Premium and my first payment gateway
- Tasks, a formula sheet and drawings that would not add up
I describe what the whole thing looks like today in the post Algebra i Spółka from the inside.

