Skip to content
02Web Development · Full-stack Development · 2026Featured

International Symposium on Functional Materials Conference Website

Summary

The registration, payment and peer-review platform for ISFM 2026, an international academic conference. As sole developer I built both halves: payment by Midtrans or bank transfer, in rupiah or dollars, and a multi-round review workflow for authors, reviewers and editors. It's live and served 191 participants from 12 countries.

Role
Sole developer
Database to deployment
Timeline
Sep 2025 – Aug 2026
Freelance, remote
Client
International academic conference
ISFM 2026
Stack
Next.js · PostgreSQL
Prisma · Midtrans · Vercel
Stack
  • Next.js
  • React
  • Prisma
  • PostgreSQL
  • TypeScript
  • Auth.js
  • Vercel
  • Midtrans
  • Cloudinary
  • Resend
  • Recharts
isfm2026.labmateconf.com
The public conference site.
(01)

The problem

An international conference needed one place where participants register, pay and submit their abstracts, and where reviewers and editors decide which ones are accepted.

Participants came from many countries. Some pay by card through a payment gateway; many, especially in Indonesia, still pay by bank transfer. Overseas participants think in dollars while the conference account is in rupiah, and the fee depends on who you are (professional or student) and when you register (early bird or late).

The abstracts need real peer review: an editor assigns a reviewer, the reviewer scores the paper, the editor decides, and the author may revise more than once before the paper is accepted or rejected. Reviewers and editors are invited, not self-registered, and submitted files stay confidential until accepted.

  • Registration with fees by category and registration period
  • Payment by Midtrans or manual bank transfer, in IDR or USD
  • Abstract submission with multi-round peer review
  • A dashboard for each role: participants, reviewers, editors and admins
  • Invoices, receipts and deadline reminders by email
  • Excel exports of abstracts, invoices and participants
(02)

How I solved it

I built the whole platform alone, from the database design to deployment. Three decisions shaped it.

01The payment gateway doesn't get to say you paid

A bank transfer is someone claiming they paid, so an admin checks the proof before the invoice counts. A gateway callback that arrives through the participant's browser has the same shape: a claim, over a channel I don't control.

So both payment paths end in the same state, waiting for confirmation, and only an admin can move an invoice from there to paid. Every action also checks that the invoice is in a state the change is allowed from, so a repeated request is rejected instead of applied twice. Each change of status is saved as a history row, which is where the receipt email reads the payment date from.

To catch a payment the browser never reported, reopening an invoice makes the server ask Midtrans directly for that order's status.

actions/transaction/midtrans-success/index.tsTypeScript
// a gateway success only moves the invoice to WAITING_CONFIRMATION;// an admin's accept-payment is the only way to SUCCESSif (transactionExist.payment_status !== "PENDING") {  return { error: "Action not allowed in current invoice status" };} transaction = await db.transaction.update({  where: { id: transaction_id },  data: {    payment_status: "WAITING_CONFIRMATION",    status_history: {      create: { user: { connect: { id: user_id } }, newStatus: "WAITING_CONFIRMATION", oldStatus: transactionExist.payment_status },    },  },});
Trade-off

Every payment waits for an admin to confirm it, so the receipt isn't instant.

02Two ways to pay, one price worked out on the server

Picking only one payment method would have cost registrations, so there are two: the Midtrans gateway, and manual bank transfer with a proof upload that an admin verifies.

The price is never taken from the browser. The server works out the fee from the participant's category and the active registration period, adds the fee of the chosen payment method, and does it again whenever the participant changes method halfway. Each Midtrans method has its own fee and its own accepted currencies, and overseas participants who pay in dollars get a live USD→IDR conversion, because the account is in rupiah.

When a payment is retried, the invoice gets a letter suffix (INV-123 → INV-123A), because the gateway needs a new, unique order ID every time.

Trade-off

Two payment paths are more work to build and to test than one.

03Review rounds are records, not a status flag

A paper's history is a sequence of rounds. Each round is its own record, holding the revised file, the reviewer's scored review and the editor's decision, so you can always answer "what did round two say?"

Each abstract also carries a "needs action from" flag for the admin, editor, reviewer and author. That turns the workflow into an inbox: everyone opens the dashboard and sees exactly what is waiting on them. Reviewers can recommend a different format (a poster instead of a talk), and deadlines come with reminder emails. Submitted files are served through signed download links, so an abstract stays private until it's accepted.

Review rounds are records, not a status flag
(03)

Results

registered participants
191
countries
12
user roles
5
data models
13

The platform is live at isfm2026.labmateconf.com and ran registration, payment and peer review for ISFM 2026, serving 191 registered participants from 12 countries. Participants, reviewers, editors and admins each work from their own dashboard, and the organisers export abstracts, invoices and participant lists to Excel.

(04)

Screens

Fees by category and registration period.
One invoice, two ways to pay.
Each review round keeps its own file, scores and decision.
Each role sees its own queue.

Next project03 / 06

Advanced Functional Materials Lab Profile Website

Got something in mind?

Let's build the next one.