UNPKG

@sahabaplus/moyasar

Version:

A comprehensive TypeScript SDK for integrating with the Moyasar payment gateway

273 lines 9.25 kB
import { API_ENDPOINTS } from "../../shared/constants/index"; import { WebhookUtils } from "./utils"; import { WebhookError } from "./errors"; import { WebhookValidation } from "./validation"; import { WebhookEvents } from "./enums"; import { TypedEmitter } from "tiny-typed-emitter"; export class WebhookService extends TypedEmitter { constructor(params) { super(); this.apiClient = params.apiClient; } /** * Create a new webhook */ async create(params) { // Validate input this.validateCreateRequest(params); try { const webhook = await this.apiClient.request({ method: "POST", url: API_ENDPOINTS.webhooks, data: params, }); return webhook; } catch (error) { const webhookError = this.handleError(error, "Failed to create webhook"); throw webhookError; } } /** * List all webhooks */ async list(options = {}) { try { return await this.apiClient.request({ method: "GET", url: API_ENDPOINTS.webhooks, params: options, }); } catch (error) { const webhookError = this.handleError(error, "Failed to list webhooks"); throw webhookError; } } /** * Retrieve a specific webhook */ async retrieve(webhookId) { if (!webhookId) { throw new WebhookError("Webhook ID is required"); } try { return await this.apiClient.request({ method: "GET", url: `${API_ENDPOINTS.webhooks}/${webhookId}`, }); } catch (error) { const webhookError = this.handleError(error, `Failed to retrieve webhook ${webhookId}`); throw webhookError; } } /** * Update a webhook */ async update(webhookId, params) { if (!webhookId) { throw new WebhookError("Webhook ID is required"); } // Validate input this.validateUpdateRequest(params); try { const webhook = await this.apiClient.request({ method: "PUT", url: `${API_ENDPOINTS.webhooks}/${webhookId}`, data: params, }); return webhook; } catch (error) { const webhookError = this.handleError(error, `Failed to update webhook ${webhookId}`); throw webhookError; } } /** * Delete a webhook */ async delete(webhookId) { if (!webhookId) { throw new WebhookError("Webhook ID is required"); } try { await this.apiClient.request({ method: "DELETE", url: `${API_ENDPOINTS.webhooks}/${webhookId}`, }); } catch (error) { const webhookError = this.handleError(error, `Failed to delete webhook ${webhookId}`); throw webhookError; } } /** * Get available webhook events */ async availableEvents() { try { return await this.apiClient.request({ method: "GET", url: API_ENDPOINTS.availableEvents, }); } catch (error) { const webhookError = this.handleError(error, "Failed to fetch available events"); throw webhookError; } } /** * Webhook Attempts Sub-service */ get attempts() { return { /** * List all webhook attempts */ list: async (options = {}) => { try { return await this.apiClient.request({ method: "GET", url: API_ENDPOINTS.webhookAttempts, params: options, }); } catch (error) { const webhookError = this.handleError(error, "Failed to list webhook attempts"); throw webhookError; } }, /** * Retrieve a specific webhook attempt */ retrieve: async (attemptId) => { if (!attemptId) { throw new WebhookError("Attempt ID is required"); } try { return await this.apiClient.request({ method: "GET", url: `${API_ENDPOINTS.webhookAttempts}/${attemptId}`, }); } catch (error) { const webhookError = this.handleError(error, `Failed to retrieve webhook attempt ${attemptId}`); throw webhookError; } }, }; } /** * Process incoming webhook payload * This method should be called from your webhook endpoint */ async processWebhook(rawPayload, options) { try { // Parse payload if it's raw const payload = typeof rawPayload === "object" && "id" in rawPayload ? rawPayload : WebhookUtils.parseWebhookPayload(rawPayload); // Validate payload structure const validationErrors = WebhookUtils.validateWebhookPayload(payload); if (validationErrors.length > 0) throw new WebhookError(`Invalid webhook payload: ${validationErrors.join(", ")}`); // Verify signature if provided const isValid = await WebhookUtils.verifyWebhookSignature(payload, options); if (!isValid) { throw new WebhookError("Webhook signature verification failed"); } // Emit the specific event this.emit(payload.type, payload); return payload; } catch (error) { const webhookError = error instanceof WebhookError ? error : this.handleError(error, "Failed to process webhook"); throw webhookError; } } /** * Utility method to create type-safe event listeners for payment events */ onPaymentEvent(event, listener) { return this.on(event, listener); } /** * Utility method to listen to all payment events */ onAnyPaymentEvent(listener) { const events = Object.values(WebhookEvents); events.forEach(event => { this.on(event, listener); }); return this; } /** * Helper method to extract webhook signature from common header patterns */ extractSignature(headers) { return WebhookUtils.extractSignatureFromHeaders(headers); } // Private helper methods validateCreateRequest(params) { const errors = []; // Required fields if (!params.url) { errors.push("url is required"); } else if (!WebhookValidation.isValidUrl(params.url)) { errors.push("url must be a valid HTTP/HTTPS URL"); } if (!params.http_method) { errors.push("http_method is required"); } else if (!WebhookValidation.isValidHttpMethod(params.http_method)) { errors.push("http_method must be a valid HTTP method"); } // Validate events if provided if (params.events) { const invalidEvents = params.events.filter(event => !WebhookValidation.isValidWebhookEvent(event)); if (invalidEvents.length > 0) { errors.push(`Invalid webhook events: ${invalidEvents.join(", ")}`); } } if (errors.length > 0) { throw new WebhookError(`Validation failed: ${errors.join(", ")}`); } } validateUpdateRequest(params) { const errors = []; // Validate URL if provided if (params.url && !WebhookValidation.isValidUrl(params.url)) { errors.push("url must be a valid HTTP/HTTPS URL"); } // Validate HTTP method if provided if (params.http_method && !WebhookValidation.isValidHttpMethod(params.http_method)) { errors.push("http_method must be a valid HTTP method"); } // Validate events if provided if (params.events) { const invalidEvents = params.events.filter(event => !WebhookValidation.isValidWebhookEvent(event)); if (invalidEvents.length > 0) { errors.push(`Invalid webhook events: ${invalidEvents.join(", ")}`); } } if (errors.length > 0) { throw new WebhookError(`Validation failed: ${errors.join(", ")}`); } } handleError(error, message) { if (error instanceof WebhookError) { return error; } const errorMessage = error?.message || error?.toString() || "Unknown error"; return new WebhookError(`${message}: ${errorMessage}`, { statusCode: error?.statusCode || error?.status, message: errorMessage, }); } } //# sourceMappingURL=service.js.map