Parses a Paysafe webhook delivery into a UnifiedWebhookEvent. Call it only
after verifyPaysafeWebhookSignature accepted the same raw body.
The envelope is read where Paysafe documents it: top-level payload,
eventName, resourceId and eventDate, or the same fields nested under
variables, as the Bacs page shows them. The older eventType/event
name fields are still read; the top-level type is the resource category
("PAYMENT"), never the event name.
pspPaymentId names a payment and nothing else:
a bank return (type "PAYMENT_RETURN", or a PAYMENT_RETURN… /
PAYMENT_RETURNED… event) reports the returned payment from
payload.paymentId, never the return's own payload.id;
other payment events report payload.id, else resourceId;
refund events report the refund as refundId and no pspPaymentId, since
the refund payload names no payment;
handle, settlement and every other resource leave it unset (correlate those
by the payload merchantRefNum on raw).
amount is left out when the payload's currency is one the adapter refuses
(see PaysafeServerAdapter), whose Paysafe minor units may not be
PayFanout's; currency and every other field are reported as for any
currency. A payload that states no currency, as Paysafe does not promise one
on card refunds, keeps its amount as delivered.
id survives Paysafe's redeliveries. Paysafe sends no event id and repeats a
notification with the next attemptNumber ("1", "2", "3"), so a hash of the
raw bytes would give every attempt its own id. The id is paysafe_ + the
SHA-256 hex of the JSON array [name, resourceId, status, time]: the
normalized event name, the resource id (payload.id, else resourceId), the
payload status, and the payload statusTime, else its txnTime, else the
envelope eventDate. Distinct notifications agreeing on all four share an id
(Paysafe's card and refund webhook examples carry no statusTime), so a host
that must see every transition re-reads with retrievePayment /
retrieveRefund whether or not the id was seen — except a bank return, which
no documented read reflects: act on the return event itself. A top-level id
is ignored — Paysafe's webhook page
shows the resource's own id there — and a body naming no resource hashes its
key-sorted JSON without attemptNumber.
Parses a Paysafe webhook delivery into a UnifiedWebhookEvent. Call it only after verifyPaysafeWebhookSignature accepted the same raw body.
The envelope is read where Paysafe documents it: top-level
payload,eventName,resourceIdandeventDate, or the same fields nested undervariables, as the Bacs page shows them. The oldereventType/eventname fields are still read; the top-leveltypeis the resource category ("PAYMENT"), never the event name.pspPaymentIdnames a payment and nothing else:type"PAYMENT_RETURN", or a PAYMENT_RETURN… / PAYMENT_RETURNED… event) reports the returned payment frompayload.paymentId, never the return's ownpayload.id;payload.id, elseresourceId;refundIdand nopspPaymentId, since the refund payload names no payment;merchantRefNumonraw).amountis left out when the payload's currency is one the adapter refuses (see PaysafeServerAdapter), whose Paysafe minor units may not be PayFanout's;currencyand every other field are reported as for any currency. A payload that states no currency, as Paysafe does not promise one on card refunds, keeps itsamountas delivered.idsurvives Paysafe's redeliveries. Paysafe sends no event id and repeats a notification with the nextattemptNumber("1", "2", "3"), so a hash of the raw bytes would give every attempt its own id. The id ispaysafe_+ the SHA-256 hex of the JSON array[name, resourceId, status, time]: the normalized event name, the resource id (payload.id, elseresourceId), the payloadstatus, and the payloadstatusTime, else itstxnTime, else the envelopeeventDate. Distinct notifications agreeing on all four share an id (Paysafe's card and refund webhook examples carry nostatusTime), so a host that must see every transition re-reads withretrievePayment/retrieveRefundwhether or not the id was seen — except a bank return, which no documented read reflects: act on the return event itself. A top-levelidis ignored — Paysafe's webhook page shows the resource's own id there — and a body naming no resource hashes its key-sorted JSON withoutattemptNumber.