| DATABASE_URL | Required | file:../data/payment.db | SQLite file location. The path is resolved relative to prisma/schema.prisma inside the container, so '../data' lands in /usr/src/app/data — which is where the data volume should be mounted. |
| PORT | Required | 7001 | HTTP port GRCpay listens on. |
| GRC_RPC_HOST | Required | — | Hostname or IP of the gridcoinresearchd RPC interface. In a Compose setup, this is the wallet service name (e.g. "wallet"). For an existing wallet on the same host, use host.docker.internal. |
| GRC_RPC_PORT | Required | 32748 | RPC port. 32748 = mainnet, 32746 = testnet. |
| GRC_RPC_USER | Optional | — | RPC username. Must match rpcuser in the wallet's gridcoinresearch.conf. Optional but strongly recommended: GRCpay will start without it for dev convenience, but the wallet will refuse RPC calls without matching credentials. |
| GRC_RPC_PASSWORD | Optional | — | RPC password. Same constraints as GRC_RPC_USER above. |
| LIFE_SPAN | Optional | 7200 | How long a payment wallet stays open before it gets marked expired, in seconds. Default is 2 hours. |
| JOBS_INTERVAL | Optional | 10 | How often the background job loop runs (balance updates → funded detection → expiry → forwarding → refunds), in seconds. A tick is skipped if the previous run is still in flight, so long RPC batches can never overlap onto themselves. |
| LATE_PAYMENT_WINDOW | Optional | 604800 | How long after a wallet reaches a terminal state (processed / refunded / norefund) GRCpay still watches it for late-arriving customer payments, in seconds. Inside the window, any GRC that trickles in from a stale checkout page or a saved address is detected and refunded to the sender automatically. Outside it, funds sent to a stale address stay in the hot wallet for manual sweep. Default 7 days, past which every reasonable browser cache or checkout session is assumed gone. |
| LATE_PAYMENT_CHECK_INTERVAL | Optional | 3600 | How often the late-payment sweep runs, in seconds. Deliberately separate from (and much slower than) JOBS_INTERVAL because late payments are an edge-case rescue path, not a latency-sensitive flow. Default once an hour. Set to 0 to disable the sweep entirely, which is useful if you don't want GRCpay auto-touching terminal wallets at all and prefer to sweep the hot wallet manually. |
| MAX_REFUND_ATTEMPTS | Optional | 5 | How many times a refund RPC call is allowed to fail before GRCpay gives up. Applies to both overpayment refunds (funded processor) and late-payment refunds. Past the cap the funded processor falls back to forwarding the full received balance so the merchant payout is never blocked indefinitely; the late-payment processor leaves the funds in the hot wallet and stops retrying. |
| REFUND_RETRY_BASE_DELAY | Optional | 30 | Exponential backoff base for refund retries, in seconds. After failure N the next attempt is gated by base * 2^(N-1), so with the default 30s the intervals are 30s, 1m, 2m, 4m, spanning ~7.5 minutes before MAX_REFUND_ATTEMPTS trips. The window exists so a real human operator has time to unlock a locked wallet (the usual root cause of a refund RPC failure) before GRCpay declares the refund hopeless. |
| MIN_CONFIRMATIONS | Optional | 2 | Minimum number of block confirmations a received tx must have before it counts toward a wallet's settled balance. The balance updater queries the daemon at both MIN_CONFIRMATIONS and 0-conf on every tick: the confirmed portion lands in amountRecieved and gates the new → funded flip, while the unconfirmed delta is reported separately as amountPending so integrators can surface a "waiting for N confirmations" state. Default 2 matches standard e-commerce hardening against same-block reorgs. Lower to 1 on private or trusted chains; set to 0 to accept 0-conf payments (faster but unsafe against reorgs). |
| RATE_LIMIT_WALLET_CREATE_PER_MIN | Optional | 10 | Per-IP rate limit on POST /wallets, in requests per minute. Keeps wallet creation cheap to abuse-proof: each call mints a fresh gridcoin address and burns an RPC slot, so the default is deliberately tight. Legitimate merchant checkout flows rarely exceed a handful per minute per IP, so raise it only if you have a specific reason. |
| RATE_LIMIT_WALLET_READ_PER_MIN | Optional | 300 | Per-IP rate limit on GET /wallets/:address, in requests per minute. Needs to be generous because the WooCommerce plugin polls every 5 seconds per open checkout page (12 req/min per tab), and multiple concurrent customers behind the same NAT share one bucket. Default 300 covers roughly 25 concurrent customers on a shared IP with headroom. |
| RATE_LIMIT_WALLET_DELETE_PER_MIN | Optional | 10 | Per-IP rate limit on DELETE /wallets/:address (merchant-initiated cancellation), in requests per minute. Same envelope as creation: rarely used in normal operation, tight by default to blunt abuse. |
| RATE_LIMIT_QR_PER_MIN | Optional | 120 | Per-IP rate limit on GET /wallets/:address/qr, in requests per minute. This endpoint is intentionally public (for <img> embedding on checkout pages), so it has its own bucket separate from the token-gated read endpoint. The QR renderer is cheap but not free; 120/min/IP is comfortable for real use without leaving an amplification target wide open. |
| RATE_LIMIT_RATES_PER_MIN | Optional | 60 | Per-IP rate limit on GET /rates and /rates/:currency, in requests per minute. Server-side cached for five minutes, so requests beyond the cache TTL hit CoinGecko rather than GRCpay; the limiter mostly exists to discourage polling storms. |
| RPC_BREAKER_THRESHOLD | Optional | 5 | Circuit breaker failure threshold for the Gridcoin RPC client. After this many consecutive failures (timeouts or errors), the breaker opens and fast-fails every subsequent RPC call without touching the wallet daemon for RPC_BREAKER_COOLDOWN_MS. One probe call goes through after the cooldown; success closes the breaker, failure reopens it with a fresh cooldown. Set to 0 to disable the breaker entirely, which is useful for dev or when troubleshooting an unrelated issue. The per-call 30s timeout still applies regardless. |
| RPC_BREAKER_COOLDOWN_MS | Optional | 30000 | How long (in milliseconds) the RPC breaker stays open before it lets a probe call through. Matches the per-call RPC timeout budget by default: five failed 30s calls already cost the job loop 2.5 minutes, so the breaker blocks further requests for at least one more cycle before we retry. Raise this if your wallet daemon takes longer to recover from the kind of failure you're seeing; lower it only in dev. |
| TRUST_PROXY_HOPS | Optional | 1 | Number of reverse-proxy hops Express should trust when reading the client IP from X-Forwarded-For. The per-IP rate limiters read req.ip, so getting this wrong either (a) trusts forged X-Forwarded-For values when GRCpay is directly exposed, letting an attacker rotate fake IPs to bypass the limit, or (b) buckets every request under nginx's loopback address when deployed behind a real proxy. Default 1 assumes a single trusted upstream (the usual nginx-in-front deployment). Set to 0 if GRCpay is directly exposed on a public port with no reverse proxy. Set to 2+ if there are multiple proxies (e.g., Cloudflare → nginx → GRCpay). |
| NODE_ENV | Optional | (unset) | Set to "production" in deployed environments. Quiets the verbose per-request HTTP access log and disables a few dev-only behaviours. |