94 lines
10 KiB
Markdown
94 lines
10 KiB
Markdown
---
|
||
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.
|