UNPKG

vue3-stripe-kit

Version:

Complete Vue 3 Stripe integration with Payment Elements, Checkout, Subscriptions. TypeScript support, composables, components, modular architecture for payments, billing, and e-commerce

382 lines (379 loc) 11.7 kB
import { ref, onUnmounted } from 'vue'; import { g as getGlobalConfig, a as buildRequestHeaders } from './config-DTKmfXwX.mjs'; function useWebhooks(config = {}) { const loading = ref(false); const error = ref(null); const events = ref([]); const stats = ref({ totalEvents: 0, successfulEvents: 0, failedEvents: 0, eventsByType: {} }); const isMonitoring = ref(false); const globalConfig = getGlobalConfig(); const mergedConfig = { ...globalConfig, ...config }; const eventHandlers = /* @__PURE__ */ new Map(); let monitoringInterval = null; const getEndpoint = (key, defaultPath, webhookConfig) => { const finalConfig = { ...mergedConfig, ...webhookConfig }; const endpoint = finalConfig[key]; if (typeof endpoint === "string") { return endpoint; } if (finalConfig.apiBaseUrl) { return `${finalConfig.apiBaseUrl}${defaultPath}`; } return defaultPath; }; const makeRequest = async (endpoint, options, retries = 3) => { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), mergedConfig.requestTimeout || 3e4); try { const response = await fetch(endpoint, { ...options, signal: controller.signal, headers: buildRequestHeaders(mergedConfig, options.headers) }); clearTimeout(timeoutId); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: "Request failed" })); throw { type: "api_error", code: response.status.toString(), message: errorData.message || `HTTP ${response.status}` }; } return await response.json(); } catch (err) { clearTimeout(timeoutId); if (err.name === "AbortError") { throw { type: "timeout_error", message: "Request timeout" }; } if (retries > 0 && !err.type) { await new Promise((resolve) => setTimeout(resolve, 1e3 * (4 - retries))); return makeRequest(endpoint, options, retries - 1); } throw err; } }; const createWebhookEndpoint = async (url, enabledEvents, webhookConfig) => { loading.value = true; error.value = null; try { const endpoint = getEndpoint("createWebhookEndpoint", "/api/stripe/webhook_endpoints", webhookConfig); const payload = { url, enabled_events: enabledEvents, description: `Webhook endpoint for ${url}`, metadata: {} }; const result = await makeRequest(endpoint, { method: "POST", body: JSON.stringify(payload) }); return result; } catch (err) { error.value = err; throw err; } finally { loading.value = false; } }; const updateWebhookEndpoint = async (endpointId, updates, webhookConfig) => { loading.value = true; error.value = null; try { const endpoint = getEndpoint("updateWebhookEndpoint", `/api/stripe/webhook_endpoints/${endpointId}`, webhookConfig); const result = await makeRequest(endpoint, { method: "PATCH", body: JSON.stringify(updates) }); return result; } catch (err) { error.value = err; throw err; } finally { loading.value = false; } }; const deleteWebhookEndpoint = async (endpointId, webhookConfig) => { loading.value = true; error.value = null; try { const endpoint = getEndpoint("deleteWebhookEndpoint", `/api/stripe/webhook_endpoints/${endpointId}`, webhookConfig); await makeRequest(endpoint, { method: "DELETE" }); } catch (err) { error.value = err; throw err; } finally { loading.value = false; } }; const listWebhookEndpoints = async (webhookConfig) => { loading.value = true; error.value = null; try { const endpoint = getEndpoint("listWebhookEndpoint", "/api/stripe/webhook_endpoints", webhookConfig); const result = await makeRequest(endpoint, { method: "GET" }); return result.data; } catch (err) { error.value = err; throw err; } finally { loading.value = false; } }; const fetchEvents = async (limit = 100, webhookConfig) => { loading.value = true; error.value = null; try { const endpoint = getEndpoint("listEventsEndpoint", "/api/stripe/events", webhookConfig); const queryParams = new URLSearchParams({ limit: limit.toString() }); const result = await makeRequest(`${endpoint}?${queryParams}`, { method: "GET" }); events.value = result.data; updateStats(result.data); return result.data; } catch (err) { error.value = err; throw err; } finally { loading.value = false; } }; const fetchEvent = async (eventId, webhookConfig) => { loading.value = true; error.value = null; try { const endpoint = getEndpoint("retrieveEventEndpoint", `/api/stripe/events/${eventId}`, webhookConfig); const result = await makeRequest(endpoint, { method: "GET" }); return result; } catch (err) { error.value = err; throw err; } finally { loading.value = false; } }; const resendEvent = async (eventId, endpointId, webhookConfig) => { loading.value = true; error.value = null; try { const endpoint = getEndpoint("resendEventEndpoint", `/api/stripe/events/${eventId}/resend`, webhookConfig); await makeRequest(endpoint, { method: "POST", body: JSON.stringify({ endpoint_id: endpointId }) }); } catch (err) { error.value = err; throw err; } finally { loading.value = false; } }; const startMonitoring = (webhookConfig) => { if (isMonitoring.value) return; const finalConfig = { ...mergedConfig, ...webhookConfig }; const interval = finalConfig.pollingInterval || 5e3; isMonitoring.value = true; monitoringInterval = window.setInterval(async () => { try { const newEvents = await fetchEvents(finalConfig.maxEvents || 50, webhookConfig); newEvents.forEach((event) => { const handler = eventHandlers.get(event.type); if (handler) { try { handler.handler(event); } catch (error2) { console.error(`Error in webhook handler for ${event.type}:`, error2); } } }); } catch (error2) { console.error("Error fetching webhook events:", error2); } }, interval); console.log("✅ Webhook monitoring started with interval:", interval); }; const stopMonitoring = () => { if (monitoringInterval) { clearInterval(monitoringInterval); monitoringInterval = null; } isMonitoring.value = false; console.log("⏹️ Webhook monitoring stopped"); }; const registerEventHandler = (handler) => { eventHandlers.set(handler.eventType, handler); console.log(`✅ Registered webhook handler for ${handler.eventType}`); }; const unregisterEventHandler = (eventType) => { eventHandlers.delete(eventType); console.log(`❌ Unregistered webhook handler for ${eventType}`); }; const clearEventHandlers = () => { eventHandlers.clear(); console.log("🗑️ Cleared all webhook handlers"); }; const simulateEvent = async (eventType, testData) => { const simulatedEvent = { id: `evt_test_${Date.now()}`, object: "event", type: eventType, created: Math.floor(Date.now() / 1e3), data: { object: testData || generateTestData(eventType) }, livemode: false, pending_webhooks: 0 }; events.value.unshift(simulatedEvent); updateStats([...events.value]); const handler = eventHandlers.get(eventType); if (handler) { await handler.handler(simulatedEvent); } console.log("🧪 Simulated webhook event:", eventType); return simulatedEvent; }; const testWebhookEndpoint = async (endpointId, eventType) => { try { loading.value = true; const testEvent = await simulateEvent(eventType); await resendEvent(testEvent.id, endpointId); return true; } catch (error2) { console.error("Webhook endpoint test failed:", error2); return false; } finally { loading.value = false; } }; const validateWebhookSignature = (payload, signature, secret) => { try { const expectedSignature = `sha256=${btoa(payload + secret)}`; return signature === expectedSignature; } catch (error2) { console.error("Signature validation error:", error2); return false; } }; const updateStats = (eventList) => { const newStats = { totalEvents: eventList.length, successfulEvents: eventList.filter((e) => !e.type.includes("failed")).length, failedEvents: eventList.filter((e) => e.type.includes("failed")).length, lastEventTime: eventList.length > 0 ? eventList[0].created : void 0, eventsByType: {} }; eventList.forEach((event) => { newStats.eventsByType[event.type] = (newStats.eventsByType[event.type] || 0) + 1; }); stats.value = newStats; }; const clearEvents = () => { events.value = []; stats.value = { totalEvents: 0, successfulEvents: 0, failedEvents: 0, eventsByType: {} }; }; const exportEvents = (format) => { if (format === "json") { return JSON.stringify(events.value, null, 2); } const headers = ["ID", "Type", "Created", "Live Mode", "Pending Webhooks"]; const rows = events.value.map((event) => [ event.id, event.type, new Date(event.created * 1e3).toISOString(), event.livemode, event.pending_webhooks ]); return [headers, ...rows].map((row) => row.join(",")).join("\n"); }; const filterEvents = (filter) => { return events.value.filter((event) => { if (filter.type && event.type !== filter.type) return false; if (filter.dateFrom && event.created < filter.dateFrom.getTime() / 1e3) return false; if (filter.dateTo && event.created > filter.dateTo.getTime() / 1e3) return false; return true; }); }; const generateTestData = (eventType) => { const baseData = { id: `test_${Date.now()}`, object: eventType.split(".")[0], created: Math.floor(Date.now() / 1e3), livemode: false }; switch (eventType) { case "customer.subscription.created": return { ...baseData, object: "subscription", status: "active", customer: "cus_test123" }; case "invoice.payment_succeeded": return { ...baseData, object: "invoice", status: "paid", amount_paid: 1e3, currency: "usd" }; default: return baseData; } }; onUnmounted(() => { stopMonitoring(); }); return { // State loading, error, events, stats, isMonitoring, // Webhook endpoint management createWebhookEndpoint, updateWebhookEndpoint, deleteWebhookEndpoint, listWebhookEndpoints, // Event monitoring startMonitoring, stopMonitoring, fetchEvents, fetchEvent, resendEvent, // Event handlers registerEventHandler, unregisterEventHandler, clearEventHandlers, // Testing & debugging simulateEvent, testWebhookEndpoint, validateWebhookSignature, // Utility functions clearEvents, exportEvents, filterEvents }; } export { useWebhooks as u }; //# sourceMappingURL=useWebhooks-CEsHK4en.mjs.map