Wise Webhook Receiver
Inbound Wise webhook receiver -- AP2 auto-settlement (MP #42).
Contract (always-200):
- HTTP 200 on every path, including signature failure, parse failure, unknown event type, and DB error. Failures are recorded via StructuredLogger; the JSON body status + reason fields describe the outcome.
Validation order (MP rule #11):
- Read raw body + headers
- RSA-SHA256 signature verification against Wise's PUBLISHED production public key (NO DB operations until this passes)
- JSON parse
- X-Test-Notification handling (Wise sends this to verify the callback URL when a subscription is being set up; we MUST respond 2xx)
- Event normalization via parse_wise_event
- Filter: must be an inbound-credit event with transaction_type == "credit" AND currency == "USD"
- 3-criteria settlement matcher
Security (MP rule #8, Path A):
- No shared secret exists in the Wise protocol; verification uses the public key embedded at module-scope (safe -- public material)
- X-Signature-SHA256 header value is logged only as an 8-char prefix for forensic correlation
- The cryptography library handles constant-time RSA verification internally; no application-level timing leaks
Coexistence with execute-time poll (check_wise_inbound_transfer): The existing poll runs synchronously during /api/ap2/execute and may have already set settlement_confirmed=true on a row. This webhook UPDATEs the same row; the matcher only considers rows with settlement_confirmed=false in its candidate pool, so a previously-confirmed row is silently passed over (no double- confirm, no false alarm).
post/api/webhooks/wise
Request
- Base URL: https://api.merchant-0.com (the document declares no server; this is the origin it was published from)
- URL: https://api.merchant-0.com/api/webhooks/wise
- Auth: none declared
Response
Successful Response
unknown required
Changes
No recorded changes to this operation across all 1 revision of this API.