Files
hope-events/backend/src/utils/settingsCache.js
T
joshuaandClaude Opus 5.5 f2b51dbd88 PayFast provider, Pay online page, amount checks on payments
- PayFast (custom integration): signed form (PHP-style encoding, checked
  against a real sandbox), ITN checks (signature over all posted fields,
  PayFast host, VALID from /eng/query/validate, amount vs checkout).
- ProviderCheckout records every checkout (amount, metadata, request);
  webhooks match payments to it and hold a different amount for review
  (ProviderTransaction.note).
- /pay/<id>: payers choose a provider when several are on, and the
  browser posts PayFast's form from there, so payment links work for
  every provider.
- Site settings → Payments: PayFast card (merchant ID/key, encrypted
  passphrase, sandbox/live) while PayFast is enabled.
- Payer-facing wording no longer names Yoco; legal pages name both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 14:25:49 +02:00

79 lines
2.3 KiB
JavaScript

/**
* Lightweight in-process cache for AppSetting values.
* Refreshes every 60 seconds so changes made in the admin settings page
* take effect without a server restart.
* Falls back to environment variables when a key is not found in the DB.
*
* Encrypted keys (SMTP credentials, Yoco secrets) are automatically decrypted on read.
*/
const prisma = require('../config/db');
const { decrypt, isEncrypted } = require('./encryption');
// Keys stored encrypted in the DB — decrypted transparently on read.
const ENCRYPTED_KEYS = new Set(['smtp_user', 'smtp_pass', 'yoco_secret_key', 'yoco_webhook_secret', 'payfast_passphrase']);
let _cache = {};
let _lastFetch = 0;
const TTL_MS = 60 * 1000;
async function refreshIfStale() {
const now = Date.now();
if (now - _lastFetch < TTL_MS && _lastFetch > 0) return;
try {
const rows = await prisma.appSetting.findMany();
const fresh = {};
for (const r of rows) fresh[r.key] = r.value;
_cache = fresh;
_lastFetch = now;
} catch {
// DB unreachable — keep existing cache; will retry next call
}
}
/**
* Internal getter that optionally decrypts encrypted keys.
* @param {string} key
* @param {string} [envFallback]
* @returns {string}
*/
function _getSync(key, envFallback = '') {
const v = _cache[key];
const raw = (v !== undefined && v !== null && v !== '') ? v : (envFallback || '');
if (ENCRYPTED_KEYS.has(key) && isEncrypted(raw)) {
try { return decrypt(raw); } catch { return ''; }
}
return raw;
}
/**
* Async getter — always checks for a stale cache before returning.
* @param {string} key
* @param {string} [envFallback]
* @returns {Promise<string>}
*/
async function getSetting(key, envFallback = '') {
await refreshIfStale();
return _getSync(key, envFallback);
}
/**
* Sync wrapper for use in synchronous functions.
* Uses the current in-memory snapshot; relies on the cache being warmed at startup.
*/
const getSettingSync = _getSync;
/**
* Call once at startup to pre-warm the cache so synchronous callers get DB values.
*/
async function warmCache() {
await refreshIfStale();
}
/** Invalidate the cache and immediately re-warm it in the background. */
function invalidate() {
_lastFetch = 0;
refreshIfStale().catch(() => {});
}
module.exports = { getSetting, getSettingSync, warmCache, invalidate, ENCRYPTED_KEYS };