gopay-merchant-qris-gateway: add topic — GoPay/GoBiz merchant→QRIS gateway reverse-engineering audit (gopay-api-gateaway, gobiz-payment, QrisMerchantID, pinned commits)
This commit is contained in:
parent
d512cd7514
commit
376a811476
6 changed files with 589 additions and 0 deletions
74
gopay-merchant-qris-gateway/security.md
Normal file
74
gopay-merchant-qris-gateway/security.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
title: Security, legal & operational risks
|
||||
description: 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.
|
||||
status: concluded
|
||||
category: technical
|
||||
tags: [gopay, gobiz, risk, tos, compliance, credentials, security]
|
||||
draft: true
|
||||
created: 2026-10-15
|
||||
updated: 2026-10-15
|
||||
---
|
||||
|
||||
# Security, legal & operational risks
|
||||
|
||||
> Part of [[gopay-merchant-qris-gateway/index|GoPay Merchant QRIS gateways]]
|
||||
|
||||
## 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|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|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|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)
|
||||
|
||||
1. **Read the code first** — prefer readable implementations; treat obfuscated auth code as unauditable and avoid it for real credentials.
|
||||
2. **Own account only** — the entire method is "your account, your traffic". Anything else is account takeover territory.
|
||||
3. **Sessions are crown jewels** — plaintext caches + `.env` + HAR files: gitignore, exclude from backups/syncs, rotate after any exposure.
|
||||
4. **Poll like a human would** — only during active checkouts, ~6 s floor, one poller per account.
|
||||
5. **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).
|
||||
6. **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]
|
||||
Loading…
Add table
Add a link
Reference in a new issue