Repository navigation
Add webhooks: transaction, hold and card authorization events - #49
Merged
Merged
Conversation
- Transactions panel: an account's history as the API returns it (newest first, pending holds on top), filtered by period (30 days, 180 days, all time; all time by default, as the seed data is from 2022–2023), by transaction type, and with or without pending holds. Signed amounts and the running balance. - Beneficiaries panel: name, bank, account, category and last payment, and a Pay form (from account with its available balance, beneficiary, amount, references). Refusals show the API's reason, e.g. Insufficient funds. - Open transaction lists and beneficiaries refresh with everything else. - API: transfermultiple and paymultiple now emit the data-change event, so an open dashboard refreshes after transfers and payments made from Postman too. Tests: 4 unit (signedAmount, periodRange, paymentError) and 3 e2e (history filters and holds, a payment updating beneficiary and history, a refused payment). 39 unit, 15 e2e and 173 API tests passing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sandbox-only webhooks, so integrations can react to the bank as they
would to real events.
- Webhook and WebhookDelivery tables. Endpoints under /webhooks
(outside /za, no token): list, register (url, events, optional secret),
PATCH to pause/resume or change, delete, send a test, delivery log, and
the event list. URLs must be http(s); secrets are never returned.
- Events: transaction.posted (top-ups, transfer and payment legs, created
and posted swipes, settled holds), transaction.pending,
transaction.reversed, card.authorization (every live swipe decision:
approved or why not, merchant, rand amount and any conversion), and
webhook.test.
- Delivery (src/webhooks.ts): JSON POST { id, event, createdAt, data }
with X-Sandbox-Event, X-Sandbox-Delivery and, with a secret,
X-Sandbox-Signature (sha256 HMAC of the body). 5s timeout, no automatic
retries; every attempt is recorded (status, response code or error such
as "Connection refused", duration) and pushed to dashboards. Publishing
runs in the background, so API calls never wait on or fail because of a
receiver.
- OpenAPI: the endpoints plus each payload under 3.1's webhooks section.
- Dashboard: a Webhooks panel to register (events, signing secret), send
tests, pause/resume and delete, with a live delivery log and payloads.
Tests: 10 API tests against a real local receiver (signature, payloads,
filtering, pausing, failures, validation) and 1 e2e test (register, test,
swipe delivered live). 183 API, 39 unit and 16 e2e passing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # src/routes/accounts.ts # web/components/dashboard/Dashboard.tsx # web/hooks/useSandbox.ts # web/lib/api/sandbox.ts # web/lib/api/types.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #48 (base
feat/dashboard-history-payments), because both change the transfer and payment code. Once #48 is squash-merged I'll mergemaininto this branch and retarget it, with no force-push.Sandbox-only webhooks: register a URL and the sandbox POSTs events as they happen, so integrations can be built and tested against the bank's events.
Events
transaction.postedtransaction.pendingtransaction.reversedcard.authorizationinsufficient_funds,card_not_active,declined_by_card_code), with merchant, rand amount and any currency conversionwebhook.testDelivery
{ id, event, createdAt, data }, withX-Sandbox-Event,X-Sandbox-Deliveryand, when a secret is set,X-Sandbox-Signature: sha256=<HMAC of the raw body>.API
/webhooks, outside/za, so no token is needed:PATCH(pause/resume, change URL or events) and deleteURLs must be
http(s), and secrets are never returned. Webhooks are configuration, so Reset data and scenarios leave them in place.OpenAPI
The endpoints, plus each event payload in 3.1's
webhookssection, so receivers can generate types too.Dashboard
A Webhooks panel:
Test plan
test/webhooks.spec.tsruns a real receiver on 127.0.0.1 and checks the HMAC signature, transfer legs, the hold lifecycle (authorization → pending → posted), declines and reversals, event filtering, pausing, a 500 and a connection refusal recorded with reasons, and validation.openapi:check(61 routes), knip, lint, build. Web types regenerated.🤖 Generated with Claude Code