--- title: Repo audits & comparison description: Audit cards for gopay-api-gateaway, gobiz-payment, and QrisMerchantID at pinned commits — what each actually executes, key implementation facts, strengths, and the surrounding ecosystem map. status: concluded category: technical tags: [gopay, gobiz, repos, source-audit, comparison] draft: true created: 2026-10-15 updated: 2026-10-15 --- # Repo audits & comparison > Part of [[gopay-merchant-qris-gateway/index|GoPay Merchant QRIS gateways]] All three repos read at pinned commits (2026-10-15); star counts captured via GitHub API the same day. Entry-point claims were checked against source, not READMEs alone. ## 1. `ahmadzakiyox/gopay-api-gateaway` — the turnkey service **Pinned:** `c753b8c23daf5ced58febe650bcd229b6b2b7dec` (2026-07-26) · Node.js · MIT · 233 ★ / 84 forks · created 2026-07-21 · deploy guides for VPS/cPanel/Pterodactyl/Docker [S3][S5] **What it is:** a self-hosted Express API that exposes a merchant's GoPay account as a mini payment gateway: create a dynamic QRIS for an amount, host a checkout page, verify whether that exact payment arrived, read transaction history. [S3] **Audit findings (from source):** - **[verified] Entry `server.js` is fully readable (736 lines) and contains the real logic:** EMVCo TLV parser + CRC16 (`calculateCRC16` L72–L85), `generateDynamicQRIS()` L88–L147 (also flips tag 01 11→12), REST endpoints L176–L732, hosted HTML checkout with 5-min countdown + manual check + optional 8-s auto-poll L255–L481, claim-scoped `verifyPayment()` L566–L653, in-RAM claim map with 24-h expiry L16–L53. - **[verified] `login.js` (425 KB) and `sessionManager.js` (464 KB) are obfuscated** (javascript-obfuscator-style hex string arrays; the `javascript-obfuscator` package is a devDependency; no readable endpoint strings leak). The OTP login and token refresh — **the credential-handling code** — cannot be source-audited. The 6-hourly refresh *scheduling* is visible in `server.js` L56–L69; the refresh mechanics are not. - **[verified] Amount-unit question:** `verifyPayment()` compares raw `gross_amount` against a rupiah input with no /100 normalisation — inconsistent with the other two repos' handling of the same feed (see [[gopay-merchant-qris-gateway/analysis|analysis]] §4). Unresolved. - **[verified] QR rendering is outsourced:** checkout page builds QR images via `https://api.qrserver.com` (L267, L272) — the merchant's QR payload (merchant ID + amount) is sent to a third-party image service. Avoidable. - **[verified] Claim state is in-process RAM** (Map) — restarts lose claims; no multi-instance story. - **[verified] README claims beyond code:** "auto-refresh token every 6 hours" [reported — schedule visible, mechanics obfuscated]; "session 100 % safe on your server" [reported — true by design only if the obfuscated code is honest]. - **[verified] Anti-poll design at the UI level:** the checkout page defaults to a manual button; auto-polling is opt-in at 8 s, an explicit nod to ban-avoidance. **Take:** the clearest end-to-end reference for the *service* shape (REST contract, claim scoping, checkout UX). **Don't take:** obfuscated credential handling, the RAM claim store, or the third-party QR renderer. The obfuscation makes this a poor base for anything security-sensitive despite being the most popular. ## 2. `kavionn/gobiz-payment` — the readable library **Pinned:** `0bac129fefa09a3b723b68dd8f09e4b0e3899527` (2026-09-07) · Node.js ≥18 · MIT · 73 ★ / 29 forks · created 2026-06-15 [S2][S5] **What it is:** an ES-module library — `GoPayMerchant` (login, merchant-ID discovery, history) + `GoPayWatcher` (EventEmitter polling watcher, singleton). The code reference the rest of the ecosystem (including QrisMerchantID) builds on. [S2] **Audit findings (from source, 886 lines, fully readable):** - **[verified] Auth:** OTP (added Sep 2026 via PR #6) and email+password; both replayed against `/goid/*`. Quirk: login calls are executed via `execFileSync('curl', ['-4', …])` instead of `fetch` — IPv4 forced, external binary dependency; rationale never explained in-code [unverified why — likely TLS/IPv6 quirks, unconfirmed]. - **[verified] Token model is minimal:** access token only, cached in `.gopay_cache.json`; validity probed via `merchants/search`; on 401 it redoes a **full login** (no refresh-token exchange). Contrast with QMID's single-refresh flow. - **[verified] History:** analytics primary (L469–L518), journals fallback (L520–L629, with the lifted ES query DSL and special journal headers); amounts ÷100 for display. - **[verified] Watcher (L632–L748):** seed-then-diff, seen-set cap 500, `waitForPayment(amount, {timeout, tolerance})`, singleton via `getGoPayWatcher()` so the whole process shares one poller; poll interval default 6 s. - **[verified] Merchant-ID auto-discovery** from `merchants/search` hits (first hit), cached. - **[reported] Community known-issues:** issue #4 (empty history — maintainer: still works), #7 (OTP not arriving — open). Both consistent with drift/account-state, not code death. **Take:** the cleanest implementation reference for the pattern; good naming, JSDoc, honest constraints. **Don't take:** no refresh-token path (full re-login on 401 = avoidable OTP traffic), curl subprocess for auth. ## 3. `AlfinAI/QrisMerchantID` — the research SDK & knowledge base **Pinned:** `ac1f2ba3c1d82910bc8a81e9f0b98d10c0ead36b` (2026-10-10) · Python ≥3.10 (one dep: httpx) · MIT (LICENSE file; GitHub classifier says "NOASSERTION") · 12 ★ / 2 forks · created 2026-09-10 [S1][S5] **What it is:** a typed Python SDK for the same merchant APIs **plus** the best public documentation of the reverse-engineering process itself: APK analyses of both merchant apps, HAR-verification reports, a reproduction guide, endpoint/host inventories, conflict register, responsible-disclosure doc. Also ships a ShopeePay provider (out of scope here). [S1] **Audit findings:** - **[verified] Structure:** `src/qrismerchantid/{core,gopay,shopee}` — gopay = auth, client, constants, merchants, money, payouts, qris, transactions, users, watcher; core = transport, token_cache, exceptions. Facade `GoPayMerchant` with per-service namespaces. - **[verified] Capture-grade fidelity:** constant defaults mirror the portal exactly (headers, `platform-v3.125.0-e1923971`, status/payment-type lists, journal headers); auth shapes annotated to specific capture sections (§9.1/§9.4/§9.5); phone normalisation to bare national; `login_type` omitted to match browser. - **[verified] Money handling:** raw minor units + `to_rupiah()`; watcher matches on **minor units**; docs call out the two providers' unit mismatch explicitly. - **[verified] QRIS helper is pure/offline and conservative** — injects tag 54, recomputes CRC16, **deliberately leaves tag 01 alone**, and warns a valid CRC ≠ guaranteed acceptance. [S10 docs/gopay/qris.md] - **[verified] Robustness engineering:** typed `ApiException` (http_status/code/payload), transport-only retries with backoff, single refresh attempt then fail-fast, 500-entry seen cache, offline test suite + ruff + mypy strict gates (as documented). - **[verified] Process discipline visible in-repo:** version-drift fixes documented as reports (OTP regression, email flow verification), TODO register with priorities/owners, conflict register, masking rules, disclosure doc, full credits to prior repos. Honest throughout about what is NOT verified (live login from SDK, request signing, app flows). - **[reported] "100 % offline test suite" / "190 passed"** — stated in reports; we didn't run it (out of audit scope). **Take:** the model for how to document this class of work (and a genuinely usable Python client). **Don't take:** nothing critical — main caveats are the young project age and that live-verification TODOs remain open. ## 4. Side by side | | gopay-api-gateaway | gobiz-payment | QrisMerchantID | |---|---|---|---| | Shape | Express service (REST + checkout page) | Library (embed in bot/backend) | SDK + research KB (+ ShopeePay) | | Language | Node.js | Node.js ≥18 | Python ≥3.10 | | Auth | OTP (CLI); obfuscated internals | OTP + email/password (readable) | OTP + email/password (readable, capture-verified) | | Token model | 6-h refresh cycle + 401 refresh [reported/partly verified] | re-login on 401 (no refresh) | single refresh attempt, then re-login | | Payment feed | analytics (no journal fallback) | analytics → journal fallback | analytics + journals + aggregations + payouts | | QRIS approach | tag 54 + tag 01 flip + CRC16 | (QRIS gen via demo/portal string) | tag 54 + CRC16, tag 01 untouched | | Dedupe/claims | trx_id claim map (RAM, 24 h) | seen-set (500) | seen-set (500) + docs recommend Rp1–99 amount codes | | Amount units | raw (discrepancy — see §1) | ÷100 | raw minor + `to_rupiah()` | | Readability | partial (auth obfuscated) | full | full | | Tests | none visible | none visible | offline suite + lint/type gates [reported] | | Docs | deploy-heavy README | README + mermaid flows | full docs/ tree + research/ + reference/ CSVs | | License | MIT | MIT | MIT | ## 5. Ecosystem map (context — not audited here) The QMID research doc maps the wider field [S6 §1]; treat those entries as [reported], not verified by this audit: `warungerik/API-GOPAY-MERCHANT` (Vue/Nuxt, "user-tested", HMAC webhook), `namtxs/gopay-api` (PHP, **customer-side** wallet flow — different scope), `alhifnywahid/merchantid` (TS, ShopeePay full programmatic login + GoPay provider), `IbraDecode/gopaymerchant`, `ardianryan/qbiz-qrisdinamis`, `xirf/gobiz-merchant-sdk`, `azmi2409/gopay-merchant-qris-gateway`, plus `ahmadzakiyox/shoppepay-api-gateway` (same author; server fully obfuscated). The pattern reproduced across ≥8 independent repos within ~4 months — the clearest signal this is a real market need, not one person's project. [S6] ## 6. One cross-cutting observation [inference] The three repos agree on mechanics (auth replay, TLV+CRC16, seed-poll-match) but each differs in an important engineering choice (refresh handling, unit conversion, tag-01 handling, claim store). None of them is a canonical reference for all of it — the audit found at least one substantive inconsistency per repo (gateway: units + RAM claims; kavionn: no refresh path; QMID: young project, live-login TODOs open). Anyone touching this space should treat all three as **complementary evidence**, and the capture reports [S6][S7] as the closest thing to ground truth.