@zed-io/wam-payment-sdk
Version:
Official WAM Payment SDK for creating and signing payment links
217 lines (209 loc) • 7.5 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var crypto = require('crypto');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto);
class WamPaymentError extends Error {
constructor(message, code, validationErrors) {
super(message);
this.code = code;
this.validationErrors = validationErrors;
this.name = 'WamPaymentError';
}
}
exports.WamPaymentErrorCode = void 0;
(function (WamPaymentErrorCode) {
WamPaymentErrorCode["INVALID_CONFIG"] = "INVALID_CONFIG";
WamPaymentErrorCode["INVALID_AMOUNT"] = "INVALID_AMOUNT";
WamPaymentErrorCode["INVALID_CURRENCY"] = "INVALID_CURRENCY";
WamPaymentErrorCode["INVALID_REFERENCE"] = "INVALID_REFERENCE";
WamPaymentErrorCode["INVALID_RETURN_URL"] = "INVALID_RETURN_URL";
WamPaymentErrorCode["SIGNING_ERROR"] = "SIGNING_ERROR";
WamPaymentErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR";
})(exports.WamPaymentErrorCode || (exports.WamPaymentErrorCode = {}));
class WamPaymentSDK {
constructor(config) {
this.validateConfig(config);
this.config = config;
this.baseUrl = this.getBaseUrl();
}
/**
* Generates a signed payment link for WAM payment processing
* @param params Payment parameters including amount, currency, reference, and optional return URL
* @returns Payment link response with URL, signature, and metadata
*/
generatePaymentLink(params) {
// Validate input parameters
this.validatePaymentParams(params);
// Create the payload to sign (must match the format expected by WAM)
const payload = `${params.amount}|TTD|${params.reference}`;
// Generate HMAC-SHA256 signature
const signature = this.generateSignature(payload);
// Build the payment URL
const paymentUrl = this.buildPaymentUrl(params, signature);
return {
url: paymentUrl,
signature,
payload,
};
}
/**
* Validates payment parameters
* @param params Payment parameters to validate
* @throws WamPaymentError if validation fails
*/
validatePaymentParams(params) {
const errors = [];
// Validate amount
if (typeof params.amount !== "number" || params.amount <= 0) {
errors.push({
field: "amount",
message: "Amount must be a positive number",
});
}
// Validate reference
if (!params.reference ||
typeof params.reference !== "string" ||
params.reference.trim().length === 0) {
errors.push({
field: "reference",
message: "Reference must be a non-empty string",
});
}
// Validate return URL if provided
if (params.returnUrl && typeof params.returnUrl !== "string") {
errors.push({
field: "returnUrl",
message: "Return URL must be a string",
});
}
if (params.returnUrl && !this.isValidUrl(params.returnUrl)) {
errors.push({
field: "returnUrl",
message: "Return URL must be a valid URL",
});
}
if (errors.length > 0) {
throw new WamPaymentError("Validation failed", exports.WamPaymentErrorCode.VALIDATION_ERROR, errors);
}
}
/**
* Validates the SDK configuration
* @param config Configuration to validate
* @throws WamPaymentError if configuration is invalid
*/
validateConfig(config) {
const errors = [];
if (!config.businessId || typeof config.businessId !== "string") {
errors.push({
field: "businessId",
message: "Business ID is required and must be a string",
});
}
if (!config.privateKey || typeof config.privateKey !== "string") {
errors.push({
field: "privateKey",
message: "Private key is required and must be a string",
});
}
if (errors.length > 0) {
throw new WamPaymentError("Invalid configuration", exports.WamPaymentErrorCode.INVALID_CONFIG, errors);
}
}
/**
* Generates HMAC-SHA256 signature
* @param payload The payload to sign
* @returns Hex-encoded signature
*/
generateSignature(payload) {
try {
return crypto__namespace
.createHmac("sha256", this.config.privateKey)
.update(payload)
.digest("hex");
}
catch (error) {
throw new WamPaymentError("Failed to generate signature", exports.WamPaymentErrorCode.SIGNING_ERROR);
}
}
/**
* Gets the base URL based on environment configuration
* @returns Base URL for WAM payment gateway
*/
getBaseUrl() {
// Determine environment from config, default to production
const environment = this.config.environment || 'production';
// Return appropriate URL based on environment
switch (environment) {
case 'staging':
case 'development':
return "https://staging.billing.wam.money";
case 'production':
default:
return "https://billing.wam.money";
}
}
/**
* Builds the complete payment URL with all parameters
* @param params Payment parameters
* @param signature Generated signature
* @returns Complete payment URL
*/
buildPaymentUrl(params, signature) {
const paymentUrl = new URL(`${this.baseUrl}/pay/external/generic/${this.config.businessId}`);
// Add required parameters
paymentUrl.searchParams.set("amount", params.amount.toString());
paymentUrl.searchParams.set("currency", "TTD");
paymentUrl.searchParams.set("reference", params.reference);
paymentUrl.searchParams.set("signature", signature);
// Add optional return URL
if (params.returnUrl) {
paymentUrl.searchParams.set("returnUrl", params.returnUrl);
}
return paymentUrl.toString();
}
/**
* Validates if a string is a valid URL
* @param url String to validate
* @returns true if valid URL, false otherwise
*/
isValidUrl(url) {
try {
new URL(url);
return true;
}
catch {
return false;
}
}
/**
* Gets the current configuration (without sensitive data)
* @returns Configuration object without private key
*/
getConfig() {
return {
businessId: this.config.businessId,
environment: this.config.environment || 'production',
baseUrl: this.baseUrl,
};
}
}
exports.WamPaymentError = WamPaymentError;
exports.WamPaymentSDK = WamPaymentSDK;
exports.default = WamPaymentSDK;
//# sourceMappingURL=index.js.map