Fix/sr push webhook admin kyc gaps - #1414
Merged
GoodnessJohn merged 5 commits intoAug 28, 2026
Merged
Conversation
…eceipts sendExpoPush() only logged ticket.status === 'error' and never inspected ticket.details.error, so tokens Expo reports as DeviceNotRegistered were never deleted and kept being resent on every notification indefinitely. The two-phase Expo flow (ticket now, receipt later) also meant real delivery failures were never observed at all. DeviceTokenService now deregisters tokens immediately on a DeviceNotRegistered ticket, records successful ticket IDs, and a new 15-minute scheduler job polls /getReceipts to prune tokens whose receipt reports DeviceNotRegistered. Claude-Session: https://claude.ai/code/session_017KdUwWCFg1x9ezdbFs9zVB
…patch attemptDelivery() retried failed subscriber deliveries in-place with up to 5 attempts at up to 300s backoff each, and dispatch() awaited subscribers sequentially. handleRemittanceCreated() awaited dispatch() inline before the inbound /webhooks/anchor handler sent its 200, so one down subscriber could hold the anchor's webhook request open for minutes, risking the anchor's own delivery timeout/retry. Dispatch now runs subscribers in parallel via Promise.allSettled, inline retries are capped by WEBHOOK_INLINE_MAX_WALL_CLOCK_MS (default 10s) and deferred to a new scheduled job otherwise, and the inbound handler no longer awaits the outbound fan-out dispatch call. Claude-Session: https://claude.ai/code/session_017KdUwWCFg1x9ezdbFs9zVB
…utes AdminConfirmationService's two-step confirmation flow for withdraw_fees, remove_agent, and update_fee was referenced only by its own file and test - no route ever constructed or called it, so the dual-admin safety control it was built to provide did not exist in practice and pending_admin_actions was never populated. Adds POST /api/admin/actions/:op/initiate, POST /api/admin/actions/:id/confirm, and GET /api/admin/actions/pending to routes/admin.ts, gated by a dedicated ADMIN_ACTIONS_API_KEY credential distinct from the identity-only x-user-id header used elsewhere in the router. Claude-Session: https://claude.ai/code/session_017KdUwWCFg1x9ezdbFs9zVB
createTransferGuard() was only wired to the /api/transfer no-op stub in routes/kyc.ts. The actual money-movement endpoint, POST /api/remittance in routes/remittance.ts, used a separate authMiddleware that only checked for a non-empty x-user-id header and never applied any KYC check, so it was reachable regardless of a user's KYC status, expiry, or re-verification-pending state. routes/remittance.ts now constructs its own KycUpsertService (same pattern as routes/kyc.ts) and applies transferGuard before the handler that inserts into transactions. Claude-Session: https://claude.ai/code/session_017KdUwWCFg1x9ezdbFs9zVB
|
@celina005 is attempting to deploy a commit to the Harold's projects Team on Vercel. A member of the Team first needs to authorize it. |
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
fix(push): delete permanently-invalid Expo tokens and poll delivery receipts (backend/src/device-token-service.ts, backend/src/scheduler.ts)
sendExpoPush() now inspects ticket.details.error and deregisters tokens reporting DeviceNotRegistered immediately. Successful tickets are persisted to a new expo_push_tickets table, and a new 15-minute scheduler job polls Expo's /getReceipts to catch delivery failures that never surfaced at the ticket phase and prune those tokens too.
fix(webhooks): bound inline retry wall-clock time and parallelize dispatch (dispatcher.ts, webhook-handler.ts, scheduler.ts)
dispatch() now fans out to subscribers via Promise.allSettled instead of sequentially. attemptDelivery() caps in-process retrying at WEBHOOK_INLINE_MAX_WALL_CLOCK_MS (10s default), deferring the rest to a new 2-minute background job. The inbound /webhooks/anchor handler no longer awaits the outbound dispatch call, so it responds immediately.
fix(admin): wire AdminConfirmationService to real confirm/initiate routes (routes/admin.ts)
Added POST /api/admin/actions/:op/initiate, POST /api/admin/actions/:id/confirm, and GET /api/admin/actions/pending, gated by a new ADMIN_ACTIONS_API_KEY credential. Since no execution call site exists yet for withdraw_fees/remove_agent/update_fee, confirmation authorizes out-of-band execution rather than triggering it directly (documented in the README).
fix(kyc): gate the real POST /api/remittance endpoint with transferGuard (routes/remittance.ts)
The real money-movement endpoint now runs createTransferGuard(kycUpsertService) before inserting into transactions, closing the gap where the only KYC-gated route was a no-op stub.
Linked Issue
Closes #441
Closes #442
Closes #443
Closes #444
Type of Change
Checklist
sdk/) if this touches a published packageBreaking Change Notes