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:
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.
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-Signatureheader. The webhook schema should include asigningSecretfield, 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–271Acceptance Criteria:
signingSecretfield toMerchantSettingsZod schema (auto-generated on merchant create if not provided)X-BettaPay-SignatureheaderPOST /api/webhooks/:id/rotate-secretendpoint to rotate the signing secretdocs/webhooks.md) explaining how merchants verify signaturesTechnical Notes:
t={timestamp},v1={hex(signature)}(Stripe-style).crypto.createHmac('sha256', secret).update(${timestamp}.${body}).digest('hex').X-BettaPay-Signatureheader alongside the existing JSON body.