Tay Za
Back to Garden

MyanTop

2026-06-04
projectdocumentation

MyanTop - Comprehensive Technical Documentation

This document provides a detailed, single-source-of-truth technical overview of the MyanTop Automated Mobile Top-up System. It covers system architecture, database design, automation engine details, REST APIs, administration features, and local development setup.


1. Project Overview

MyanTop is a production-grade, highly secure mobile top-up automation platform built on Next.js 15 and native Supabase (PostgreSQL). The system utilizes a direct REST API for provider interaction and QR code generation while providing users with a comprehensive wallet transaction system and administrators with granular dashboard control.

Core System Features

  • Secure Authentication: Integrated Supabase SSR authentication supporting Passwordless OTP and Google OAuth.
  • Wallet Balance Management: Localized digital wallets enabling customers to hold funds in MMK, request deposits, make top-up purchases, and receive automatic refunds on failed processing runs.
  • Synchronous Direct-to-Memory Processing: Processes checkouts and payments inline synchronously, providing blistering-fast responses and minimal infrastructure overhead.
  • Direct REST API (High Performance): Processes checkout transactions and fetches payment QR codes using a fast, lightweight API client directly interacting with MyEzTopup core backend endpoints synchronously and serverless-compatibly.
  • Myanmar Phone Number Intelligence & Formatting: Dynamic UI formatter that formats numbers as "09 - XXXX - XXXX" automatically. It includes real-time auto-detection of carrier prefixes (MPT, Ooredoo, Atom, Mytel).
  • Brand Asset Overlay: Modernized carrier inputs that dynamically overlay brand logos (Atom.webp, MPT.webp, Mytel.webp, U9.webp) depending on the auto-detected mobile carrier prefix.
  • Dual-Language UI (i18n): Fully internationalized frontend supporting English (en) and Myanmar (my) translations powered by next-intl dynamic routing. Includes new interactive About Us and Support (FAQ + ticket submission) pages. Runtime errors resolved by adding complete pagination keys (previous, next, showingPage).
  • Dual-Method Checkout Options:
    • MMQR Direct: Customers pay via auto-generated MMQR codes scanned in banking apps. Includes a dynamic, real-time 5-minute expiration countdown that enforces session validity and automatically disables payment capabilities upon timeout.
    • MyanTop Wallet: Logged-in customers pay instantly from their pre-funded account. UI includes real-time balance lookup, overdraft protection, and direct balance funding shortcuts.
    • Note: The Payment Method selector features a premium, glassmorphic layout with radial backdrop glows, standard Lucide icons, and state-driven visual indicators.
  • Atomic Transactions & Idempotency: All financial balance mutations (payments, approvals, and refunds) are executed at the database-engine level using native PostgreSQL Stored Procedures (RPCs) to prevent double-spending and race conditions. Strict idempotency key constraints are enforced to prevent double-charging during concurrent requests.
  • Granular Admin Control & Order Recovery: Dual-purpose dashboard structure featuring order management at /admin and wallet deposit approvals at /admin/deposits. Features a hardened, synchronous Order Recovery architecture with robust error handling for upstream gateway outages.
  • Observability & Analytics: Integrated Sentry for real-time error tracking and performance monitoring, alongside PostHog for comprehensive user behavior analytics.
  • Fail-Safe Admin Alerts: Immediate alert notifications dispatched via the Telegram Bot API on critical checkout API failures.
  • Order Deletion Policies: Admin-only privilege to delete completed or failed records to maintain a clean database index.

2. Architecture & Data Flow

MyanTop orchestrates a clean separation between client-side interactions, server routes, synchronous API automation, and database persistence.

🔄 MMQR-Based Order Creation & Confirmation Flow (Synchronous Direct Execution)

