research/gopay-merchant-qris-gateway/repos.md

94 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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.