myinvois-sdk
Version:
TypeScript SDK for interacting with the Malaysia e-invoicing system (MyInvois) API
193 lines (192 loc) • 7.78 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.InvoiceService = void 0;
const document_signer_1 = require("../utils/document-signer");
const invoice_1 = require("../models/invoice");
const crypto = __importStar(require("crypto"));
/**
* Service for invoice operations
*/
class InvoiceService {
/**
* Creates a new invoice service
* @param httpClient The HTTP client to use
* @param authService The authentication service to use
* @param certificateHandler The certificate handler to use
* @param config The MyInvois configuration
*/
constructor(httpClient, authService, certificateHandler, config) {
this.httpClient = httpClient;
this.authService = authService;
this.certificateHandler = certificateHandler;
this.documentSigner = new document_signer_1.DocumentSigner(certificateHandler);
this.config = config;
}
/**
* Sign an invoice
* @param invoice The invoice to sign
* @returns A promise resolving to the signed invoice
*/
async signInvoice(invoice) {
return this.documentSigner.signInvoice(invoice);
}
/**
* Submit a signed invoice
* @param signedInvoice The signed invoice to submit
* @param authTIN The TIN to use for authentication
* @returns A promise resolving to the submission result
*/
async submitInvoice(signedInvoice, authTIN) {
console.log('signedInvoice', JSON.stringify(signedInvoice, null, 2));
let documentB64 = Buffer.from(JSON.stringify(signedInvoice)).toString('base64');
let documentToSubmit = {
format: 'JSON',
document: documentB64,
documentHash: this.calculateSHA256Hex(JSON.stringify(signedInvoice)),
codeNumber: signedInvoice.invoice.id,
//signature: signature
};
const token = await this.authService.getToken(authTIN);
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
};
const url = `${this.config.transactionUrl}/api/v1.0/documentsubmissions/`;
const payload = {
documents: [documentToSubmit]
};
console.log('payload', JSON.stringify(payload, null, 2));
// get the payload size in KB ::
//console.log('payload', JSON.stringify(signedDocuments[0]));
console.log('payload size', JSON.stringify(payload).length / 1024);
try {
console.log('Submitting invoice to MyInvois:', payload);
const response = await this.httpClient.post(url, payload, { headers });
return response;
}
catch (error) {
console.error('Failed to submit invoice to MyInvois:', error);
console.log('error', JSON.stringify(error, null, 2));
throw new Error('Invoice submission failed');
}
}
/**
* Submit multiple signed invoices
* @param signedInvoices The signed invoices to submit
* @param authTIN The TIN to use for authentication
* @returns A promise resolving to the submission result
*/
async submitInvoices(signedInvoices, authTIN) {
const token = await this.authService.getToken(authTIN);
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
};
const url = `${this.config.transactionUrl}/api/v1.0/documentsubmissions/`;
const payload = {
documents: signedInvoices.map(invoice => invoice.toJSON())
};
try {
const response = await this.httpClient.post(url, payload, { headers });
return response;
}
catch (error) {
console.error('Failed to submit invoices to MyInvois:', error);
throw new Error('Invoice submission failed');
}
}
/**
* Create a builder for an invoice
* @returns An invoice instance for building
*/
createInvoice() {
return new invoice_1.Invoice();
}
calculateSHA256Hex(input) {
try {
// Create a hash object for SHA-256
const hash = crypto.createHash('sha256');
// Update the hash object with the input string
hash.update(input, 'utf8');
// Calculate the digest and return it as a hexadecimal string
return hash.digest('hex');
}
catch (error) {
throw new Error(`Failed to calculate SHA256 hash: ${error}`);
}
}
/**
* Cancel an invoice
* @param documentUuid The UUID of the document to cancel
* @param reason The reason for cancellation
* @param authTIN The TIN to use for authentication
* @returns A promise resolving to the cancellation result
*/
async cancelInvoice(documentUuid, reason, authTIN) {
const token = await this.authService.getToken(authTIN);
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
};
const url = `${this.config.transactionUrl}/api/v1.0/documents/state/${documentUuid}/state`;
const payload = {
status: 'cancelled',
reason: reason.substring(0, 300) // Limit to 300 chars as per docs
};
try {
const response = await this.httpClient.put(url, payload, { headers });
return response;
}
catch (error) {
if (error.statusCode === 400) {
const errorCode = error.responseData?.error?.code;
switch (errorCode) {
case 'OperationPeriodOver':
throw new Error('Cancellation period has expired (72 hours)');
case 'IncorrectState':
throw new Error('Invoice cannot be cancelled in its current state');
case 'ActiveReferencingDocuments':
throw new Error('Invoice has active referencing documents');
default:
throw new Error(error.responseData?.error?.message || 'Error cancelling invoice');
}
}
console.error('Failed to cancel invoice:', error);
throw new Error('Invoice cancellation failed');
}
}
}
exports.InvoiceService = InvoiceService;