Mockup Image simulating MinsaPay on Macbook Pro
Analytics Data Value
Number of Users 400
Number of Transactions 2,900
Total Payment Amount ₩4,604,210 (About $4,000)
Total Transaction Amount ₩17,319,300 (About $14,400)

TL;DR: MinsaPay is a payment system built for the Minjok Summer Festival. It works like a prepaid tap-to-pay card. Every source and anonymized transaction data is available on GitHub. This is how our team got to build a payment system for our school festival. The official name of the product is MinsaPay, where the prefix "Minsa" came from the acronym (민사고, pronounced as Minsa-Go) of my school's name (민족사관고등학교, a.k.a Korean Minjok Leadership Academy).

So why does a High School Festival need a payment system at the first place?

Like any other school, KMLA (Korean Minjok Leadership Academy) had a summer festival. Students open booths to sell foods and items they created. We also screen movies created by our students and host dance clubs. One of the oldest traditions of the festival is the water party in the afternoon.

Because there were a lot of products being sold, it was hard to use regular paper money (a later analysis made by the MinsaPay team confirmed that the total volume of payments reached more than $4,000.) That made our student council to create proprietary money, called the Minjok Festival Notes. The student council had a dedicated student department act as a bank system, to publish the Notes and monitor the flow of the currency. Also, the Minjok Festival Notes acted as a memorabilia of the festival since each year's design had its own unique design.

Image simulating Minjok Festival Currency 2018
Minjok Festival Note Design for 2018. The actual note had photos of the KMLA student council members at the center of the bill. Approximately, the yellow one is worth $5, the green one is worth $1, and the red one is worth $0.5.

But there were problems.

  • First, it was not eco-friendly. There were thousands of notes printed and disposed of for just a single day in the entire year. It was a waste of resources.
  • The water party mentioned above made a problem as well. Minjok Festival Notes were made out of nothing special, just ordinary paper. That made the notes extremely vulnerable to water, and students lost a lot of money after the water party.

Eventually, the KMLA students sought for a way to resolve all of these issues.

Idea

The student council first offered me to develop some sort of payment system. Because I have thought about the issue beforehand, I thought it made a lot of sense. I instantly detailed out about the feasibility and possibilities of the payment system. But even after designing the system in such great detail that I could immediately jump into the development, I turned down the offer.

I believe in the social responsibilities of the developer. Devs should not be copy-pasters who just meet the technical requirements and deliver the product. They are the people with one of the most enormous potential to open an entirely new horizon of the world by conversating with computers with the medium called technologies. Therefore developers start to possess the real power to impact the daily lives of the rest, and it is their bound responsibility to use that power to enhance the world. That means developers should understand how impactful a single line of code can be.

Of course, I was tempted. But I have never done a project where security was the primary interest. It was a considerable risk to start with a project like this without any experience or knowledge in security. Many what-ifs flooded my brain. What if a single line of code makes the balance disappear? What if the payment record gets mixed up? What if the server was hacked? More realistically, what if the server gets down?

People praise audacity, but I prefer prudence. Bravery and temerity are just one step away. A financial system should be flawless (as much as possible.) It should not only be working but also be performing so resiliently under any condition. It didn't seem impossible, but it was being too naïve to believe nothing will happen as I was a total newbie in security. So I turned it down.

Payments using Google Forms?

The student council still wanted to continue the project. I thought they will out-source the project to some outside organization. It sounded better since they will at least have some degree of security. But the council thought differently. They were making it themselves. With Google forms.

