Implement idempotency keys on POST /usage and POST /settle to prevent double billing
Description
POST /api/v1/usage and POST /api/v1/settle in src/index.ts are not idempotent: a network retry that re-sends the same request will double-count usage or settle twice, because each call blindly mutates usageStore. For a billing surface that mirrors an on-chain settle(), replay safety is essential. This issue adds an Idempotency-Key header so retried requests return the original result instead of mutating again.
Requirements and context
- Repository scope:
Agentpay-Org/Agentpay-backend only.
- Honour an
Idempotency-Key request header on POST /api/v1/usage, POST /api/v1/usage/bulk, and POST /api/v1/settle.
- Store the first response keyed by
(apiKey-or-ip, idempotency-key) with a TTL; on replay return the stored status + body verbatim and set an Idempotency-Replayed: true header.
- Detect key reuse with a different body and respond
409 idempotency_conflict (with requestId).
- Cap the idempotency cache and evict by age, mirroring the existing
EVENT_LOG_CAP eviction pattern.
- Do not change the success-path response shapes.
Suggested execution
- Fork the repo and create a branch
git checkout -b feature/payments-04-idempotency-keys
- Implement changes
- Write code in: a reusable
src/middleware/idempotency.ts plus the route wiring in src/index.ts.
- Write comprehensive tests in: new
src/middleware/idempotency.test.ts — first call, replay, conflict, TTL expiry.
- Add documentation: document the header behaviour in
docs/idempotency.md.
- Add TSDoc on the middleware factory.
- Validate security assumptions: key namespacing per caller, no cross-tenant replay.
- Test and commit
Test and commit
- Run
npm run build, npm test, and npm run lint.
- Cover edge cases: same key/same body, same key/different body, missing key (passthrough), expired key.
- Include the full
npm test output in the PR description.
Example commit message
feat: add idempotency-key support to usage and settle endpoints
Guidelines
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
Community & contribution rewards
- 💬 Join the AgentPay community on Discord for questions, reviews, and faster merges: https://discord.gg/eXvRKkgcv
- ⭐ This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project — if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward.
Implement idempotency keys on POST /usage and POST /settle to prevent double billing
Description
POST /api/v1/usageandPOST /api/v1/settleinsrc/index.tsare not idempotent: a network retry that re-sends the same request will double-count usage or settle twice, because each call blindly mutatesusageStore. For a billing surface that mirrors an on-chainsettle(), replay safety is essential. This issue adds anIdempotency-Keyheader so retried requests return the original result instead of mutating again.Requirements and context
Agentpay-Org/Agentpay-backendonly.Idempotency-Keyrequest header onPOST /api/v1/usage,POST /api/v1/usage/bulk, andPOST /api/v1/settle.(apiKey-or-ip, idempotency-key)with a TTL; on replay return the stored status + body verbatim and set anIdempotency-Replayed: trueheader.409 idempotency_conflict(withrequestId).EVENT_LOG_CAPeviction pattern.Suggested execution
git checkout -b feature/payments-04-idempotency-keyssrc/middleware/idempotency.tsplus the route wiring insrc/index.ts.src/middleware/idempotency.test.ts— first call, replay, conflict, TTL expiry.docs/idempotency.md.Test and commit
npm run build,npm test, andnpm run lint.npm testoutput in the PR description.Example commit message
feat: add idempotency-key support to usage and settle endpointsGuidelines
Community & contribution rewards