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
JavaScript
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