Back when I was designing the system, the primary issue was about payment authorization. The passcode shouldn't be shared with the merchant, while the system could correctly authorize and process the order. That way, the users can only use the deposited money in their own accounts. This authorization should happen in real-time. But I wasn't able to think the way to nail the real-time authorization with Google Forms. I asked for more technical details from one member of the student council. The idea was like the following.

  • Create one google form per each user (We have about 400 users in total)
  • Create QR codes with links to the google form (So it's 400 QR codes in total)
  • Create a wristband with the QR code and distribute them to the users.
  • Show that wristband when purchasing something.
  • The merchant scans the QR code and opens the link in incognito mode.
  • Input the price and the name of the booth.
  • Confirm with the user(customer) and submit the response.
  • Close the incognito tab.

So the idea was using the unique address of the google form as a password. Since the merchants are supposed to use incognito mode, there should be a safety layer to protect the user's google form address (in theory.) They will need to make a deferred payment after the festival. But as a developer, this approach had multiple problems.

  • How are we going to manage all 400 google forms?
  • Intended or not, people will lose their wristbands. To calculate the spending, in that case, we will need to note the owner of the wristband in every google form. If we do so, can we deliver those QR codes to the correct owner?
  • If the merchant doesn't use incognito mode, it will be hard for an ordinary person to tell the difference. If that happens, it is possible to attack the exposed google form by submitting fake orders. We could also add a "password," but in that case, we cannot stop the customer from providing a false password and claiming that they were hacked by someone else.
  • If the merchant has to manually select the booth and input the price, there will be occasions where they make a typo. Typo in the price value could be fixed relatively quickly, but a typo or misselection in the booth value would be a pain to find out who made a mistake and what was the original order. Imagine there were 20 wrong booth values. How are we going to trace the real booth value? We could guess, but would that sort of record have its value as a reliable data?
  • How are we going to make the deferred payment? How are we going to extract and merge all 400 of the google forms response sheets? Even worse, the day after the festival is a vacation. People care about losing money but not so much about paying their debts. There could be students who just won't come back. It would be excruciating to notify all those who didn't pay. But if the money is prepaid, the solution is comparably easy. The left balance could simply be deposited to their phone number or bank account. We don't need to message dozens of students; just we could do the work by ourselves.
  • The google form will be made with the student council's google account. That Google account will have restricted access, but still, there will be few students who will be working together to create all 400 google forms. If someone manipulates the google form for their own benefit, can we track who made the rogue action?
  • Can this all be free from human error?

It could work in an ideal situation. But it will accompany a great deal of confusion and entail a noticeable discomfort on the festival day. That made me think that even my idea had its own risks, mine would still be better. So I changed my mind.

Development

Fortunately, I met a friend with the same intent. Our vision and idea about the project aligned as well. I explained about my previous concept, and we talked to each other and co-developed the actual product. We also met at a cafe several times. I set up and managed the DNS and create the Front end side. Below are the things we thought about while creating the product.

  • We won't be able to use any payment gateway or third-party payment service since we are not officially registered, and we are going to use it for a single day. Some students didn't own smartphones, so we won't be able to use Toss or KakaoPay as well (Both are well-known P2P payment services in South Korea, just like Venmo). Therefore there cannot be any devices on the client-side. We would need to install computers on the merchant's side.
  • It is impossible to build a completely automated system. Especially in dealing with cash, we would need some help from the student council and the Department of Finances and Information. Trusted members from the committee will manually count and deposit the money.
  • At least, there must be no error on the merchant and customer field, since they will be the most difficult error to fix later. We cannot expect that people will make no mistake. Instead, we need to engineer an environment that no one can make any error even if they wanted to.
  • The booths can be congested. If every customer needs to input their username and password every time, that will cause a severe inconvenience. For the sake of user experience, some sort of one-touch payment would be ideal.
  • For that, we could use the Campus ID card. Each campus ID card has a student number (of course) and a unique value for identifying students at the school front door. We could use this student number as the username and the unique value as a password. Since this "password" is guaranteed to be different for every student, we would only need the password to identify each student.
  • In the end, the payment system would look like a prepaid tap-to-pay card. Each account will be connected with the owner's student ID. Students could withdraw the left money after the festival.

We disagreed with two problems.

One was about the platform. While he insisted on using Windows Executable programs, I wanted the system to be multi-platform and therefore asked to use web apps. As you might expect, I use a Mac.

The other was about the method of reading data from the Campus ID card. The Campus ID card had an RFID chip and a bar code, both storing the same value. If we chose RFID values, we had to purchase ten RFID readers, spending an additional $100 in total. I insisted on using the embedded webcam on laptops to scan the barcode at first because MinsaPay was on a pilot experiment at that time, and I thought that such an expense would make the entire system questionable in terms of cost-effectiveness. (Such as: "Wait, we need to spend an additional $100 even though we have no idea if the system will work?")

We chose web and RFID, conceding one for each. I agreed to use RFID after knowing that using a camera to read bar codes wasn't that fast and efficient.

Mockup Image simulating MinsaPay on iPhone
Main Home, Admin Page, and Balance Check Page of the actual product.

And... that happened.

Remember that one of the concerns was about the server going down?

On the festival day, senior students have to self-study at school. Then at one moment, I found my phone had several missed calls. The server went down. I rushed to the festival and sat at one corner, gasping, and trying to find the reason. The server was intact, but the database was not responding.

It was an absurd problem (well, no problem is absurd per se, but we couldn't hide our disappointment after figuring out the reason.) When we constructed our database, we thought that the free plan would be more than enough. However, the payment request surged at one moment and exceeded the database free tier. We purchased a $9 plan, and the database went back to its work.

While the server wen down, each booth made a spreadsheet and wrote down who needs to pay how much. Afterward, we settled the problem by opening a new booth for making deferred payments.

The payment log showed that the server went down right after 10:17:55 AM and went back to work at 10:31:10 AM. It was self-evident yet intriguing to find out that the payments made per minute were around 10 to 30 before the crash, but went down to almost zero right after restoring the server. If you are interested, please look here.

Image simulating MinsaPay Server Downtime Logs
The server went down for 13 minutes and 15 seconds after payment #1546 due to exceeding the database free tier.

Results

Name Address
MinsaPay MinsaPay.com
MinsaPay Codebase github.com/minsapay/server
MinsaPay Payment Logs github.com/minsapay/transaction-data-2019
MinsaPay in action. I used MinsaPay to purchase an $8 t-shirt. It is even compatible with Internet Explorer as well (wow)

#1. MinsaPay

The entire codebase for MinsaPay is available on GitHub. Though, I must mention that I still question the integrity of this system. During the development, one developer reported a security flaw, which we managed to fix before the launch. There must be other undiscovered flaws in the system; for example, we weren't able to fix the problem in time where a merchant can copy the RFID value and forge fake ID cards (although that is really unlikely to happen).

#2. Payment Data

I wanted to give a more relatable and exciting data for students studying data analysis. Also, I wanted to provide some financial insights for students planning to run a booth the following year. Therefore we decided to open the entire payment data. There was one problem—and it was about data privacy.

So I wrote a short script that would anonymize any personal data. If any CSV file is provided, it will anonymize a selected column. Identical values will have the same anonymized value. You can check the anonymized data here.

import pandas as pd
Dataframe = pd.read_csv('raw.csv')

def anonymize(df, targetColumn):
    anon = {}
    id = 0
    for x in range(len(df)):
        user = df.loc[x, targetColumn]
        if user in anon:
            df.loc[x, targetColumn] = anon[user]
        else:
            if id < 10:
                unknown = "#00" + str(id)
            elif id < 100:
                unknown = "#0" + str(id)
            else:
                unknown = "#" + str(id)
            anon[user] = targetColumn + str(unknown)
            id += 1
            df.loc[x, targetColumn] = anon[user]

anonymize(Dataframe, 'user')
anonymize(Dataframe, 'booth')

Dataframe.to_csv("anonymized.csv", mode='w')

Developer Note

If you are ever going to use this system, I strongly recommend you to thoroughly audit the entire code, or more preferably, rewrite them. FYI, the MinsaPay is under the MIT license.

What I learned

There is a lot of space for improvements.

First of all, there are codes with a lot of compromises. I remember we made a lot of trade-offs to not miss the product deadline (the festival day.) We also wanted to include safety features such as canceling payments, but we weren't even to start those. I thought a little bit more time would make the entire product better, but at the same time, I thought that was the current limit of my dev abilities.

Since I wasn't comfortable with the security of the system, I kept the repository quiet and undisclosed at the beginning. I found myself self-contradicting as I already knew that security done by hiding is not the best practice.

Also, we were not free from human errors. RFID values were long strings of digits, and there were few mistakes that someone would input that in the charge amount, therefore making the charge amount something like Integer.MAX_VALUE. We could've added a simple confirmation prompt, but at that time, we didn't know this would happen.

In hindsight, it was such a great experience for me who never done large scale real-life projects. I found myself compromising even after acknowledging the anti-patterns. I also understood that knowing and doing are two completely different things since knowing has no barriers, but doing accompanies an extreme level of stress both in time and environment.

Still, It was such an exciting project.

I would like to personally thank everyone who made MinsaPay possible.

  • Jueon An, a talented developer who created MinsaPay with me
  • KMLA student council and Department of Finances and Information, who oversaw the entire MinsaPay Initiatives.
  • Open-source developers who reported the security flaws.
  • Users who experienced server failures during the festival day.
  • and the 400 users of MinsaPay

Thank you all.

You’ve successfully subscribed to Sunghyun Cho
Welcome back! You’ve successfully signed in.
Great! You’ve successfully signed up.
Your link has expired
Success! Check your email for magic link to sign-in.