sequenceDiagram
    actor User as Client Browser
    participant API as Next.js API Routes
    participant DB as Supabase DB
    participant MyEzAPI as core.myeztopup.com

    %% Phase 1
    User->>User: Select MPT & Enter Phone (Auto-carrier detected)
    User->>API: POST /api/order {phone, provider, amount, paymentMethod: 'MMQR'}
    activate API
    API->>MyEzAPI: POST /api/v1/payment/topup/create (Auth Bearer JWT)
    MyEzAPI-->>API: Return paymentQrCode payload string
    API->>API: Draw base64 QR code Data URL (node-qrcode in-memory)
    API->>DB: Save QR Code in DB via save_qr_code_secure
    API-->>User: HTTP 201 Created (with waiting_payment & base64 QR URL)
    deactivate API

    User->>User: Scans QR & Pays via KBZPay / WavePay
    User->>API: POST /api/order/[id]/confirm
    activate API
    API->>MyEzAPI: GET /api/v1/products & POST /api/v1/checkout/direct (Wallet checkout + PIN)
    MyEzAPI-->>API: Return transaction success
    API->>DB: Update order to 'completed'
    API-->>User: HTTP 200 Order Completed Successfully
    deactivate API

💰 Wallet-Based Instant Happy Path (Synchronous Direct Execution)

sequenceDiagram
    actor User as Client Browser
    participant API as Next.js API Routes
    participant DB as Supabase DB
    participant MyEzAPI as core.myeztopup.com

    User->>User: Select Provider & paymentMethod: 'wallet'
    User->>API: POST /api/order {phone, provider, amount, paymentMethod: 'wallet'}
    activate API
    API->>DB: Atomic decrement wallet balance (RPC)
    alt Balance Overdraft
        DB-->>API: Exception: Insufficient Balance
        API-->>User: HTTP 402 Insufficient Balance
    else Balance Deducted Successfully
        API->>MyEzAPI: GET /api/v1/products & POST /api/v1/checkout/direct (Wallet checkout + PIN)
        MyEzAPI-->>API: Return transaction success
        API->>DB: Update order to 'completed'
        API-->>User: HTTP 201 Created (Order Completed Instantly)
    end
    deactivate API

3. Database Schema (Supabase & PostgreSQL)

The database layers are fully managed natively within Supabase PostgreSQL. Strongly-typed definitions are maintained in types/supabase.ts.

📋 Database Tables

1. User

Stores system accounts, handles profile metadata, and maintains roles.

  • id: UUID (Primary Key, maps to auth.users.id via Foreign Key)
  • name: text | null - User's full name or screen name
  • email: text | null - Authenticated email address
  • emailVerified: timestamptz | null - Timestamp when email verified
  • phoneNumber: text | null - User primary phone number
  • image: text | null - Avatar URL
  • role: Enums["Role"] - Account privileges: "USER" or "ADMIN"
  • createdAt: timestamptz - Account creation timestamp
  • updatedAt: timestamptz - Account update timestamp

2. Wallet

Maintains the monetary balances for each user.

  • id: UUID (Primary Key)
  • userId: UUID (Foreign Key -> User.id, unique 1:1 constraint)
  • balance: numeric/float8 - Current liquid cash in MMK (defaults to 0)

3. Transaction

Logs all historical modifications to wallet balances for auditing.

  • id: UUID (Primary Key, defaults to random UUID)
  • userId: UUID (Foreign Key -> User.id)
  • amount: numeric/float8 - Mutated value (always absolute positive)
  • type: Enums["TransactionType"] - "deposit" | "payment" | "refund"
  • status: Enums["TransactionStatus"] - "pending" | "completed" | "failed"
  • orderId: UUID | null (Foreign Key -> Order.id) - Associated order reference (for payments and refunds)
  • phoneNumber: text | null - Reference phone number of KBZPay/WavePay account used for manual transfers
  • method: text | null - Selected payment channel (e.g. "KBZPay", "WavePay")
  • createdAt: timestamptz - Transaction timestamp

4. Order

