shora-ai-payment-sdk
Version:
The first open-source payment SDK designed specifically for AI agents and chatbots - ACP Compatible
190 lines (189 loc) • 8.15 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecurityEnhancement = void 0;
exports.createSecurityEnhancement = createSecurityEnhancement;
exports.generateEncryptionKey = generateEncryptionKey;
const crypto_js_1 = __importDefault(require("crypto-js"));
const axios_1 = __importDefault(require("axios"));
const pkg = require('../package.json');
class SecurityEnhancement {
constructor(config) {
this.auditLogs = [];
this.requestContext = null;
this.config = { pbkdf2Iterations: 100000, ...config };
}
setRequestContext(ctx) {
this.requestContext = ctx;
}
getClientIP() {
return this.requestContext?.ip || '127.0.0.1';
}
getUserAgent() {
const sdkVersion = this.config.sdkVersion || pkg.version || 'unknown';
return this.requestContext?.userAgent || `ShoraAI-PaymentSDK/${sdkVersion}`;
}
encryptToken(token, additionalData) {
const salt = crypto_js_1.default.lib.WordArray.random(256 / 8);
const iv = crypto_js_1.default.lib.WordArray.random(128 / 8);
const iterations = this.config.pbkdf2Iterations || 100000;
const key = crypto_js_1.default.PBKDF2(this.config.encryptionKey, salt, {
keySize: 256 / 32,
iterations,
hasher: crypto_js_1.default.algo.SHA256,
});
const payload = {
token,
tenantId: this.config.tenantId,
ts: Date.now(),
};
if (additionalData !== undefined) {
payload.additionalData = additionalData || null;
}
const encrypted = crypto_js_1.default.AES.encrypt(JSON.stringify(payload), key, {
iv,
mode: crypto_js_1.default.mode.CBC,
padding: crypto_js_1.default.pad.Pkcs7,
});
return {
encrypted: encrypted.toString(),
iv: iv.toString(crypto_js_1.default.enc.Hex),
salt: salt.toString(crypto_js_1.default.enc.Hex),
timestamp: Date.now(),
};
}
decryptToken(encryptedToken) {
try {
const salt = crypto_js_1.default.enc.Hex.parse(encryptedToken.salt);
const iv = crypto_js_1.default.enc.Hex.parse(encryptedToken.iv);
const iterations = this.config.pbkdf2Iterations || 100000;
const key = crypto_js_1.default.PBKDF2(this.config.encryptionKey, salt, {
keySize: 256 / 32,
iterations,
hasher: crypto_js_1.default.algo.SHA256,
});
const decrypted = crypto_js_1.default.AES.decrypt(encryptedToken.encrypted, key, {
iv,
mode: crypto_js_1.default.mode.CBC,
padding: crypto_js_1.default.pad.Pkcs7,
});
const decryptedString = decrypted.toString(crypto_js_1.default.enc.Utf8);
if (!decryptedString) {
this.logAudit('decrypt_failed', 'Token decryption failed - invalid data');
return null;
}
const payload = JSON.parse(decryptedString);
if (!payload || payload.tenantId !== this.config.tenantId) {
this.logAudit('decrypt_failed', 'Token decryption failed - tenant mismatch');
return null;
}
this.logAudit('decrypt_success', 'Token decrypted successfully');
return payload.token;
}
catch (error) {
this.logAudit('decrypt_error', 'Token decryption error: ' + String(error));
return null;
}
}
logAudit(action, details, transactionId, userId, agentId, amount, currency, status = 'success', metadata) {
if (!this.config.enableAuditLogging)
return;
const auditEntry = {
timestamp: new Date().toISOString(),
tenantId: this.config.tenantId,
transactionId,
action,
userId,
agentId,
amount,
currency,
status,
metadata: {
...metadata,
details,
sdkVersion: this.config.sdkVersion || pkg.version || 'unknown',
pbkdf2Iterations: this.config.pbkdf2Iterations || 100000,
environment: process.env.NODE_ENV || 'production',
},
ipAddress: this.getClientIP(),
userAgent: this.getUserAgent(),
};
this.auditLogs.push(auditEntry);
if (this.config.auditLogEndpoint) {
this.sendAuditLog(auditEntry).catch((error) => {
console.warn('sendAuditLog failed', error);
});
}
}
getAuditLogs(startDate, endDate, action) {
let filtered = this.auditLogs.filter((log) => log.tenantId === this.config.tenantId);
if (startDate) {
const start = new Date(startDate).getTime();
filtered = filtered.filter((log) => new Date(log.timestamp).getTime() >= start);
}
if (endDate) {
const end = new Date(endDate).getTime();
filtered = filtered.filter((log) => new Date(log.timestamp).getTime() <= end);
}
if (action) {
filtered = filtered.filter((log) => log.action === action);
}
return filtered.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
}
async sendAuditLog(auditEntry) {
if (!this.config.auditLogEndpoint)
return;
try {
await axios_1.default.post(this.config.auditLogEndpoint, auditEntry, {
headers: { 'Content-Type': 'application/json' },
});
}
catch (error) {
console.warn('Failed to POST audit log:', error);
}
}
generateSecurePaymentToken(paymentData) {
const tokenData = {
...paymentData,
tenantId: this.config.tenantId,
nonce: crypto_js_1.default.lib.WordArray.random(128 / 8).toString(),
expires: Date.now() + 30 * 60 * 1000,
};
const encrypted = this.encryptToken(JSON.stringify(tokenData));
this.logAudit('payment_token_generated', `Secure payment token generated for ${paymentData.amount} ${paymentData.currency}`, undefined, paymentData.userId, paymentData.agentId, paymentData.amount, paymentData.currency, 'success', { tokenExpires: new Date(Date.now() + 30 * 60 * 1000).toISOString() });
return encrypted;
}
validatePaymentToken(encryptedToken) {
const decrypted = this.decryptToken(encryptedToken);
if (!decrypted) {
this.logAudit('payment_token_validation_failed', 'Token decryption failed');
return { valid: false, error: 'Invalid token' };
}
try {
const data = JSON.parse(decrypted);
if (data.expires && Date.now() > data.expires) {
this.logAudit('payment_token_validation_failed', 'Token expired');
return { valid: false, error: 'Token expired' };
}
if (data.tenantId !== this.config.tenantId) {
this.logAudit('payment_token_validation_failed', 'Tenant mismatch');
return { valid: false, error: 'Tenant mismatch' };
}
this.logAudit('payment_token_validated', 'Payment token validated successfully', undefined, data.userId, data.agentId, data.amount, data.currency);
return { valid: true, data };
}
catch (error) {
this.logAudit('payment_token_validation_failed', 'Token parsing error: ' + String(error));
return { valid: false, error: 'Invalid token format' };
}
}
}
exports.SecurityEnhancement = SecurityEnhancement;
function createSecurityEnhancement(config) {
return new SecurityEnhancement(config);
}
function generateEncryptionKey() {
return crypto_js_1.default.lib.WordArray.random(256 / 8).toString(crypto_js_1.default.enc.Hex);
}