Case Study

Payment Service

Live

A shared subscription billing microservice for the ProjectLabs ecosystem. Multi-app support, configurable plans with durations, QRIS payments via Pakasir, webhook-driven activation, grace period management, and daily subscription checks.

App

Overview

Payment Service is a centralized billing engine that handles subscriptions for all ProjectLabs products. Each app (BukuBengkel, future products) integrates via API keys and webhooks — create subscriptions, generate invoices, process payments, and notify apps of status changes. No Stripe, no credit cards. Just QRIS — scan and pay.

The Problem

Indonesian small businesses don't use credit cards. Stripe isn't an option. The existing Indonesian payment gateways (Midtrans, Xendit) are overkill for a simple subscription model and their pricing eats into small transactions. We needed a lightweight service that generates QRIS codes, tracks invoice status, and tells each app when a tenant has paid or expired — without coupling billing logic into every product.

What Was Built

A standalone billing microservice with:

  • Multi-app support — each app registers with an API key and webhook URL. Invoices and subscriptions are scoped per app
  • Configurable plans — define plan name, price, duration in months. Each app can have multiple plans (e.g., Bulanan, Tahunan)
  • QRIS payments — generates QRIS strings via Pakasir API, rendered as QR codes on each app's pay page
  • Webhook-driven activation — Pakasir sends payment confirmation, the service updates invoice status, activates the subscription, and notifies the target app via webhook
  • Grace period management — when a subscription expires, it enters a grace period (configurable days) before suspension. Daily cron job handles status transitions and auto-generates renewal invoices
  • Admin dashboard — manage apps, plans, subscriptions, and view invoice history from a single panel

Integration Flow

When a tenant registers on BukuBengkel and selects a plan, BukuBengkel calls the Payment Service to create a subscription and invoice. The service returns a QRIS payment URL. BukuBengkel renders a QR code on the pay page. The tenant scans and pays. Pakasir sends a webhook to the Payment Service, which marks the invoice as paid, activates the subscription, and fires a webhook back to BukuBengkel with the new status. BukuBengkel updates the tenant's access. The whole cycle runs without manual intervention.

Technical Notes

  • Stack — Next.js 16 (App Router), React 19, TypeScript, Drizzle ORM, SQLite (better-sqlite3), better-auth
  • Payment gateway — Pakasir API for QRIS transaction creation and webhook callbacks
  • Cron — daily check script that evaluates all subscriptions: expiring soon (generate renewal invoice), expired (set grace period), grace period over (suspend)
  • Webhooks — outgoing to registered apps on status change, incoming from Pakasir on payment confirmation
  • Self-hosted — co-deployed on the same VPS via PM2 + Caddy, port 5005

View Project All projects →