Webhook Event Structure
Webhook Event Structure
This guide documents the exact structure of all webhook events emitted by QZPay. Understanding these structures is essential for building robust webhook handlers.
Event Structure
All QZPay events follow this base structure:
interface QZPayEvent<T> { /** Unique event identifier */ id: string;
/** Event type (e.g., 'customer.created') */ type: QZPayBillingEvent;
/** Event-specific data payload */ data: T;
/** Whether this is a live mode event */ livemode: boolean;
/** When the event was created */ createdAt: Date;}Event Types
Customer Events
customer.created
Emitted when a new customer is created.
{ id: "evt_abc123", type: "customer.created", data: { id: "cus_abc123", externalId: "user_789", email: "customer@example.com", name: "John Doe", metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}customer.updated
Emitted when customer information is updated.
{ id: "evt_abc124", type: "customer.updated", data: { id: "cus_abc123", externalId: "user_789", email: "newemail@example.com", // Updated field name: "John Doe", metadata: { tier: "premium" }, // Updated metadata createdAt: Date, updatedAt: Date // New timestamp }, livemode: true, createdAt: Date}customer.deleted
Emitted when a customer is deleted.
{ id: "evt_abc125", type: "customer.deleted", data: { id: "cus_abc123", externalId: "user_789", email: "customer@example.com", name: "John Doe", metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}Subscription Events
subscription.created
Emitted when a new subscription is created.
{ id: "evt_sub001", type: "subscription.created", data: { id: "sub_abc123", customerId: "cus_abc123", planId: "plan_pro", priceId: "price_pro_monthly", status: "active", quantity: 1, interval: "month", intervalCount: 1, currentPeriodStart: Date, currentPeriodEnd: Date, trialStart: null, trialEnd: null, canceledAt: null, cancelAtPeriodEnd: false, pausedAt: null, metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}subscription.updated
Emitted when subscription details change (plan, quantity, etc.).
{ id: "evt_sub002", type: "subscription.updated", data: { id: "sub_abc123", customerId: "cus_abc123", planId: "plan_enterprise", // Changed from plan_pro priceId: "price_enterprise_monthly", status: "active", quantity: 2, // Increased from 1 interval: "month", intervalCount: 1, currentPeriodStart: Date, currentPeriodEnd: Date, trialStart: null, trialEnd: null, canceledAt: null, cancelAtPeriodEnd: false, pausedAt: null, metadata: { upgraded: true }, createdAt: Date, updatedAt: Date // Updated timestamp }, livemode: true, createdAt: Date}subscription.canceled
Emitted when a subscription is canceled.
{ id: "evt_sub003", type: "subscription.canceled", data: { id: "sub_abc123", customerId: "cus_abc123", planId: "plan_pro", priceId: "price_pro_monthly", status: "canceled", quantity: 1, interval: "month", intervalCount: 1, currentPeriodStart: Date, currentPeriodEnd: Date, trialStart: null, trialEnd: null, canceledAt: Date, // Set when canceled cancelAtPeriodEnd: false, pausedAt: null, metadata: { cancellationReason: "switching_provider" }, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}subscription.paused
Emitted when a subscription is paused.
{ id: "evt_sub004", type: "subscription.paused", data: { id: "sub_abc123", customerId: "cus_abc123", planId: "plan_pro", priceId: "price_pro_monthly", status: "paused", quantity: 1, interval: "month", intervalCount: 1, currentPeriodStart: Date, currentPeriodEnd: Date, trialStart: null, trialEnd: null, canceledAt: null, cancelAtPeriodEnd: false, pausedAt: Date, // Set when paused metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}subscription.resumed
Emitted when a paused subscription is resumed.
{ id: "evt_sub005", type: "subscription.resumed", data: { id: "sub_abc123", customerId: "cus_abc123", planId: "plan_pro", priceId: "price_pro_monthly", status: "active", quantity: 1, interval: "month", intervalCount: 1, currentPeriodStart: Date, currentPeriodEnd: Date, trialStart: null, trialEnd: null, canceledAt: null, cancelAtPeriodEnd: false, pausedAt: null, // Cleared when resumed metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}subscription.trial_ending
Emitted 3 days before trial ends (configurable).
{ id: "evt_sub006", type: "subscription.trial_ending", data: { id: "sub_abc123", customerId: "cus_abc123", planId: "plan_pro", priceId: "price_pro_monthly", status: "trialing", quantity: 1, interval: "month", intervalCount: 1, currentPeriodStart: Date, currentPeriodEnd: Date, trialStart: Date, trialEnd: Date, // 3 days from now canceledAt: null, cancelAtPeriodEnd: false, pausedAt: null, metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}subscription.trial_ended
Emitted when trial period ends.
{ id: "evt_sub007", type: "subscription.trial_ended", data: { id: "sub_abc123", customerId: "cus_abc123", planId: "plan_pro", priceId: "price_pro_monthly", status: "active", // Converted from trialing quantity: 1, interval: "month", intervalCount: 1, currentPeriodStart: Date, currentPeriodEnd: Date, trialStart: Date, trialEnd: Date, // Just ended canceledAt: null, cancelAtPeriodEnd: false, pausedAt: null, metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}Payment Events
payment.succeeded
Emitted when a payment is successful.
{ id: "evt_pay001", type: "payment.succeeded", data: { id: "pay_abc123", customerId: "cus_abc123", amount: 9900, // $99.00 in cents currency: "USD", status: "succeeded", provider: "stripe", providerPaymentId: "pi_stripe123", invoiceId: "inv_abc123", subscriptionId: "sub_abc123", failureReason: null, metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}payment.failed
Emitted when a payment fails.
{ id: "evt_pay002", type: "payment.failed", data: { id: "pay_abc124", customerId: "cus_abc123", amount: 9900, currency: "USD", status: "failed", provider: "stripe", providerPaymentId: "pi_stripe124", invoiceId: "inv_abc124", subscriptionId: "sub_abc123", failureReason: "card_declined", metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}payment.refunded
Emitted when a payment is refunded.
{ id: "evt_pay003", type: "payment.refunded", data: { id: "pay_abc123", customerId: "cus_abc123", amount: 9900, currency: "USD", status: "refunded", provider: "stripe", providerPaymentId: "pi_stripe123", invoiceId: "inv_abc123", subscriptionId: "sub_abc123", failureReason: null, metadata: { refundReason: "customer_request", refundedAt: Date }, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}payment.disputed
Emitted when a payment is disputed (chargeback).
{ id: "evt_pay004", type: "payment.disputed", data: { id: "pay_abc123", customerId: "cus_abc123", amount: 9900, currency: "USD", status: "disputed", provider: "stripe", providerPaymentId: "pi_stripe123", invoiceId: "inv_abc123", subscriptionId: "sub_abc123", failureReason: null, metadata: { disputeReason: "fraudulent", disputeId: "dp_stripe123" }, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}Invoice Events
invoice.created
Emitted when an invoice is created.
{ id: "evt_inv001", type: "invoice.created", data: { id: "inv_abc123", customerId: "cus_abc123", subscriptionId: "sub_abc123", amount: 9900, currency: "USD", status: "open", dueDate: Date, paidAt: null, voidedAt: null, lines: [ { description: "Pro Plan - Monthly", quantity: 1, unitAmount: 9900, priceId: "price_pro_monthly" } ], metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}invoice.paid
Emitted when an invoice is paid.
{ id: "evt_inv002", type: "invoice.paid", data: { id: "inv_abc123", customerId: "cus_abc123", subscriptionId: "sub_abc123", amount: 9900, currency: "USD", status: "paid", dueDate: Date, paidAt: Date, // Set when paid voidedAt: null, lines: [...], metadata: { paymentId: "pay_abc123" }, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}invoice.payment_failed
Emitted when invoice payment fails.
{ id: "evt_inv003", type: "invoice.payment_failed", data: { id: "inv_abc124", customerId: "cus_abc123", subscriptionId: "sub_abc123", amount: 9900, currency: "USD", status: "open", dueDate: Date, paidAt: null, voidedAt: null, lines: [...], metadata: { paymentAttempts: 1, lastPaymentError: "card_declined" }, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}invoice.voided
Emitted when an invoice is voided.
{ id: "evt_inv004", type: "invoice.voided", data: { id: "inv_abc125", customerId: "cus_abc123", subscriptionId: "sub_abc123", amount: 9900, currency: "USD", status: "voided", dueDate: Date, paidAt: null, voidedAt: Date, // Set when voided lines: [...], metadata: { voidReason: "duplicate" }, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}Checkout Events
checkout.completed
Emitted when checkout session completes successfully.
{ id: "evt_co001", type: "checkout.completed", data: { id: "cs_abc123", customerId: "cus_abc123", planId: "plan_pro", priceId: "price_pro_monthly", mode: "subscription", status: "completed", successUrl: "https://example.com/success", cancelUrl: "https://example.com/cancel", metadata: {}, completedAt: Date, expiresAt: Date, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}checkout.expired
Emitted when checkout session expires.
{ id: "evt_co002", type: "checkout.expired", data: { id: "cs_abc124", customerId: null, planId: "plan_pro", priceId: "price_pro_monthly", mode: "subscription", status: "expired", successUrl: "https://example.com/success", cancelUrl: "https://example.com/cancel", metadata: {}, completedAt: null, expiresAt: Date, // Expired timestamp createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}Vendor Events
vendor.created
Emitted when a vendor is created.
{ id: "evt_ven001", type: "vendor.created", data: { id: "ven_abc123", name: "Acme Inc", email: "vendor@acme.com", revenueSharePercentage: 80, // 80% to vendor status: "active", metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}vendor.updated
Emitted when vendor information is updated.
{ id: "evt_ven002", type: "vendor.updated", data: { id: "ven_abc123", name: "Acme Inc", email: "vendor@acme.com", revenueSharePercentage: 85, // Updated from 80% status: "active", metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}vendor.payout
Emitted when a payout is made to a vendor.
{ id: "evt_ven003", type: "vendor.payout", data: { id: "po_abc123", vendorId: "ven_abc123", amount: 8000, // $80.00 currency: "USD", status: "completed", periodStart: Date, periodEnd: Date, metadata: { transactionId: "txn_123", method: "bank_transfer" }, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}Add-on Events
addon.created
Emitted when a new add-on is created.
{ id: "evt_addon001", type: "addon.created", data: { id: "addon_abc123", name: "Extra Storage", description: "100GB additional storage", active: true, unitAmount: 1000, currency: "USD", billingInterval: "month", billingIntervalCount: 1, compatiblePlanIds: [], allowMultiple: true, maxQuantity: null, entitlements: ["extra_storage"], limits: [ { key: "storage_gb", value: 100, action: "increment" } ], metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}addon.updated
Emitted when an add-on is updated.
{ id: "evt_addon002", type: "addon.updated", data: { id: "addon_abc123", name: "Extra Storage", description: "100GB additional storage", active: true, unitAmount: 1200, // Price updated currency: "USD", billingInterval: "month", billingIntervalCount: 1, compatiblePlanIds: [], allowMultiple: true, maxQuantity: null, entitlements: ["extra_storage"], limits: [...], metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}addon.deleted
Emitted when an add-on is deleted.
{ id: "evt_addon003", type: "addon.deleted", data: { id: "addon_abc123", name: "Extra Storage", description: "100GB additional storage", active: false, // Set to inactive unitAmount: 1000, currency: "USD", billingInterval: "month", billingIntervalCount: 1, compatiblePlanIds: [], allowMultiple: true, maxQuantity: null, entitlements: ["extra_storage"], limits: [...], metadata: {}, createdAt: Date, updatedAt: Date }, livemode: true, createdAt: Date}Subscription Add-on Events
subscription.addon_added
Emitted when an add-on is added to a subscription.
{ id: "evt_subaddon001", type: "subscription.addon_added", data: { subscription: { id: "sub_abc123", customerId: "cus_abc123", planId: "plan_pro", // ... full subscription object }, subscriptionAddOn: { id: "sa_abc123", subscriptionId: "sub_abc123", addOnId: "addon_abc123", quantity: 2, unitAmount: 1000, currency: "USD", status: "active", addedAt: Date, canceledAt: null, expiresAt: null, metadata: {}, createdAt: Date, updatedAt: Date }, addon: { id: "addon_abc123", name: "Extra Storage", // ... full add-on object } }, livemode: true, createdAt: Date}subscription.addon_removed
Emitted when an add-on is removed from a subscription.
{ id: "evt_subaddon002", type: "subscription.addon_removed", data: { subscription: { /* full subscription */ }, subscriptionAddOn: { id: "sa_abc123", subscriptionId: "sub_abc123", addOnId: "addon_abc123", quantity: 2, unitAmount: 1000, currency: "USD", status: "canceled", addedAt: Date, canceledAt: Date, // Set when removed expiresAt: null, metadata: {}, createdAt: Date, updatedAt: Date }, addon: { /* full add-on */ } }, livemode: true, createdAt: Date}subscription.addon_updated
Emitted when a subscription add-on is updated (e.g., quantity changed).
{ id: "evt_subaddon003", type: "subscription.addon_updated", data: { subscription: { /* full subscription */ }, subscriptionAddOn: { id: "sa_abc123", subscriptionId: "sub_abc123", addOnId: "addon_abc123", quantity: 5, // Updated from 2 unitAmount: 1000, currency: "USD", status: "active", addedAt: Date, canceledAt: null, expiresAt: null, metadata: {}, createdAt: Date, updatedAt: Date }, addon: { /* full add-on */ } }, livemode: true, createdAt: Date}Event Handling Example
import type { QZPayEvent, QZPayEventMap } from '@qazuor/qzpay-core';
// Type-safe event handlerbilling.on('subscription.created', async (event: QZPayEvent<QZPayEventMap['subscription.created']>) => { console.log('New subscription:', event.data.id); console.log('Customer:', event.data.customerId); console.log('Plan:', event.data.planId); console.log('Status:', event.data.status);});
// Handle payment failuresbilling.on('payment.failed', async (event) => { await sendEmail(event.data.customerId, 'payment_failed', { amount: event.data.amount / 100, reason: event.data.failureReason, retryUrl: `${APP_URL}/billing/retry` });});
// Handle add-on additionsbilling.on('subscription.addon_added', async (event) => { console.log('Add-on added to subscription'); console.log('Subscription:', event.data.subscription.id); console.log('Add-on:', event.data.addon.name); console.log('Quantity:', event.data.subscriptionAddOn.quantity);});Related
- Webhook Handling - Setting up webhooks
- Webhooks Concept - Understanding webhooks
- API Reference - Full API documentation