UNPKG

eu-tin-validation

Version:

Javascript function to check TIN and VAT validation using European SOAP Service inspired by validate-vat @viruschidai

216 lines (179 loc) 7.67 kB
import http from 'http'; import https from 'https'; import url from 'url'; const proxyUrl = "https://cors-anywhere.herokuapp.com/"; var baseConfig = { serviceUrl: { tin: proxyUrl + "https://ec.europa.eu/taxation_customs/tin/services/checkTinService", vat: proxyUrl + "http://ec.europa.eu/taxation_customs/vies/services/checkVatService" }, soapAction: { tin: 'urn:ec.europa.eu:taxud:tin:services:checkTin/checkTin', vat: 'urn:ec.europa.eu:taxud:vies:services:checkVat/checkVat' }, soapBodyTemplate: { tin: `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns1="urn:ec.europa.eu:taxud:tin:services:checkTin:types" xmlns:impl="urn:ec.europa.eu:taxud:tin:services:checkTin"> <soap:Header> </soap:Header> <soap:Body> <tns1:checkTin xmlns:tns1="urn:ec.europa.eu:taxud:tin:services:checkTin:types" xmlns="urn:ec.europa.eu:taxud:tin:services:checkTin:types"> <tns1:countryCode>_country_code_placeholder_</tns1:countryCode> <tns1:tinNumber>_doc_number_placeholder_</tns1:tinNumber> </tns1:checkTin> </soap:Body> </soap:Envelope> `, vat: `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns1="urn:ec.europa.eu:taxud:vies:services:checkVat:types" xmlns:impl="urn:ec.europa.eu:taxud:vies:services:checkVat"> <soap:Header> </soap:Header> <soap:Body> <tns1:checkVat xmlns:tns1="urn:ec.europa.eu:taxud:vies:services:checkVat:types" xmlns="urn:ec.europa.eu:taxud:vies:services:checkVat:types"> <tns1:countryCode>_country_code_placeholder_</tns1:countryCode> <tns1:vatNumber>_doc_number_placeholder_</tns1:vatNumber> </tns1:checkVat> </soap:Body> </soap:Envelope> ` } }; var countries = ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'EL', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'GB']; const messages = { 'INVALID_ENDPOINT': 'Endpoint must be "tin" or "vat"', 'INVALID_INPUT': 'The provided CountryCode is invalid or the VAT number is empty', 'SERVICE_UNAVAILABLE': 'The VIES VAT service is unavailable, please try again later', 'MS_UNAVAILABLE': 'The VAT database of the requested member country is unavailable, please try again later', 'MS_MAX_CONCURRENT_REQ': 'The VAT database of the requested member country has had too many requests, please try again later', 'TIMEOUT': 'The request to VAT database of the requested member country has timed out, please try again later', 'SERVER_BUSY': 'The service cannot process your request, please try again later', 'UNKNOWN': 'Unknown error' }; function getErrorMessage (faultstring) { return messages[faultstring] || messages["UNKNOWN"]; } function parseSoapResponse (soapMessage, endpoint) { function parseField(field) { const regex = new RegExp(`<${field}>\((\.|\\s)\*?\)</${field}>`, 'gm'); const match = regex.exec(soapMessage); if (!match) { let err = new Error(`Failed to parseField ${field}`); err.soapMessage = soapMessage; throw err; } return match[1]; } const hasFault = soapMessage.match(/<soap:Fault>\S+<\/soap:Fault>/g); let ret; if (hasFault) { ret = { faultCode: parseField('faultcode'), faultString: parseField('faultstring') }; } else { if (endpoint === "tin") { ret = { countryCode: parseField('countryCode'), vatNumber: parseField('tinNumber'), requestDate: parseField('requestDate'), valid: parseField('validSyntax') === 'true' && parseField('validStructure') === 'true', validSyntax: parseField('validSyntax') === 'true', validStructure: parseField('validStructure') === 'true' }; } else if (endpoint === "vat") { ret = { countryCode: parseField('countryCode'), vatNumber: parseField('vatNumber'), requestDate: parseField('requestDate'), valid: parseField('valid') === 'true', name: parseField('name'), address: parseField('address').replace(/\n/g, ', ') }; } } return ret; } var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var index = (async function (endpoint, countryCode, docNumber, timeout, callback) { if (typeof timeout === 'function') { callback = timeout; timeout = null; } if (!countries.includes(countryCode) || docNumber.length <= 0) { return process.nextTick(() => { callback(getErrorMessage("INVALID_INPUT")); }); } if (endpoint !== "tin" && endpoint !== "vat") { return process.nextTick(() => { callback(getErrorMessage("INVALID_ENDPOINT")); }); } let config = _extends({}, baseConfig, { serviceUrl: baseConfig.serviceUrl[endpoint], soapAction: baseConfig.soapAction[endpoint], soapBodyTemplate: baseConfig.soapBodyTemplate[endpoint] }); const parsedUrl = await url.parse(config.serviceUrl); let headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'node-soap', 'Accept': 'text/html,application/xhtml+xml,application/xml,text/xml;q=0.9,*/*;q=0.8', 'Accept-Encoding': 'none', 'Accept-Charset': 'utf-8', 'Access-Control-Allow-Origin': '*', 'Connection': 'close', 'Host': parsedUrl.hostname, 'SOAPAction': config.soapAction }; const xml = config.soapBodyTemplate.replace('_country_code_placeholder_', countryCode).replace('_doc_number_placeholder_', docNumber).replace('\n', '').trim(); headers['content-length'] = Buffer.byteLength(xml, 'utf8'); const options = { host: parsedUrl.host, method: "POST", path: parsedUrl.path, headers: headers, family: 4 }; const requester = endpoint === "tin" ? https : http; const req = await requester.request(options, res => { res.setEncoding('utf8'); let str = ''; res.on('data', chunk => { str += chunk; }); res.on('end', () => { let data, err; try { data = parseSoapResponse(str, endpoint); } catch (error) { err = error; return callback(err); } if (data && data.faultString != null && data.faultString.length > 0) { err = getErrorMessage(data.faultString); err.code = data.faultString; return callback(err); } return callback(null, data); }); }); if (timeout) { req.setTimeout(timeout, () => { return req.abort(); }); } req.on('error', e => { return callback(e); }); req.write(xml); return req.end(); }); export default index; //# sourceMappingURL=index.esm.js.map