@sahabaplus/moyasar
Version:
A comprehensive TypeScript SDK for integrating with the Moyasar payment gateway
273 lines • 9.25 kB
JavaScript
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