Maintains the execution state of REST API checks and merchant payments.

  • id: UUID (Primary Key, defaults to random UUID)
  • userId: UUID (Foreign Key -> User.id)
  • phoneNumber: text - The target mobile number receiving top-up credits
  • provider: text - Telecom company (e.g. MPT, Ooredoo, Atom, Mytel)
  • amount: numeric/float8 - Desired top-up amount in MMK
  • paymentMethod: text - Selected payment route: "MMQR" or "wallet"
  • providerOrderId: text | null - Remote provider's unique transaction identifier
  • status: Enums["OrderStatus"] - Lifecycle execution code (see below)
  • qrCode: text | null - Base64 Data URL string containing the generated provider checkout QR code image
  • createdAt: timestamptz - Order submission timestamp

5. SystemConfig

Stores centralized, persistent key-value configuration like API authentication tokens and catalog caches.

  • id: text (Primary Key) - String identifier (e.g., myeztopup_auth, myeztopup_catalog)
  • value: jsonb - Configuration payload
  • updatedAt: timestamptz - Last modification timestamp

📂 Database Enums

OrderStatus

Governs order stages and client dashboard tracking.

  • pending: Order received; initiating top-up QR code generation.
  • processing: Transaction is currently being processed by the merchant API.
  • waiting_payment: MMQR code generated and waiting for client scan/payment.
  • paid: Client verified scanning and clicking "I have paid".
  • completed: Top-up successfully completed and completed via merchant API.
  • failed: Explicit manual cancel, timeout, or merchant provider transaction issue.

Role

  • USER: Standard customer; wallet limits apply.
  • ADMIN: Elevated administrator; grants access to dashboard and action controls.

TransactionType

  • deposit: Increment requested via external payment.
  • payment: Deducted value corresponding to a top-up checkout.
  • refund: Automatic balance return following a failed checkout automation.

TransactionStatus

  • pending: Awaiting admin verify or bank deposit verification.
  • completed: Funds successfully deposited, paid, or returned.
  • failed: Rejected or errored transfer.

⚡ Stored Procedures & RPC Functions

To guarantee transaction isolation and prevent double-spending, balance changes are handled natively inside PostgreSQL database procedures:

  1. increment_wallet_balance(p_amount: numeric, p_user_id: UUID)
    • SQL Definition: UPDATE "Wallet" SET balance = balance + p_amount WHERE "userId" = p_user_id;
    • Usage: Applied on admin deposit approval and automated order failure refunds.
  2. decrement_wallet_balance(p_amount: numeric, p_user_id: UUID)
    • SQL Definition: UPDATE "Wallet" SET balance = balance - p_amount WHERE "userId" = p_user_id;
    • Usage: Triggered inside a single operation block upon submitting a valid pending order. Throws DB exceptions on overdraft.
  3. approve_deposit_secure(p_transaction_id: text) [UPDATED]
    • Usage: Validates caller permissions via is_admin() or service_role. Directly credits the Wallet balance and sets the original Transaction status to completed in a single query, preventing duplicate transaction logs.
    • Note: Wallet-related RPCs (approve_deposit_secure, add_wallet_balance_secure, deduct_wallet_balance_secure) were recently refactored to remove the legacy p_system_secret parameter, relying strictly on native PostgreSQL roles (authenticated) and is_admin() checks.
  4. is_admin() [SECURITY CLEANUP]
    • Usage: Securely validates whether the caller holds administrative privileges. To protect database access, direct execution of is_admin() is revoked from standard PUBLIC, anon, and authenticated roles, but is explicitly granted to service_role, authenticated, and anon to allow internal evaluation of row-level security (RLS) policies without exposing direct API query endpoints.
  5. refund_wallet_order_secure(p_order_id: text, p_system_secret: text) [NEW]
    • Usage: Secured via SHA-256 nextauth system secret passcode. Securely processes a refund for a failed order by returning the paid amount to the user's wallet and creating a refund transaction record.
  6. save_qr_code_secure(p_order_id: text, p_qr_code: text, p_system_secret: text) [NEW]
    • Usage: Secured via SHA-256 nextauth system secret passcode. Securely stores the in-memory base64 QR code Data URL and updates the order status to waiting_payment in a single transactional operation.

4. System API Endpoints (REST API Reference)

All requests and responses utilize standard JSON payloads. Protected endpoints verify authorization through cookies.

