Skip to content

Add Webhook Signing Secret for Webhook Payload Verification #256

Description

@therealjhay

Category: New Features & Improvements
Difficulty: High

Description:
Webhook payloads delivered to merchants are sent as plain JSON POST requests with no HMAC signature. Any attacker who intercepts the webhook URL (e.g., via DNS poisoning, MITM, or leaked webhook URL) can replay or spoof webhook payloads. The settlement engine and indexer should sign webhook payloads with a HMAC-SHA256 signature using a merchant-specific signing secret, delivered in the X-BettaPay-Signature header. The webhook schema should include a signingSecret field, and the merchant settings panel should allow rotating the secret. This is standard practice for payment platforms like Stripe and PayPal.

Location:
services/indexer/src/index.ts:110–115, services/settlement-engine/src/index.ts:136–144, shared/validation/schemas.ts:261–271

Acceptance Criteria:

  • Add signingSecret field to MerchantSettings Zod schema (auto-generated on merchant create if not provided)
  • Generate and store a signing secret per webhook subscription (or per merchant)
  • Sign the webhook body with HMAC-SHA256 and include the signature in X-BettaPay-Signature header
  • Include the timestamp in the signature payload (replay protection)
  • Add a POST /api/webhooks/:id/rotate-secret endpoint to rotate the signing secret
  • Write tests verifying signature generation and rotation
  • Add documentation (docs/webhooks.md) explaining how merchants verify signatures

Technical Notes:

  • Signature format: t={timestamp},v1={hex(signature)} (Stripe-style).
  • Use crypto.createHmac('sha256', secret).update(${timestamp}.${body}).digest('hex').
  • Each webhook delivery should include the X-BettaPay-Signature header alongside the existing JSON body.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions