This PR resolves four backend reliability and security issues (#584 – #587):
-
#584 — Webhook retry jitter (
src/services/webhook.ts): Added ±20 % random jitter togetNextRetryDelay()so retries after a mass failure are spread across a window instead of all firing at the same instant (thundering-herd prevention). -
#585 — Canonical webhook signatures (
src/services/webhook.ts): IntroducedcanonicalJsonStringify()— a recursive, alphabetically-key-sorted JSON serialiser — and switched bothgenerateSignature()and the payload body indeliverWebhook()to use it. Subscriber verification is now deterministic regardless of Node version or middleware insertion order. -
#586 — Separate recurring-support execution table (
prisma/schema.prisma,src/services/drip-scheduler.ts,src/app.ts): Instead of recording a faketxHash = 'pending_…'row inSupportTransaction, the drip scheduler now creates aRecurringSupportExecutionrow (status:pending). When the user later submits the real Stellar transaction viaPOST /support-transactions, the endpoint validates the execution details match the subscription and atomically marks itsuccess. This keepsSupportTransactionclean and audit-ready. -
#587 — Stellar address as supporterAddress (
prisma/schema.prisma,src/app.ts): AddedsupporterAddresstoRecurringSupportand now populate it fromreq.auth!.walletAddress(the verified Stellar public key), notuser.email. Schema is resilient to future email/wallet-address separation.
drip-scheduler.test.tsrewritten to cover the newrecurringSupportExecution.createpath (6 tests, all pass).app.test.ts: added Test 42b — integration test forrecurringSupportExecutionIdvalidation; corrected URL-normalisation and sanitize-array expectations.src/middleware/sanitize.ts: fixedjavascript:/data:scheme blocking, normalised URLs to always return canonicalhref(with trailing slash), and enabled HTML sanitization for plain strings inside sanitised arrays.
Closes #584, #585, #586, #587
- Bug fix
- New feature
- Refactor
- Docs / config only
- Webhook jitter: Call
getNextRetryDelay(0)(1 s base) multiple times — values will vary within [800 ms, 1 200 ms]. - Canonical signatures: Sign a payload with keys in arbitrary order; verify with keys in a different order — HMAC should always match.
- Recurring execution flow:
POST /recurring-supportto create a subscription.- Inspect the drip-scheduler log — no
SupportTransactionrow is created; aRecurringSupportExecutionrow withstatus = pendingappears instead. POST /support-transactionswithrecurringSupportExecutionId— mismatched amount/asset/recipient returns400; matching details succeed and mark the executionsuccess.
- Run tests:
npm testinbackend/— all tests pass (DB-dependent tests requireDATABASE_URL).
- I have read the CONTRIBUTING guide
- My branch is up to date with main
- Linter passes (
npm run lint) - Tests pass (
npm test) if applicable - I have not committed
.envfiles or secrets