👤 Client-Facing API Endpoints

POST /api/order

Creates a top-up order and initiates direct, synchronous REST API execution.

  • Authentication: Required (Supabase session)
  • Request Body:
    {
      "phoneNumber": "09971234567",
      "provider": "Ooredoo",
      "amount": 10000,
      "paymentMethod": "MMQR" // or "wallet"
    }
    
  • Responses:
    • 201 Created: {"orderId": "uuid", "status": "waiting_payment", "qrCode": "data:image/png;base64,...", "message": "Order created successfully."}
    • 402 Payment Required: {"error": "Failed to create order. Insufficient balance"}
    • 400 Bad Request: Validation failure.

GET /api/order/[id]

Used by clients to poll/view order state.

  • Authentication: Required
  • Response (200 OK):
    {
      "id": "uuid",
      "phoneNumber": "09971234567",
      "provider": "Ooredoo",
      "amount": 10000,
      "paymentMethod": "MMQR",
      "status": "waiting_payment",
      "qrCode": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
      "createdAt": "2026-05-17T14:16:55.000Z"
    }
    

POST /api/order/[id]/confirm

Clients invoke this to verify they scanned the provider QR and sent the top-up payment.

  • Authentication: Required
  • Response (200 OK):
    {
      "orderId": "uuid",
      "message": "Payment confirmed and top-up processed successfully."
    }
    
    Updates database status to completed upon successful inline checkout API call.

GET /api/wallet

Retrieves a customer's liquid balance and chronological wallet transactions.

  • Authentication: Required
  • Response (200 OK):
    {
      "wallet": {
        "id": "uuid",
        "userId": "uuid",
        "balance": 25000
      },
      "transactions": [
        {
          "id": "uuid",
          "amount": 10000,
          "type": "payment",
          "status": "completed",
          "orderId": "uuid",
          "createdAt": "...",
          "order": {
            "provider": "Ooredoo",
            "phoneNumber": "09971234567"
          }
        }
      ]
    }
    

POST /api/wallet/deposit

Submits a deposit cash transfer request for admin checking.

  • Authentication: Required
  • Request Body:
    {
      "amount": 50000
    }
    
  • Response (201 Created):
    {
      "id": "uuid",
      "userId": "uuid",
      "amount": 50000,
      "type": "deposit",
      "status": "pending",
      "createdAt": "..."
    }
    

🛡️ Administrator-Facing API Endpoints

All admin routes require verification via the isAdminAuthenticated utility, validating an HTTP-Only secure SHA-256 session cookie matching ADMIN_PASSWORD.

GET /api/order

Lists all submitted orders.

  • Authentication: Admin Required
  • Query Parameters: status (Optional filter)
  • Response (200 OK): Array of Order records.

PATCH /api/order/[id]

Allows admins to override statuses, bypass automation blocks, or mark checkouts complete.

  • Authentication: Admin Required
  • Request Body:
    {
      "status": "completed"
    }
    
  • Response (200 OK): Updated Order object.

DELETE /api/order/[id]

Deletes an order record. Allowed only for terminal states (completed, failed).

  • Authentication: Admin Required
  • Response (200 OK): {"success": true}

5. Automation Engine & Direct REST API Integration

The entire top-up lifecycle has been migrated from legacy asynchronous Puppeteer browser workers to 100% synchronous direct API integration, communicating with core.myeztopup.com using standard, lightweight REST requests.

Key System Components

  1. myeztopup-api-client.ts: Manages secure token authentication, automated JWT cookie/token refreshes, and structured HTTPS clients. Uses a Supabase-backed persistent cache (SystemConfig) to share tokens and product catalogs across serverless functions, preventing race conditions and hitting rate limits.
  2. myeztopup-api-service.ts: Implements the main API-driven service logic:
    • Stage 1 (generateQRCode): Posts order parameters directly to the merchant backend, receives the raw payment QR code string, draws the MMQR code using node-qrcode inside an in-memory canvas, converts it to a base64 Data URL, and writes it directly to the database via secure stored procedures.
    • Stage 2 (completeWalletTopup): Queries product categories (cached centrally) to match desired provider and amount, resolves the exact target product ID, and executes a direct checkout using the secure PIN wallet transaction endpoint. Returns providerOrderId from upstream upon success.
  3. myeztopup.types.ts: Houses strongly typed definitions for direct API requests, catalog products, categorizations, and transaction response structures.

