Payment webhooks
How HyoNova receives payment provider webhooks — raw body, signature verification, deduplication, ordering, atomic processing, and which HTTP answers make providers retry.
A payment method appears only after HyoNova has set up its payment provider.
Payment providers tell HyoNova about payments and subscriptions by calling a webhook. These webhooks — not the browser coming back from a payment page — are what change a company's subscription. This page describes the receiving side. (For the webhooks Hyo Mail sends to your systems, see Webhooks.)
Endpoint#
POST https://<HyoNova product API host>/api/v1/webhooks/payments/<provider><provider> is the provider's webhook path: fastspring, triple-a or stripe. Bank transfer has no webhook. Control Center shows the exact URL on each provider instance. Unknown paths answer 404, an empty body 400. Bodies are limited to 512 KiB and the endpoint is rate limited.
Processing, step by step#
- Raw body. The body is read as raw bytes, because the signature covers the exact bytes. No JSON parsing happens before verification.
- Signature. HyoNova tries each instance of that provider (test and live can coexist) with its signing secret, using a constant-time comparison. No match → 401
WEBHOOK_SIGNATURE_INVALIDand nothing is stored. - Normalize. The adapter turns the payload into provider-independent canonical events. One notification can produce several events.
- Deduplicate. Each event is recorded by its key (provider, event id). A key already processed is a
duplicateand changes nothing. - Environment check. A test event never changes data of a live instance, and the reverse.
- Match the company from references HyoNova stored itself (checkout, subscription, payment, order, invoice or provider customer). Ids the provider echoes back are trusted only when they match a stored record.
- Order. An event older than the last one applied to a subscription — by the provider's sequence number when it has one, else by time — is ignored and never moves a subscription back.
- Apply atomically. The change, the company's billing timeline entry, the outbox event for notifications and the audit entry commit together in one transaction.
Signatures#
| Provider | Header | Signed content |
|---|---|---|
| FastSpring | X-FS-Signature | Base64 HMAC-SHA256 of the raw body with the webhook HMAC secret |
| Triple-A | Triplea-Signature: t=<unix>,v1=<hex> | HMAC-SHA256 of <t>.<raw body> with the notification secret; 300-second tolerance |
| Stripe | Stripe-Signature: t=<unix>,v1=<hex> | HMAC-SHA256 of <t>.<raw body> with the endpoint secret; 300-second tolerance |
HTTP answers#
| Answer | When | Provider behaviour |
|---|---|---|
| 200 | Every event was processed, ignored or a duplicate. The body lists the processed event ids | Done |
| 401 | No instance verified the signature | Nothing stored; fix the secret |
500 PROCESSING_FAILED | At least one event failed; it is stored as failed with a safe error | The provider retries; processed events are deduplicated, the failed one runs again |
| 404 / 400 | Unknown provider path / empty body | — |
Security notes#
- No session or cookie is involved. The CSRF check is skipped only for this exact path pattern; the signature is the authentication.
- Secrets are read from the secret store for verification only, never logged.
- Failed events that stay failed appear in Control Center's reconciliation as attention items.
Was this page helpful?
Thanks. Page ratings are not collected yet, so nothing was sent. If something is missing or wrong, see the Get help page.