Decodes an already-parsed purchase webhook body into a typed PurchaseEvent. The money
field uses the same decimal-string codec as the rest of the API, so the amount never passes
through a JSON number; a wrong-shape body or bad amount throws, letting the server reply 400
before anything reaches the ledger. Purchase is the only kind with a body decoder — the other
kinds map straight from a WebhookEvent via toOperation.
Decodes an already-parsed purchase webhook body into a typed PurchaseEvent. The money field uses the same decimal-string codec as the rest of the API, so the amount never passes through a JSON number; a wrong-shape body or bad amount throws, letting the server reply 400 before anything reaches the ledger. Purchase is the only kind with a body decoder — the other kinds map straight from a WebhookEvent via toOperation.