Direct API Safety & Error Handling

  • Synchronous Execution: Eliminates complex PostgreSQL queue locking and state tracking. Errors are returned inline to clients, avoiding duplicate state drifts or hanging worker threads.
  • Atomic Transactions: Balance checking and debiting happen inside atomic DB procedures, ensuring that if subsequent Stage 1 or Stage 2 checkout actions fail, wallet funds are rolled back cleanly and securely.

6. Environment Variables (.env)

Configure these settings inside the root .env file of your workspace:

| Variable | Description | Requirement / Default | | :--- | :--- | :--- | | NEXT_PUBLIC_SUPABASE_URL | Endpoint URL of your Supabase project | Required | | NEXT_PUBLIC_SUPABASE_ANON_KEY | Supabase Client Anonymous API Access key | Required | | SUPABASE_SERVICE_ROLE_KEY | Highly privileged DB key bypasses RLS policies | Required | | NEXTAUTH_SECRET | System-wide secret token used for secure RPCs | Required | | ADMIN_PASSWORD | Password used for /admin panel access | Default: admin123 | | MYEZTOPUP_EMAIL | Merchant provider login email address | Required | | MYEZTOPUP_PASSWORD | Merchant provider login account password | Required | | MYEZTOPUP_API_BASE_URL | Base endpoint URL for the direct merchant platform REST API | Default: https://core.myeztopup.com | | MYEZTOPUP_PIN | Secure checkout PIN used to authenticate direct API checkout transactions | Required | | TELEGRAM_BOT_TOKEN | API Token generated by BotFather | Optional | | TELEGRAM_CHAT_ID | Telegram Chat/Channel ID receiving failure reports | Optional |


7. Logging & Diagnostics

Event Tracing

API route actions, system errors, and transaction outcomes are logged to help monitor direct REST operations.

  • Output Channel: Standard console streams (e.g., Vercel / server stdout).
  • Exceptions: Critical failures (e.g., wallet authentication token expiration or invalid PIN codes) dispatch direct notifications when configured.

System Health Service

Crucial connectivity diagnostics are implemented inside services/health/health.service.ts. This executes lightweight queries (select id from User limit 1) to verify API-to-Supabase round-trips, responding with:

  • status: "ok"
  • message: "Service is running"
  • database: "Supabase connected"

8. Development & Commands

Initial System Setup

  1. Dependencies: Execute npm install in your workspace.
  2. Credentials: Create a .env file copied from .env.example containing active Supabase credentials and provider passwords.
  3. Database Migration: Run RPC triggers, tables, and enum definitions in your Supabase DB dashboard (e.g. supabase/migrations/).
  4. Local Server: Run npm run dev to start Next.js. No separate background worker process is required!

Reference Scripts

  • npm run dev: Starts the Next.js App Router local development server.
  • npm run build: Bundles compiling assets for production execution (fully compatible with serverless/read-only runtimes like Vercel).
  • npm run lint: Performs static analysis and ESLint code checks.
  • npx tsx scripts/test-api-direct.ts: Runs manual transaction verification tests directly against the REST endpoints.

9. Coding Conventions

  1. TypeScript Verification: Strict compilation rules apply. Ensure types for database rows (UserRow, OrderRow, WalletRow, TransactionRow) are fully verified.
  2. Imports: Always use path aliases @/ for imports (e.g. @/components/navbar or @/services/wallet/...).
  3. Atomic Balance Calculations: Under no circumstances should balance addition/subtraction occur client-side. Always leverage increment_wallet_balance and decrement_wallet_balance Supabase RPC procedures.
  4. Controller Safety: Keep REST route files highly modular. Business decisions, direct API checkout drivers, and database queries must stay contained inside services/.