Skip to content
GitLab
Projects Groups Topics Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Register
  • Sign in
  • A Ahad Mobile
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributor statistics
    • Graph
    • Compare revisions
  • Issues 0
    • Issues 0
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 0
    • Merge requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Terraform modules
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • holatech
  • Ahad Mobile
  • Merge requests
  • !81

feat(keysecure): SP2 webhook intake

  • Review changes

  • Download
  • Patches
  • Plain diff
Merged Khanh Pham requested to merge feat/keysecure-sp2-webhook-intake into develop Sep 30, 2026
  • Overview 0
  • Commits 6
  • Pipelines 1
  • Changes 14

KeySecure SP2 — webhook intake

Receives KeySecure (card provider) callbacks and stores them. No ledger posting and no card-status changes yet: those handlers arrive in SP3.

What's in it

  • POST /keysecure/webhooks (no auth guard, the callback signature authenticates it). Answers { "code": 0 } immediately and processes in the background.
  • New table keysecure_webhook_events (migration 1789800000000), which keeps raw_body verbatim so a real sandbox callback can serve as the golden vector before signatures are enforced.
  • Dedupe on the unique push_id (the envelope id). A redelivery that carries a different body is logged as an error.
  • KEYSECURE_SIG_MODE:
    • log (default): a bad signature is stored, with its reason in sig_result, but never reaches a handler.
    • enforce: a bad signature gets 401 and nothing is stored.
  • Typed parsing of Consumption and Card_update, with amounts kept as the provider's decimal text. Auth is marked IGNORED (we run in credit mode).
  • registerHandler (at-least-once delivery, so handlers must be idempotent), plus listFailed / replayFailed.
  • Request bodies over 64 KiB get 413. A missing KeySecure config gets 503.

Verification

  • yarn jest src/modules/keysecure: 19 suites, 222 tests pass. yarn build succeeds.
  • Booted the built app on a copy of the local DB: the migration applied and the route was mapped. Auth returned {code:0} and was stored IGNORED, a duplicate id added no new row, Card_update was PROCESSED, and a malformed body returned 400.
  • Final review (Opus) approved in 2 rounds.

Follow-ups (SP3)

  • A replay cron for rows stuck in RECEIVED/FAILED. It must skip or re-verify rows whose signature was not verified.
  • Handlers must be idempotent by business key (consume_no / card_no + status). The provider does not document whether id is per delivery or per record.
  • Before switching KEYSECURE_SIG_MODE=enforce: verify one real sandbox callback.

🤖 Generated with Claude Code

Assignee
Assign to
Reviewers
Request review from
Time tracking
Source branch: feat/keysecure-sp2-webhook-intake