Receive a Payssage webhook delivery
Ingestion endpoint called by Payssage when a payment event occurs on an intent. The body is the Payssage delivery envelope (D2) — the correlation key is `intent_id`, never a provider-specific id: ```json { "intent_id": "...", "wallet_id": "...", "provider": "mercadopago", "external_id": "<provider_payment_id>", "event_type": "payment.succeeded", "status_detail": "high_risk", "payload": { "...": "raw provider payload" } } ``` Authenticated by the `X-Payssage-Signature` header — hex (HMAC-SHA256(raw body, `PAYSSAGE_WEBHOOK_SECRET`)). A signature mismatch is rejected with 400 (Payssage does not retry). Returns 200 after successful processing so Payssage stops retrying; returns non-2xx when the purchase cannot be correlated yet (card race, D3) so Payssage retries. This is the only component that confirms payment — checkout never self-approves (D3). Public: Payssage cannot send an Authorization header.
/webhooks/payssageIngestion endpoint called by Payssage when a payment event occurs on
an intent. The body is the Payssage delivery envelope (D2) — the
correlation key is intent_id, never a provider-specific id:
{
"intent_id": "...", "wallet_id": "...", "provider": "mercadopago",
"external_id": "<provider_payment_id>",
"event_type": "payment.succeeded",
"status_detail": "high_risk",
"payload": { "...": "raw provider payload" }
}Authenticated by the X-Payssage-Signature header — hex
(HMAC-SHA256(raw body, PAYSSAGE_WEBHOOK_SECRET)). A signature
mismatch is rejected with 400 (Payssage does not retry). Returns 200
after successful processing so Payssage stops retrying; returns
non-2xx when the purchase cannot be correlated yet (card race, D3)
so Payssage retries. This is the only component that confirms
payment — checkout never self-approves (D3). Public: Payssage
cannot send an Authorization header.
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
The Payssage webhook delivery envelope (D2). Correlation is by
intent_id — the receiver loads the purchase on it, never on a
provider-specific id. Every field is required: a malformed body is
rejected with 400 and payssage does not retry.
Response Body
application/problem+json
application/problem+json
curl -X POST "https://example.com/webhooks/payssage" \ -H "Content-Type: application/json" \ -d '{ "intent_id": "05704cef-194d-4987-b2cb-c2c20c9cd88a", "wallet_id": "d9a8fbfc-848f-43a2-9168-911ed3a04a48", "provider": "string", "external_id": "string", "event_type": "string", "payload": {} }'{
"detail": "string",
"instance": "../dictionary",
"errors": [
{
"field": "string",
"message": "string"
}
],
"type": "http://example.com",
"status": 400,
"title": "Validation Failed"
}{
"detail": "string",
"instance": "../dictionary",
"errors": [
{
"field": "string",
"message": "string"
}
],
"type": "http://example.com",
"status": 500,
"title": "Internal Server Error"
}Revoke a signature POST
Revokes a previously fulfilled signature. Authenticated by the signature token passed as the `token` query parameter (NOT a JWT).
List my purchases GET
Lists the authenticated user's purchases with their items, newest first. The purchases table is the record of truth (issue #61); items come from `purchase_items` — the availability ledger (D4). Read-only; no state transitions.