bridge-node-server/node_server/utils/diligence/tracesmart-idu-aml.js
Martin Donnelly 57bd6c8e6a init
2018-06-24 21:15:03 +01:00

224 lines
7.0 KiB
JavaScript

/**
* Uses the LexisNexis tracesmart IDU-AML SOAP api for verification
*/
'use strict';
var Q = require('q');
var _ = require('lodash');
var soap = require('soap');
var debug = require('debug')('utils:diligence:tracesmart');
var config = require(global.configFile);
var errors = require(global.pathPrefix + '../utils/diligence/diligence_errors.js');
var adminNotifier = require(global.pathPrefix + '../utils/adminNotifier.js');
const Request = require('./tracesmart-idu-aml/request.js');
module.exports = {
verifyIdentity: verifyIdentity
};
/**
* Verifies the identity of the person using the provided information
*
* @param {Object} client - The client object for this person
* @param {Object} address - The address object for this _person_ (not credit card!)
*
* @return {Promise} - A promise for the completion of the verification
*/
function verifyIdentity(client, address) {
debug('Verify identity');
const soapClientP = Q.nfcall(soap.createClient, config.tracesmartIduAmlUrl);
let request = new Request(client);
request.Person.applyClient(client);
request.Person.applyResidentialAddress(address);
let responseP = soapClientP.then((soapClient) => {
debug('SOAP Client Found');
//
// The soap client splits out the members of the object into individual
// calls to the soap object (which we don't want). So we wrap our request
// in a wrapper so it passes on our mega-object.
//
const wrappedRequest = {
params: request.getRequest() //getTestParams() //request
};
debug('Send request to tracesmart:');
// Make the call
return Q.ninvoke(
soapClient,
'IDUProcess',
request.getRequest()
).then((result) => {
debug('RESPONSE OK:');
return result[0];
}).catch((err) => {
debug('ERROR: ', err);
return err;
});
});
var convertP = responseP.then((response) => tidyResponse(response));
//
// Look at the results and decide if they are a pass or fail.
// Very basic criteria for now.
//
var resultP = convertP.then((converted) => {
if (
_.isObject(converted) &&
_.isObject(converted.Results) &&
_.isObject(converted.Results.Summary) &&
converted.Results.Summary.ResultText !== 'FAIL'
) {
let response = {
Smartscore: converted.Results.Summary.Smartscore,
ID: converted.Results.Summary.ID,
IKey: converted.Results.Summary.IKey,
ProfileURL: converted.Results.Summary.ProfileURL,
Warnings: []
};
if (converted.Results.Summary.ResultText === 'REFER') {
response.Warnings.push(errors.WARNINGS.REFER);
}
if (_.isArray(converted.Results.Sanction) && converted.Results.Sanction.length) {
response.Warnings.push(errors.WARNINGS.PEPS);
/**
* The items in the `Sanction` field may be PEPs or Sanctions.
* If they are sanctions this is a higher level issue
*/
for (let i = 0; i < converted.Results.Sanction.length; ++i) {
if (converted.Results.Sanction[i].Type === 'SANCTION') {
response.Warnings.push(errors.WARNINGS.SANCTIONS);
break; // Only need to find one to add the status
}
}
}
debug('Result: ', JSON.stringify(converted));
/**
* Potentially notify if credits are running out
*/
adminNotifier.notifyCredits('tracesmart', converted.Results.Summary.Credits);
return Q.resolve(response);
} else {
//
// Treat everything else as a fail for now
//
return Q.reject({
name: errors.ERRORS.VERIFICATION_FAILED
});
}
});
return resultP;
}
/**
* This function tidies up a SOAP response to be a simpler JS object without all
* the SOAP related attributes.
*
* @example
*
* SOAP response of:
*
* {
* "Status": {
* "attributes": {
* "xsi:type": "xsd:boolean"
* },
* "$value": "true"
* },
* "ID": {
* "attributes": {
* "xsi:type": "xsd:string"
* },
* "$value": "1234567890"
* },
* "Smartscore": {
* "attributes": {
* "xsi:type": "xsd:int"
* },
* "$value": "55"
* }
* }
*
* is transformed to:
*
* {
* "Status": true,
* "ID": "1234567890",
* "Smartscore": 55
* }
*
* Note that types are applied as appropriate for the related SOAP type attribute.
*
* @param {Object} response - The soap response from the tracesmart interface
* @returns {any} - The simplified response
*/
function tidyResponse(response) {
let tidied = null;
let type = null;
if (_.isObject(response.attributes)) {
type = response.attributes['xsi:type'];
}
//
// If this is a basic type then we just return the $value (which may not exist)
//
if (_.startsWith(type, 'xsd:') && _.isUndefined(response.$value)) {
//
// Don't coerce basic types with an undefined $value.
// Note: all Basic types start with 'xsd:'
//
tidied = undefined;
} else if (type === 'xsd:string') {
tidied = '' + response.$value; // Coerce to string
} else if (type === 'xsd:boolean') {
tidied = !!(response.$value); // Coerce to boolean
} else if (type === 'xsd:int') {
tidied = +(response.$value); // Coerce to number
} else if (type === 'SOAP-ENC:Array') {
//
// Arrays are a bit special in that they only have one other key, and
// if the array is length 1, the related value won't actually be an array!
//
tidied = []; // Default to empty array
_.forOwn(response, function(value, key) {
if (key === 'attributes') {
return; // Do nothing with attributes
} else if (_.isArray(value)) {
// This is an actual array of values so iterate it and push
// them into our response
_.forEach(value, (arrayItem) => {
tidied.push(tidyResponse(arrayItem));
});
} else if (_.isObject(value)) {
// Length 1 arrays are not in an array, so just push this item in
tidied.push(tidyResponse(value));
}
});
} else {
//
// We assume it is an object and iterate through and tidy them
//
tidied = {}; // Default to empty object
_.forOwn(response, function(value, key) {
if (key === 'attributes') {
return; // Do nothing with attributes
} else {
tidied[key] = tidyResponse(value);
}
});
}
return tidied;
}