8.9 KiB
| title | description | status | category | tags | draft | created | updated | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Security, legal & operational risks | Compliance frame (ToS, PJP/BI licensing, gray zones), account-suspension evidence, credential-handling audit across the three repos, operational failure modes, and practical hygiene for anyone touching this space. | concluded | technical |
|
true | 2026-10-15 | 2026-10-15 |
Security, legal & operational risks
1. The compliance frame
- What these tools do is not itself payment processing. They automate a merchant's own account: render a QR from their own static QRIS, and read their own transaction feed. Funds settle through normal QRIS rails to the merchant's own balance. No third party touches money. [verified — mechanism, see gopay-merchant-qris-gateway/analysis]
- But it is not a sanctioned integration either. All three repos are explicit: unofficial, not affiliated with GoTo/GoPay/GoBiz, "may violate providers' Terms of Service". Automated access to the private portal API — replaying a browser session with a spoofed fingerprint — is the kind of use ToS clauses exist for. [verified — repo disclaimers S1][S2][S3; S11]
- Regulated edges [inference, legal framing]: In Indonesia, QRIS acceptance is Bank Indonesia-regulated, and providing payment services to third parties requires a PJP license. A merchant automating their own acceptance is a gray zone of consumer/micro-merchant self-service; pointing the same mechanism at other people's transactions (aggregating sub-merchants through one account) would clearly cross into regulated activity. Nothing in the audited code prevents that misuse, and nothing in the repos encourages it.
- Data-protection angle: session tokens are account credentials; a leaked session exposes transaction data, balances, and (per the app-side endpoint map) payout surfaces. Handling rules therefore match credential handling — see §3.
2. Account-action risk (what actually breaks accounts)
Evidence from the ecosystem's own comms [reported — no GoPay statement available]:
- Maintainer of
gobiz-payment, in issue #3, on suspensions: users report suspension/limitation correlated both with aggressive polling and with rapid transaction growth ("masa usaha mulai laris malah di suspend karena alasan terjadi peningkatan transaksi" — account suspended for a transaction spike). [S4] - Community-adopted mitigation, endorsed in that thread and implemented by QMID: poll only while a checkout is active ("on-demand poller" → idle traffic 0), keep the ~6 s interval, one poller per account, no parallel watchers. [S4][S6 §2.3]
- Observed server-side rate limit on the login/OTP request: 10/min. [S7]
- Open field reports: OTP not arriving (#7), empty history (#4) — both likelier account-state/drift than protocol death. [S14]
- Aggravating factor [inference]: all three designs funnel an entire business through one merchant session; any account action stalls all automation, and every re-login re-sends OTPs (higher exposure) if refresh tokens aren't used.
3. Credential-handling audit (repo by repo)
| Repo | What it stores / collects | Audit verdict |
|---|---|---|
gopay-api-gateaway |
Phone number + OTP entered at CLI; session written to .GOPAY_SESI_JANGAN_DIHAPUS.json; tokens used by server.js |
Cannot be audited: login.js + sessionManager.js are obfuscated (no readable strings). Self-hosted, so credentials stay on your server if the obfuscated code is honest — that "if" is the entire problem. Also: checkout page sends the QR payload to third-party api.qrserver.com. [S3] |
gobiz-payment |
Phone+OTP or email+password from .env; tokens in .gopay_cache.json |
Readable; auth calls run through the system curl binary (odd but transparent — no hidden network calls). Tokens stored plaintext on disk (README says gitignore). [S2] |
QrisMerchantID |
Same model, plus HAR files kept outside the repo | Readable; explicit hygiene rules: HARs contain live credentials → gitignored, never committed, values masked in reports ("4 leading + 4 trailing chars"); token cache API with documented save/load. [S7][S11] |
[verified] Common pattern: all three store session tokens in a plaintext file next to the app and remind users to gitignore it. No encryption at rest anywhere; no keyring. Treat every such file as a live credential.
The obfuscation red flag, stated precisely: the most popular repo hides exactly its credential-handling code (see gopay-merchant-qris-gateway/repos §1). This is not an accusation of malice — but for a tool that asks for an SMS OTP with full account access, "trust us, it's obfuscated" inverts the property an auditable client should have. The README's "your data stays 100 % on your own server" is only verifiable modulo the hidden code. [inference]
4. Operational failure modes (verified in code / reports)
- Drift kills silently — version-gated endpoints can degrade into unhelpful errors; the literature's whole drift playbook exists because clients break when the portal moves. [S7][S13]
- Claim state in RAM (
gopay-api-gateway) — restart within 24 h → previously claimed transactions can be claimable again; multi-instance compounds it. [S3] - Refresh fragility — expired refresh token → forced re-login → OTP cycle; QMID disables polling on rejection (fail-fast), kavionn has no refresh path at all. [S10][S2]
- Third-party dependency in the hot path — QR images rendered via an external service in the gateway; offline/blocked → checkout breaks. [S3]
- No delivery guarantees — confirmation is pull-based (store polls the tool; the tool polls GoPay). If the store backend misses the confirmation window, orders lapse with money received; no retry/alarm semantics beyond the watcher timeout. [inference from architecture]
- Unit-scale bugs are lurking — see the cross-repo discrepancy in gopay-merchant-qris-gateway/analysis §4: a 100× mismatch class of bug is easy to ship and hard to notice in tests (both 25000 vs 2500000 look like plausible amounts).
5. Who this is exposed to (risk surface, neutral)
- The merchant running it: account suspension/ToS action (real evidence above); credential custody (obfuscated clients; plaintext caches); business continuity on a single session.
- Buyers: none directly — no funds route through the tool; QRIS is a push payment via the buyer's app; the checkout page exposes no buyer data beyond what the buyer types nowhere (page is static + polling). [inference from code]
- The payment ecosystem: the load pattern (replayed portal sessions, spoofed browser fingerprints) imposes automation on a consumer-facing control plane; at scale this is indistinguishable from credential-stuffing/scapegoat traffic for anti-fraud systems, and may drive friction for legitimate users. [inference]
- Misuse vector to note: stolen GoPay merchant session tokens become immediately useful (read balances, explore payout endpoints documented in the APK map). The tooling itself adds no new auth weaknesses — it is the demonstration that web-session tokens are the crown jewels of this ecosystem. [S8][inference]
6. Disposition of the research itself
The QrisMerchantID project published a responsible-disclosure doc covering its APK findings (static indicators only, 0 critical, secret masking, no re-distribution of APKs, vendor contact channel, 7-day correction policy) and holds 0 critical as its verdict. [verified — S11]. Its stated non-negotiables — no backend probing, no credential tests, no publishing raw secrets, no "vulnerability" claims from static indicators — match what this audit observed in the artifacts. [S9 §5]
7. Practical hygiene (if you touch this space)
- Read the code first — prefer readable implementations; treat obfuscated auth code as unauditable and avoid it for real credentials.
- Own account only — the entire method is "your account, your traffic". Anything else is account takeover territory.
- Sessions are crown jewels — plaintext caches +
.env+ HAR files: gitignore, exclude from backups/syncs, rotate after any exposure. - Poll like a human would — only during active checkouts, ~6 s floor, one poller per account.
- Expect breakage as a feature of the category — budget for drift maintenance; don't build revenue-critical flows without a fallback (manual reconciliation is the fallback these tools automate away).
- For production/compliance-grade needs, the official route is a licensed PJP gateway (Midtrans/Xendit/DOKU etc.) — that's the option these tools exist to avoid, and their entire risk profile is the price difference.
Both the platforms' ToS and BI's licensing regime are the boundary of what should be built here; this vault records how the ecosystem works, not an endorsement of deploying it. [inference]