Webhook Event Reference
Every webhook Von Payments delivers shares the same envelope. The type field tells you which event it is; the data field holds the per-event payload. The event families below cover what the public ecommerce integration surface supports today. The event picker at /dashboard/developers/webhooks is the canonical list of what's available to subscribe to from your dashboard right now.
Event families are added to the catalog as each capability ships. New event types may appear without an SDK version bump — handlers should treat unknown type values as a no-op (return 200 OK, log for inspection, do not raise).
Envelope
Every event Von Payments delivers is wrapped in the same envelope:
{
"id": "vp_evt_live_8x4n2pq7m1",
"type": "charge.succeeded",
"created": 1728936000,
"livemode": true,
"merchant_id": "b6b8d25f-80d5-4b31-8ac6-fd3c5727c4ce",
"data": { /* per-event payload, see sections below */ }
}
| Field | Type | Notes |
|---|---|---|
id | string | Unique per outbound event. Use this for idempotent processing — the same event ID is never delivered twice with different payloads. |
type | string | Event type from the catalog below. Treat unknown event types as a no-op (forward-compatible: new types may appear without an SDK bump). |
created | integer | Unix seconds when Von Payments emitted the event. For replay-window enforcement, compare against the t= field inside the x-vonpay-signature header (there is no separate timestamp header). |
livemode | boolean | true for events from a live merchant; false for sandbox / test-mode dispatches. |
merchant_id | string | The merchant the event belongs to (a UUID). On a multi-tenant platform integrator, route on this value. |
data | object | Per-event payload. Shape is fixed per type; see sections below. |
All amounts in minor units (cents for USD, pence for GBP, etc.). All currency codes uppercase ISO-4217. All timestamps as unix seconds.
Charge events
The charge.* family fires for the card-charge lifecycle when a session reaches settlement (or a direct /v1/payment_intents charge succeeds without a session wrapper).
charge.succeeded
A charge completed successfully. Fires after the buyer finishes checkout and the processor confirms the charge.
{
"id": "vp_evt_live_8x4n2pq7m1",
"type": "charge.succeeded",
"created": 1728936000,
"livemode": true,
"merchant_id": "b6b8d25f-80d5-4b31-8ac6-fd3c5727c4ce",
"data": {
"session_id": "vp_cs_test_kJq7Lp...",
"payment_intent_id": "vpi_9f2ndx7k...",
"transaction_id": "vp_tx_9f2nd...",
"amount": 1499,
"currency": "USD",
"card": { "brand": "visa", "last4": "4242" }
}
}
data field | Type | Description |
|---|---|---|
session_id | string | null | The session this charge belongs to. Null for direct-charge flows that do not go through /v1/sessions. |
payment_intent_id | string | null | Payment intent (vpi_*) this charge settled. Null on hosted-direct flows that never minted an intent. |
transaction_id | string | null | The charge transaction ID. Use this for reconciliation against the merchant's transaction list. |
amount | integer | Amount charged, in minor units. |
currency | string | ISO-4217 uppercase. |
card | object | null | PCI-safe card presentation — { "brand": ..., "last4": ... }. brand is one of visa, mastercard, amex, discover, diners, jcb, unionpay, unknown; last4 is 4 digits. null until card enrichment is active for the merchant. No PAN, BIN, or fingerprint is ever included. |
charge.failed
A charge attempt failed. Includes a failure_reason describing the decline.
{
"id": "vp_evt_live_3x8m1q4r5s",
"type": "charge.failed",
"created": 1728936010,
"livemode": true,
"merchant_id": "b6b8d25f-80d5-4b31-8ac6-fd3c5727c4ce",
"data": {
"session_id": "vp_cs_test_pK7nLm...",
"payment_intent_id": "vpi_8d4nex2p...",
"transaction_id": "vp_tx_8d4ne...",
"amount": 1499,
"currency": "USD",
"failure_reason": "Your card was declined.",
"failure_code": "card_declined",
"network_decline_code": "05",
"card": { "brand": "visa", "last4": "4242" }
}
}
failure_reason is a human-readable summary; failure_code is the normalized decline code (card_declined, insufficient_funds, expired_card, fraudulent, processing_error, …) you should branch on; network_decline_code is the raw issuer code (05, 51, …). All three are string | null. Surface failure_reason to your UI so the buyer can act on it. card carries the PCI-safe { brand, last4 } (or null until card enrichment is active) — same shape as on charge.succeeded.
charge.refunded
A charge was refunded — full or partial. Fires once per refund record. A fully-refunded charge that was issued in two partial refunds fires this event twice.
{
"id": "vp_evt_live_7t6r5w4v3u",
"type": "charge.refunded",
"created": 1729022400,
"livemode": true,
"merchant_id": "b6b8d25f-80d5-4b31-8ac6-fd3c5727c4ce",
"data": {
"session_id": "vp_cs_test_kJq7Lp...",
"payment_intent_id": "vpi_9f2ndx7k...",
"transaction_id": "vp_tx_9f2nd...",
"refund_id": "vpr_3kQpL2nM...",
"amount": 500,
"currency": "USD",
"reason": "customer_request",
"is_partial": true,
"original_charge_amount": 1499,
"card": { "brand": "visa", "last4": "4242" }
}
}
data field | Type | Description |
|---|---|---|
payment_intent_id | string | null | Payment intent (vpi_*) of the original charge. |
refund_id | string | null | Refund record id (vpr_*) — identifies WHICH refund in a multi-partial sequence this event references. |
amount | integer | Amount of THIS refund in minor units (not the original charge total — see original_charge_amount). To get the cumulative refunded total, sum amount across all charge.refunded events for the same transaction_id. |
reason | string | null | Refund reason — customer_request / duplicate / fraudulent / merchant free-form. |
is_partial | boolean | null | True when amount < original_charge_amount. |
card | object | null | PCI-safe { brand, last4 } of the refunded card (or null until card enrichment is active) — same shape as on charge.succeeded. |
original_charge_amount | integer | null | Full charge total before any refund — lets you compute the remaining refundable balance without a round-trip. |
Payment intent events
The payment_intent.* family fires for the discrete-lifecycle API (POST /v1/payment_intents). If your integration uses the hosted-checkout sessions flow only, you can ignore these. If you call paymentIntents.create directly via the SDK, these are the events that confirm terminal state.
payment_intent.succeeded
A payment intent reached terminal succeeded status.
{
"id": "vp_evt_live_5p3q2t1u8v",
"type": "payment_intent.succeeded",
"created": 1728936000,
"livemode": true,
"merchant_id": "b6b8d25f-80d5-4b31-8ac6-fd3c5727c4ce",
"data": {
"session_id": null,
"payment_intent_id": "vpi_abc123x7...",
"transaction_id": "vp_tx_abc123",
"amount": 1499,
"currency": "USD"
}
}
session_id is null for payment intents created outside the hosted-checkout flow.
payment_intent.failed
A payment intent reached terminal failed status.
{
"id": "vp_evt_live_9w8x7y6z1a",
"type": "payment_intent.failed",
"created": 1728936010,
"livemode": true,
"merchant_id": "b6b8d25f-80d5-4b31-8ac6-fd3c5727c4ce",
"data": {
"session_id": null,
"payment_intent_id": "vpi_xyz789k2...",
"transaction_id": "vp_tx_xyz789",
"amount": 1499,
"currency": "USD",
"failure_reason": "Your card was declined.",
"failure_code": "card_declined",
"network_decline_code": "05"
}
}
payment_intent.cancelled
A payment intent was cancelled before reaching a terminal state. Typically fires when an authorized intent is voided before capture.
{
"id": "vp_evt_live_2b1c4d3e5f",
"type": "payment_intent.cancelled",
"created": 1728936020,
"livemode": true,
"merchant_id": "b6b8d25f-80d5-4b31-8ac6-fd3c5727c4ce",
"data": {
"session_id": null,
"payment_intent_id": "vpi_def456m9...",
"transaction_id": "vp_tx_def456",
"amount": 1499,
"currency": "USD",
"cancellation_reason": "buyer_abandoned"
}
}
Forward compatibility
New event types may be added to this catalog without an SDK version bump. Handlers should treat unknown type values as no-ops (return 200 OK, log for inspection, do not raise). New fields may be added to existing data payloads at any time; consumers should ignore unknown keys without failing.
The charge.* and payment_intent.* events above are the ones you can subscribe to via enabledEvents (the dashboard event picker). Von Payments also delivers platform-level events your endpoint may receive but that are not individually selectable — session.succeeded / session.failed, dispute.created / dispute.won / dispute.lost, application.approved / application.denied, payout.paid / payout.failed, and merchant.ready_for_payments. They share the same envelope; the SDK's WebhookEvent union models them so you can switch on type.
Related
- Webhooks — overview: how the surface works, registering endpoints, retry behavior
- Webhook Verification — HMAC-SHA256 verification across languages
- Webhook Signing Secrets — create, view-once, rotate