@sahabaplus/moyasar
Version:
A comprehensive TypeScript SDK for integrating with the Moyasar payment gateway
190 lines • 6.17 kB
JavaScript
import { InvoiceStatus } from "./enums";
import { CreateInvoiceSchema, BulkCreateInvoiceSchema, invoiceSchema, } from "./validation/schemas";
import { PaymentStatus } from "../payment/index";
export class InvoiceUtils {
/**
* Validate single invoice creation request using Zod
*/
static validateCreateInvoiceRequest(request) {
const result = CreateInvoiceSchema.safeParse(request);
if (result.success) {
return {
success: true,
data: result.data,
errors: [],
};
}
const errors = result.error.issues.map(err => {
const path = err.path.length > 0 ? `${err.path.join(".")}: ` : "";
return `${path}${err.message}`;
});
return {
success: false,
errors,
};
}
/**
* Validate bulk invoice creation request using Zod
*/
static validateBulkCreateRequest(request) {
const result = BulkCreateInvoiceSchema.safeParse(request);
if (result.success) {
return {
success: true,
data: result.data,
errors: [],
};
}
const errors = result.error.issues.map(err => {
let message = err.message;
// Format array index errors more clearly
if (err.path.length >= 2 &&
err.path[0] === "invoices" &&
typeof err.path[1] === "number") {
const invoiceIndex = err.path[1];
const fieldPath = err.path.slice(2).join(".");
const fieldPrefix = fieldPath ? `${fieldPath}: ` : "";
message = `Invoice ${invoiceIndex + 1}: ${fieldPrefix}${err.message}`;
}
else if (err.path.length > 0) {
const path = err.path.join(".");
message = `${path}: ${err.message}`;
}
return message;
});
return {
success: false,
errors,
};
}
/**
* Convert amount to display format
*/
static formatAmount(amount, currency) {
const divisors = {
KWD: 1000,
JPY: 1,
SAR: 100,
USD: 100,
EUR: 100,
};
const divisor = divisors[currency] ?? 100;
const formattedAmount = (amount / divisor).toFixed(divisor === 1 ? 0 : 2);
return `${formattedAmount} ${currency}`;
}
/**
* Parse amount from display format to smallest unit
*/
static parseAmount(formattedAmount, currency) {
const divisors = {
KWD: 1000,
JPY: 1,
SAR: 100,
USD: 100,
EUR: 100,
};
const amount = parseFloat(formattedAmount.replace(/[^\d.]/g, ""));
const divisor = divisors[currency.toUpperCase()] || 100;
return Math.round(amount * divisor);
}
/**
* Check if invoice is in a final state
*/
static isInvoiceFinal(status) {
const finalStatuses = [
InvoiceStatus.PAID,
InvoiceStatus.CANCELED,
InvoiceStatus.EXPIRED,
InvoiceStatus.VOIDED,
];
return finalStatuses.includes(status);
}
/**
* Check if invoice can be canceled
*/
static canCancelInvoice(invoice) {
const cancelableStatuses = [
InvoiceStatus.INITIATED,
InvoiceStatus.FAILED,
];
return cancelableStatuses.includes(invoice.status);
}
/**
* Check if invoice is expired
*/
static isInvoiceExpired(invoice) {
if (!invoice.expired_at) {
return false;
}
return new Date(invoice.expired_at) <= new Date();
}
/**
* Get time until expiry
*/
static getTimeUntilExpiry(invoice) {
if (!invoice.expired_at) {
return null;
}
const expiryTime = new Date(invoice.expired_at).getTime();
const currentTime = new Date().getTime();
return Math.max(0, expiryTime - currentTime);
}
/**
* Get payment summary for an invoice
*/
static getPaymentSummary(invoice) {
const payments = invoice.payments;
return {
total: payments.length,
paid: payments.filter(p => p.status === PaymentStatus.PAID).length,
failed: payments.filter(p => p.status === PaymentStatus.FAILED).length,
pending: payments.filter(p => p.status === PaymentStatus.INITIATED)
.length,
totalAmount: payments.reduce((sum, p) => sum + p.amount, 0),
paidAmount: payments
.filter(p => p.status === PaymentStatus.PAID)
.reduce((sum, p) => sum + p.amount, 0),
refundedAmount: payments.reduce((sum, p) => sum + p.refunded, 0),
};
}
/**
* Build metadata query parameters for filtering
*/
static buildMetadataQuery(metadata) {
const query = {};
Object.entries(metadata).forEach(([key, value]) => {
query[`metadata[${key}]`] = value;
});
return query;
}
/**
* Sanitize invoice description
*/
static sanitizeDescription(description) {
return description.trim().substring(0, 255);
}
/**
* Generate invoice reference number
*/
static generateReference(prefix = "INV") {
const timestamp = Date.now().toString(36);
const random = Math.random().toString(36).substring(2, 8);
return `${prefix}-${timestamp}-${random}`.toUpperCase();
}
/**
* Parse and validate a CreateInvoiceRequest, returning sanitized data
*/
static parseCreateInvoiceRequest(request) {
return this.validateCreateInvoiceRequest(request);
}
/**
* Parse and validate a BulkCreateInvoiceRequest, returning sanitized data
*/
static parseBulkCreateRequest(request) {
return this.validateBulkCreateRequest(request);
}
static parseInvoice(invoice) {
return invoiceSchema.parse(invoice);
}
}
//# sourceMappingURL=utils.js.map